You are here

availability_calendars.module in Availability Calendars 6

Availability Calendars Module.

Allows for availability information to be displayed using calendars on specified content types. Originally based on the Availability Module. @author Dan Karran (geodaniel) <dan at karran dot net> @author Nicholas Alipaz (nicholas.alipaz)

File

availability_calendars.module
View source
<?php

/**
 * Availability Calendars Module.
 *
 * @file
 * Allows for availability information to be displayed using calendars on specified content types. Originally based on the Availability Module.
 * @author Dan Karran (geodaniel) <dan at karran dot net>
 * @author Nicholas Alipaz (nicholas.alipaz)
 */

/**
 * Implementation of hook_init().
 * Currently just adds our css/js files to pages.
 */
function availability_calendars_init() {

  // css is added to all pages, since blocks will need to make use of it
  // base css
  drupal_add_css(drupal_get_path('module', 'availability_calendars') . '/availability_calendars.css');

  // current theme info
  $theme_data = _system_theme_data();

  // array containing info about all themes
  $theme_key = array_pop(explode('/', path_to_theme()));

  // get the currently enabled theme's namespace, can't find a better way to do this
  $theme = $theme_data[$theme_key];

  // object containing all info about current theme
  $theme_bases = $theme->base_themes;

  // array of current theme's base themes
  // themes we will perform specific theming on
  $core_themes = array(
    'garland',
    'bluemarine',
    'chameleon',
    'marvin',
    'pushbutton',
  );
  $base_themes = array(
    'fusion_core',
    'tao',
    'zen',
    'ninesixty',
  );

  // check to see if we are using a core theme
  if (in_array($theme_key, array_merge($core_themes, $base_themes))) {
    $add_css = TRUE;
  }

  // check whether the theme being used is a subtheme of one of our themed base themes
  foreach ($base_themes as $key) {
    if (is_array($theme_bases)) {
      if (array_key_exists($key, $theme_bases)) {
        $theme_key = $key;

        // if the current theme is a subtheme of one of our base themes then use the base theme's namespace instead of our enabled theme's
        $add_css = TRUE;
        break;
      }
    }
  }
  if ($add_css) {
    drupal_add_css(drupal_get_path('module', 'availability_calendars') . '/css/' . $theme_key . '.css');
  }
}

/**
 * A callback function to see if allowed to edit the calendar.
 *
 * @global object $user
 * @param int $nid
 * @return boolean
 */
function availability_can_edit($nid = NULL) {
  if ($nid == NULL) {
    return user_access('edit availability calendars') || user_access('edit own availability calendars');
  }
  else {
    $node = node_load($nid);
    global $user;
    return user_access('edit availability calendars') || user_access('edit own availability calendars') && $user->uid == $node->uid;
  }
}

/**
 * Implementation of hook_menu().
 *
 * @return array
 */
function availability_calendars_menu() {
  $items = array();
  $items['admin/settings/availability-calendars'] = array(
    'title' => 'Availability Calendars',
    'description' => 'Configure global settings for availability calendars module.',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'availability_calendars_admin_settings',
    ),
    'access arguments' => array(
      'edit availability calendars',
    ),
  );
  $items['availability-calendars/%'] = array(
    'title' => 'Availability calendar',
    'page callback' => 'availability_calendars_page',
    'page arguments' => array(
      1,
    ),
    'access callback' => 'availability_can_edit',
    'access arguments' => array(
      1,
    ),
    'type' => MENU_CALLBACK,
  );
  return $items;
}

/**
 * Implementation of hook_perm().
 *
 * @return array
 */
function availability_calendars_perm() {
  return array(
    'edit own availability calendars',
    'edit availability calendars',
  );
}

/**
 * Settings page callback.
 *
 * @return array
 */
function availability_calendars_admin_settings() {
  $form = array();
  $settings = availability_calendar_getsettings();
  $form['display'] = array(
    '#type' => 'fieldset',
    '#title' => t('View settings'),
    '#description' => t('Check where you want availability calendars to be displayed. If you choose none of the suggested places below you will need to output it manually using %func function.', array(
      '%func' => 'theme_availability_calendars_node()',
    )),
  );
  $form['display']['availability_calendars_settings_system_nodeview'] = array(
    '#type' => 'checkbox',
    '#title' => t('Node view page'),
    '#default_value' => $settings->nodeview,
  );
  $form['display']['availability_calendars_settings_system_monthcount'] = array(
    '#type' => 'textfield',
    '#title' => t('Number of months to display'),
    '#default_value' => $settings->monthcount,
    '#description' => t("Your calendars will show this number of months to all users except those with the 'edit own availability calendars' or 'edit availability calendars' who will always see 3 extra months on the calendars they can edit. This is to allow them to enter information into future calendars before it is made publicly available."),
  );
  $form['display']['availability_calendars_settings_system_splitday'] = array(
    '#type' => 'checkbox',
    '#title' => t('Allow split day statuses.'),
    '#default_value' => $settings->splitday,
  );
  $form['display']['availability_calendars_settings_system_defaultstatus'] = array(
    '#type' => 'select',
    '#options' => availability_calendars_options(),
    '#title' => t("Set the default status in which to set all newly created nodes' statuses."),
    '#default_value' => $settings->defaultstatus,
  );
  $form['global'] = array(
    '#type' => 'fieldset',
    '#title' => t('Global settings'),
    '#description' => t('The following settings toggle their setting globally, leave them unchecked to allow configuration per node.'),
  );
  $form['global']['availability_calendars_settings_system_hideold'] = array(
    '#type' => 'checkbox',
    '#title' => t('Set past dates to fully booked.'),
    '#default_value' => $settings->hideold,
  );
  $form['global']['availability_calendars_settings_system_showteaser'] = array(
    '#type' => 'checkbox',
    '#title' => t('Show availability calendars within teasers.'),
    '#default_value' => $settings->showteaser,
  );

  //TODO: add status codes and css classes in here
  return system_settings_form($form);
}

/**
 * Create tab to show node availability.
 *
 * @return string or FALSE
 */
function availability_calendars_menu_callback() {
  $node = menu_get_object();
  if (availability_calendars_getsettings('nodetype', $node->type)) {
    return theme('availability_calendars_node', $node);
  }
  else {
    return FALSE;
  }
}

/**
 * Availability calendars page.
 *
 * @param int $nid
 * @param int $year
 * @param int $month
 * @param boolean $edit
 * @return string
 */
function availability_calendars_page($nid = NULL, $year = NULL, $month = NULL, $edit = FALSE) {
  $settings = availability_calendar_getsettings();
  if (is_numeric($nid)) {
    $node = node_load($nid);
    drupal_set_title(t('Availability for %name', array(
      '%name' => $node->title,
    )));
    if (is_numeric($year) && is_numeric($month)) {

      // #1093408: Summer/winter time offsets: don't use midnight sharp
      drupal_set_title(t('Availability for !name in !date', array(
        '!name' => $node->title,
        '!date' => format_date(mktime(12, 0, 0, $month, 1, $year), 'custom', 'F Y'),
      )));
      if ($edit == 'edit') {

        // Display the edit form for the availability calendar on the node
        return availability_calendars_node_edit($node, $year, $month);
      }
      else {

        // Display availability calendar for single month
        $monthstodisplay = 1;
        return theme('availability_calendars_node', $node, $year, $month, $monthstodisplay);
      }
    }
    elseif (is_numeric($year)) {

      // Display availability calendar for a whole year
      $month = 1;
      $monthstodisplay = 12;
      return theme('availability_calendars_node', $node, $year, $month, $monthstodisplay);
    }
    else {

      // Display rolling availability calendar from this point onwards
      $year = date('Y');
      $month = date('m');
      $monthstodisplay = $settings->monthcount;
      return theme('availability_calendars_node', $node, $year, $month, $monthstodisplay);
    }
  }
  else {
    print drupal_not_found();
  }
}

/**
 * Callback function to produce the node edit form.
 *
 * @return string
 */
function availability_calendars_node_edit() {
  return drupal_get_form('availability_calendars_node_edit_form');
}

/**
 * Creates the node edit form.
 *
 * @return array
 */
function availability_calendars_node_edit_form() {
  $nid = check_plain(arg(1));
  $settings = availability_calendar_getsettings('node', $nid);
  $year = check_plain(arg(2));
  $month = check_plain(arg(3));
  $form['nid'] = array(
    '#type' => 'hidden',
    '#default_value' => $nid,
  );
  $form['year'] = array(
    '#type' => 'hidden',
    '#default_value' => $year,
  );
  $form['month'] = array(
    '#type' => 'hidden',
    '#default_value' => $month,
  );
  $month_meta = availability_calendars_month_meta($year, $month, $settings);

  // find all entries in database for this month ($availability, $notes) and pre-populate
  $notes_result = db_query('SELECT week, note FROM {availability_calendars_week} WHERE nid = %d AND year = %d and month = %d', $nid, $year, $month);
  while ($note = db_fetch_array($notes_result)) {
    $notes[$note['week']] = $note['note'];
  }
  $status_result = db_query('SELECT day, status FROM {availability_calendars_day} WHERE nid = %d AND year = %d AND month = %d', $nid, $year, $month);
  while ($status = db_fetch_array($status_result)) {
    $day_status[$status['day']] = $status['status'];
  }
  $day = 1;
  for ($week = 1; $week <= $month_meta['weeksinmonth']; $week++) {
    $form['week-' . $week] = array(
      '#type' => 'fieldset',
      '#title' => t('Week @week', array(
        '@week' => $week,
      )),
    );
    $form['week-' . $week]['note-' . $week] = array(
      '#type' => 'textfield',
      '#title' => t('Note'),
      '#default_value' => $notes[$week],
      '#description' => t('This will be displayed beside the week in the calendar. It could include, for example, a weekly price.'),
    );
    if ($week == 1) {
      $daysinweekremaining = 7 - $month_meta['firstday'];
    }
    else {
      $daysinweekremaining = 7;
    }
    while ($daysinweekremaining > 0 && $day <= $month_meta['daysinmonth']) {
      $form['week-' . $week]['day-' . $day] = array(
        '#type' => 'select',
        '#title' => format_date(mktime(12, 0, 0, $month, $day, $year), 'custom', 'l j F'),
        '#options' => availability_calendars_options(),
        '#default_value' => $day_status[$day] === NULL ? $settings->defaultstatus : $day_status[$day],
      );
      $day++;
      $daysinweekremaining--;
    }
  }
  $form['submit'] = array(
    '#type' => 'submit',
    '#value' => t('Submit'),
  );
  return $form;
}

/**
 * Callback function for submitting a node edit form.
 *
 * @param array $form
 * @param array $form_state
 */
function availability_calendars_node_edit_form_submit($form, &$form_state) {
  $nid = check_plain($form_state['values']['nid']);
  $year = check_plain($form_state['values']['year']);
  $month = check_plain($form_state['values']['month']);

  // save weekly notes
  $week = 1;
  $nomoreweeks = FALSE;
  while (!$nomoreweeks) {
    if (isset($form_state['values']['note-' . $week])) {
      $notes[$week] = check_plain($form_state['values']['note-' . $week]);
      $week++;
    }
    else {
      $nomoreweeks = TRUE;
    }
  }

  // save $days
  $day = 1;
  $nomoredays = FALSE;
  while (!$nomoredays) {
    if (isset($form_state['values']['day-' . $day])) {
      $days[$day] = $form_state['values']['day-' . $day];
      $day++;
    }
    else {
      $nomoredays = TRUE;
    }
  }
  db_query('DELETE FROM {availability_calendars_week} WHERE nid = %d AND year = %d AND month = %d', $nid, $year, $month);
  foreach ($notes as $week => $note) {
    db_query('INSERT INTO {availability_calendars_week} (nid, year, month, week, note) VALUES (%d, %d, %d, %d, "%s")', $nid, $year, $month, $week, $note);
  }
  db_query('DELETE FROM {availability_calendars_day} WHERE nid = %d AND year = %d AND month = %d', $nid, $year, $month);
  foreach ($days as $day => $status) {
    db_query("INSERT INTO {availability_calendars_day} (nid, year, month, day, status, date) VALUES (%d, %d, %d, %d, '%s', '%s')", $nid, $year, $month, $day, $status, "{$year}-{$month}-{$day} 12:00:00");
  }
  cache_clear_all();

  // clear cache so that updates to this node show
  drupal_set_message(t('Availability information saved.'));
}

/**
 * Implementation of hook_theme().
 *
 * @return array
 */
function availability_calendars_theme() {
  return array(
    'availability_calendars_node' => array(
      'arguments' => array(
        'node' => NULL,
        'year' => 2008,
        'month' => 01,
        'monthstodisplay' => 12,
      ),
    ),
    'availability_calendars_month' => array(
      'arguments' => array(
        'node' => NULL,
        'year' => 2008,
        'month' => 01,
        'settings' => NULL,
      ),
    ),
  );
}

/**
 * Utility function to get settings related to nodes or administration.
 *
 * @param string $scope
 * @param string $arg
 * @return object
 *   When $scope is set to 'node' then the system settings are moved under
 *     $settings->system.
 *   Otherwise settings are set within the root of the object.
 */
function availability_calendar_getsettings($scope = NULL, $arg = NULL) {
  switch ($scope) {
    case 'nodetype':
      $settings = variable_get('availability_calendars_settings_system-type_' . $arg, 0);
      break;
    case 'node':
      if ($arg) {
        $settings->startofweek = variable_get('availability_calendars_settings_node_' . $arg . '_startofweek', 1);
        $settings->showkey = variable_get('availability_calendars_settings_node_' . $arg . '_showkey', 1);
        $settings->firstletter = variable_get('availability_calendars_settings_node_' . $arg . '_firstletter', 0);
        $settings->showteaser = variable_get('availability_calendars_settings_node_' . $arg . '_showteaser', 0);
        $settings->hideold = variable_get('availability_calendars_settings_system_hideold', 0) ? 1 : variable_get('availability_calendars_settings_node_' . $arg . '_hideold', 0);
        $settings->defaultstatus = variable_get('availability_calendars_settings_node_' . $arg . '_defaultstatus', NULL) === NULL ? variable_get('availability_calendars_settings_system_defaultstatus', 'calavailable') : variable_get('availability_calendars_settings_node_' . $arg . '_defaultstatus', 'calavailable');
      }
    default:
      $types = node_get_types();
      $system->status = array();
      foreach ($types as $id => $val) {
        $system->status[$val->type] = variable_get('availability_calendars_settings_system-type_' . $val->type, 0);
      }
      $system->monthcount = variable_get('availability_calendars_settings_system_monthcount', 12);
      $system->nodeview = variable_get('availability_calendars_settings_system_nodeview', 1);
      $system->showteaser = variable_get('availability_calendars_settings_system_showteaser', 1);
      $system->splitday = variable_get('availability_calendars_settings_system_splitday', 0);
      $system->hideold = variable_get('availability_calendars_settings_system_hideold', 0);
      $system->defaultstatus = variable_get('availability_calendars_settings_system_defaultstatus', 'calavailable');
      if ($scope == 'node' && $arg) {
        $settings->system = $system;
      }
      else {
        $settings = $system;
      }
      break;
  }
  return $settings;
}

/**
 * Themed output to display a list of node dates.
 *
 * @param object $node
 * @param int $year
 * @param int $month
 * @param int $monthstodisplay
 * @return string
 */
function theme_availability_calendars_node($node, $year = 2008, $month = 01, $monthstodisplay = 12) {
  $rows = array();
  $settings = availability_calendar_getsettings('node', $node->nid);

  // Calendar code based on example at http://evolt.org/node/60673 :
  $day = 1;
  $output = '';

  // Create our key for the availability calendar if the node has it set to do so
  if ($settings->showkey === 1) {
    $output .= availability_calendars_key();
  }
  $monthsremaining = $monthstodisplay;
  while ($monthsremaining > 0) {
    $output .= theme('availability_calendars_month', $node, $year, $month, $settings);
    $monthsremaining--;
    $month++;
    if ($month > 12) {
      $month = 1;
      $year++;
    }
  }

  // add an extra three months for editors only
  if (availability_can_edit($node->nid)) {
    $monthsremaining = 3;
    while ($monthsremaining > 0) {
      $output .= theme('availability_calendars_month', $node, $year, $month, $settings);
      $monthsremaining--;
      $month++;
      if ($month > 12) {
        $month = 1;
        $year++;
      }
    }
  }
  return $output;
}

/**
 * Creates a key for our calendars.
 *
 * @return string
 */
function availability_calendars_key() {

  //use all the same classes for cells and table, so it styles the same as the calendars
  $keytitle = '<div class="month_title key_title">' . t('Key') . '</div>';
  $headers = array(
    t('Color'),
    t('Availability'),
  );
  $rows = array(
    // keystatus class used to allow better styling of the status column
    array(
      array(
        'data' => '&nbsp;',
        'class' => 'calavailable',
      ),
      array(
        'data' => t('Available'),
        'class' => 'keystatus',
      ),
    ),
    array(
      array(
        'data' => '&nbsp;',
        'class' => 'calnotavailable',
      ),
      array(
        'data' => t('Fully booked'),
        'class' => 'keystatus',
      ),
    ),
    array(
      array(
        'data' => '&nbsp;',
        'class' => 'calnotavailableprov',
      ),
      array(
        'data' => t('Provisionally booked'),
        'class' => 'keystatus',
      ),
    ),
    array(
      array(
        'data' => '&nbsp;',
        'class' => 'caltoday',
      ),
      array(
        'data' => t('Current Day'),
        'class' => 'keystatus',
      ),
    ),
  );
  $key = theme_table($headers, $rows, array(
    'class' => 'cal',
  ));
  return '<div class="calmonth-wrapper key-wrapper">' . $keytitle . $key . '</div>';
}

/**
 * Utility function to create an array of meta data for the month.
 *
 * @param int $year
 * @param int $month
 * @param int $startofweek
 * @return array
 */
function availability_calendars_month_meta($year, $month, $settings) {
  $month_meta['daysinmonth'] = date("t", mktime(0, 0, 0, $month, 1, $year));
  $month_meta['firstday'] = date("w", mktime(0, 0, 0, $month, 1, $year)) + $settings->startofweek;
  $tempDays = $month_meta['firstday'] + $month_meta['daysinmonth'];

  // padding
  $month_meta['weeksinmonth'] = ceil($tempDays / 7);

  // Stop empty weeks occuring at start of month
  if ($month_meta['firstday'] > 6) {
    $month_meta['firstday'] = $month_meta['firstday'] - 7;
    $month_meta['weeksinmonth']--;
  }
  return $month_meta;
}

/**
 * Implementation of hook_theme().
 * produces our calendars on the viewing of an availability_calendars enabled node.
 *
 * @param object $node
 * @param int $year
 * @param int $month
 * @param object $settings
 * @return string
 */
function theme_availability_calendars_month($node, $year, $month, $settings) {
  $month_meta = availability_calendars_month_meta($year, $month, $settings);

  /**
   * here we list all the days of the week, an array of 14 (two full weeks) so
   * that if users select monday as the first day we still get a full week in
   * our following loop .
   */
  $days = array(
    13 => t('Mon'),
    12 => t('Tue'),
    11 => t('Wed'),
    10 => t('Thu'),
    9 => t('Fri'),
    8 => t('Sat'),
    7 => t('Sun'),
    6 => t('Mon'),
    5 => t('Tue'),
    4 => t('Wed'),
    3 => t('Thu'),
    2 => t('Fri'),
    1 => t('Sat'),
    0 => t('Sun'),
  );
  for ($j = 0; $j < $month_meta['weeksinmonth']; $j++) {
    for ($i = 0; $i < 7; $i++) {
      $counter++;
      $week[$j][$i] = $counter;

      // offset the days
      $week[$j][$i] -= $month_meta['firstday'];
      if ($week[$j][$i] < 1 || $week[$j][$i] > $month_meta['daysinmonth']) {
        $week[$j][$i] = "";
      }
    }
  }
  if (availability_can_edit($node->nid)) {

    //month_title has the edit link if permissable
    $month_title = t("@date", array(
      '@date' => format_date(mktime(12, 0, 0, $month, 1, $year), 'custom', 'F Y'),
    )) . ' ' . l(t('edit'), 'availability-calendars/' . $node->nid . '/' . date('Y/m', mktime(0, 0, 0, $month, 1, $year)) . '/edit', array(
      'query' => 'destination=node/' . $node->nid,
    ));
  }
  else {

    // no edit link
    $month_title = t("@date", array(
      '@date' => format_date(mktime(12, 0, 0, $month, 1, $year), 'custom', 'F Y'),
    ));
  }
  $month_title = '<div class="month_title">' . $month_title . '</div>';

  // add a class to our month_title
  $headers = array();

  // container for header row
  array_push($headers, array(
    'data' => '&nbsp;',
    'class' => 'calempty',
  ));

  // add one empty cell for the notes column
  // add a header row showing the day of the week, we do some odd backwards looping through this...
  // because the options in the node's form are set in a way that requires it
  for ($i = $settings->startofweek + 7; $i > $settings->startofweek && $i <= 7 + $settings->startofweek; $i--) {
    $day = $settings->firstletter == 0 ? $days[$i] : substr($days[$i], 0, 1);
    array_push($headers, array(
      'data' => $day,
      'class' => 'dayofweek',
    ));
  }

  // find all entries in database for this month ($availability, $notes) and pre-populate
  $notes_result = db_query('SELECT week, note FROM {availability_calendars_week} WHERE nid = %d AND year = %d AND month = %d', $node->nid, $year, $month);
  while ($note = db_fetch_array($notes_result)) {
    $notes[$note['week']] = $note['note'];
  }
  $status_result = db_query('SELECT day, status FROM {availability_calendars_day} WHERE nid = %d AND year = %d AND month = %d', $node->nid, $year, $month);
  while ($status = db_fetch_array($status_result)) {
    $day_status[$status['day']] = $status['status'];
  }
  $availability_calendars_options = availability_calendars_options();
  $today = mktime(0, 0, 0, date('m'), date('j'), date('Y'));
  $rows = array();

  // our container for rows
  $cells = array();

  // our container for cells
  $options = availability_calendars_options();
  foreach ($week as $key => $val) {
    $weeknumber = $key + 1;
    array_push($cells, array(
      'data' => $notes[$weeknumber],
      'class' => 'calnote',
    ));

    // add the note cell to the cells array
    for ($i = 0; $i < 7; $i++) {
      $day = $week[$key][$i];
      $daystamp = mktime(0, 0, 0, (int) $month, (int) $day, (int) $year);

      // if there's a date, it's part of this month
      if ($day) {
        $classes = array();
        if ($today == $daystamp) {
          $classes[] = 'caltoday';
        }

        // today
        if ($daystamp < $today) {
          $classes[] = 'calpastdate';
        }

        // past date
        if ($settings->hideold === 1 && $daystamp < $today) {
          $classes[] = 'calnotavailable';
        }
        else {
          if ($day_status[$day] === NULL) {
            $classes[] = $settings->defaultstatus ? $settings->defaultstatus : $options[0];
          }
          else {
            $classes[] = $day_status[$day];
          }
        }
        $classes = implode(' ', $classes);
        $day = strpos($day_status[$day], 'calsplit ') === 0 ? '&nbsp;<div class="cal-split-date">' . $day . '</div><div class="cal-split-bg"></div>' : $day;
        array_push($cells, array(
          'data' => $day,
          'class' => $classes,
        ));
      }
      else {

        // empty, typically row 1 or 5 in a month
        array_push($cells, array(
          'data' => '&nbsp;',
          'class' => 'calother',
        ));
      }
    }
    array_push($rows, array(
      'data' => $cells,
      'class' => 'calweek',
    ));
    $cells = array();

    // clear out our $cells array before running the next week
  }
  if ($weeknumber == 5) {
    for ($i = 0; $i < 7; $i++) {
      array_push($cells, array(
        'data' => '&nbsp;',
      ));
    }
    array_push($rows, array(
      'data' => $cells,
    ));
  }
  $output = theme_table($headers, $rows, array(
    'class' => 'cal',
  ));

  // our final table
  // now output the table with the month title above a wrapper to allow for better styling
  return '<div class="calmonth-wrapper">' . $month_title . $output . '</div>';
}

/**
 * availability_calendars status options.
 *
 * @return array
 */
function availability_calendars_options() {

  // TODO: make these configurable
  $settings = availability_calendar_getsettings();
  $statuses = array(
    'available' => t('Available'),
    'notavailable' => t('Fully booked'),
    'notavailableprov' => t('Provisionally booked'),
  );
  $ret = array();
  foreach ($statuses as $class => $label) {
    $ret['cal' . $class] = $label;
  }
  if ($settings->splitday === 1) {
    foreach ($statuses as $class => $label) {
      $sub = $statuses;
      unset($sub[$class]);
      foreach ($sub as $subclass => $sublabel) {
        $ret['calsplit cal-' . $class . '_' . $subclass] = t('@a (am)/@b (pm)', array(
          '@a' => $label,
          '@b' => $sublabel,
        ));
      }
    }
  }
  return $ret;
}

/**
 * Implementation of hook_form_alter().
 * All form alterations needed for the calendars.
 *
 * @global object $user
 * @param array $form
 * @param array $form_state
 * @param string $form_id
 */
function availability_calendars_form_alter(&$form, &$form_state, $form_id) {
  global $user;

  // Alter node type form to allow availability support to be enabled/disabled
  if ($form_id == 'node_type_form') {
    $settings = availability_calendar_getsettings();
    $enabled = $settings->status[$form['#node_type']->type];
    $form['availability_calendars'] = array(
      '#type' => 'fieldset',
      '#title' => t('Availability calendars'),
      '#collapsible' => TRUE,
      '#collapsed' => !$enabled,
    );
    $form['availability_calendars']['availability_calendars_settings_system-type'] = array(
      '#type' => 'checkbox',
      '#title' => t('Availability calendar support'),
      '#default_value' => $enabled,
      '#description' => t('Enable or disable availability support for this content type. If enabled, node owner will be able to specify node availability.'),
    );

    // TODO: add options for status' here
  }
  elseif (isset($form['type']) && $form['type']['#value'] . '_node_form' == $form_id) {
    $settings = availability_calendar_getsettings('node', $form['nid']['#value']);
    if ($settings->system->status[$form['type']['#value']] === 1) {
      $form['availability_calendars'] = array(
        '#type' => 'fieldset',
        '#title' => t('Availability calendar settings'),
      );
      $form['availability_calendars']['startofweek'] = array(
        '#type' => 'select',
        '#title' => t('First day of week'),
        '#default_value' => $settings->startofweek,
        '#options' => array(
          6 => t('Monday'),
          5 => t('Tuesday'),
          4 => t('Wednesday'),
          3 => t('Thursday'),
          2 => t('Friday'),
          1 => t('Saturday'),
          0 => t('Sunday'),
        ),
      );
      $form['availability_calendars']['defaultstatus'] = array(
        '#type' => 'select',
        '#title' => t('Default status for each day'),
        '#default_value' => $settings->defaultstatus,
        '#options' => availability_calendars_options(),
      );
      if ($settings->system->showteaser === 0) {
        $form['availability_calendars']['showteaser'] = array(
          '#type' => 'checkbox',
          '#title' => t('Show calendars in teaser'),
          '#default_value' => $settings->showteaser,
        );
      }

      // allow users to choose whether a key should be shown on the node on a per node basis
      $form['availability_calendars']['showkey'] = array(
        '#type' => 'checkbox',
        '#title' => t('Show a key for the availability calendars'),
        '#default_value' => $settings->showkey,
      );

      // whether to use only the first letter for the day of the week or not
      $form['availability_calendars']['firstletter'] = array(
        '#type' => 'checkbox',
        '#title' => t('Use only the first letter from the day of the week'),
        '#default_value' => $settings->firstletter,
      );
      if ($settings->system->hideold === 0) {
        $form['availability_calendars']['hideold'] = array(
          '#type' => 'checkbox',
          '#title' => t('Set all calendars to have their past dates shown as fully booked'),
          '#default_value' => $settings->hideold,
        );
      }
    }
  }
}

/**
 * Implementation hook node_nodeapi().
 *
 * @global object $user
 * @param object $node
 * @param string $op
 * @param string $a3
 * @param string $a4
 */
function availability_calendars_nodeapi(&$node, $op, $a3 = NULL, $a4 = NULL) {
  global $user;
  $settings = availability_calendar_getsettings('node', $node->nid);
  if ($settings->system->status[$node->type] === 0) {
    return;
  }
  switch ($op) {
    case 'view':
      if ($settings->system->nodeview === 1 && !$a3 || $settings->system->nodeview === 1 && $a3 && ($settings->system->showteaser === 1 || $settings->system->showteaser === 0 && $settings->showteaser === 1)) {
        $year = date('Y');
        $month = date('m');
        $calendar_node = $node;
        if ($node->tnid) {

          // if Content Translation module is enabled and this is a translated node, use calendar of original node
          if ($node->tnid != $node->nid) {
            $calendar_node = node_load($node->tnid);
          }
        }
        $node->content['availability_calendars'] = array(
          '#value' => theme('availability_calendars_node', $calendar_node, $year, $month, $settings->system->monthcount),
          '#weight' => 10,
        );
      }
      break;
    case 'insert':
    case 'update':
      availability_calendars_save($node);
      break;
    case 'delete':
      break;
  }
}

/**
 * Saves a set of dates associated with the node after editing it.
 *
 * @param object $node
 *   Node information to save into the database.
 */
function availability_calendars_save(&$node) {
  variable_set('availability_calendars_settings_node_' . $node->nid . '_startofweek', $node->startofweek);
  variable_set('availability_calendars_settings_node_' . $node->nid . '_showkey', $node->showkey);
  variable_set('availability_calendars_settings_node_' . $node->nid . '_firstletter', $node->firstletter);
  variable_set('availability_calendars_settings_node_' . $node->nid . '_hideold', $node->hideold);
  variable_set('availability_calendars_settings_node_' . $node->nid . '_defaultstatus', $node->defaultstatus);
  variable_set('availability_calendars_settings_node_' . $node->nid . '_showteaser', $node->showteaser);
}

/**
 * All block functions.
 */

/**
 * Implementation of hook_block().
 * Generates block for the legend to be shown in when on availability calendar nodes
 *
 * @param string $op
 * @param int $delta
 * @param array $edit
 * @return array
 */
function availability_calendars_block($op = 'list', $delta = 0, $edit = array()) {

  // The $op parameter determines what piece of information is being requested.
  switch ($op) {
    case 'list':
      $blocks['key']['info'] = t('Availability Calendar Key');
      return $blocks;
    case 'configure':
      break;
    case 'save':
      break;
    case 'view':
      switch ($delta) {
        case 'key':
          $block['subject'] = t('Availability Key');
          $block['content'] = availability_calendars_key();
          break;
      }
      return $block;
  }
}

Functions

Namesort descending Description
availability_calendars_admin_settings Settings page callback.
availability_calendars_block Implementation of hook_block(). Generates block for the legend to be shown in when on availability calendar nodes
availability_calendars_form_alter Implementation of hook_form_alter(). All form alterations needed for the calendars.
availability_calendars_init Implementation of hook_init(). Currently just adds our css/js files to pages.
availability_calendars_key Creates a key for our calendars.
availability_calendars_menu Implementation of hook_menu().
availability_calendars_menu_callback Create tab to show node availability.
availability_calendars_month_meta Utility function to create an array of meta data for the month.
availability_calendars_nodeapi Implementation hook node_nodeapi().
availability_calendars_node_edit Callback function to produce the node edit form.
availability_calendars_node_edit_form Creates the node edit form.
availability_calendars_node_edit_form_submit Callback function for submitting a node edit form.
availability_calendars_options availability_calendars status options.
availability_calendars_page Availability calendars page.
availability_calendars_perm Implementation of hook_perm().
availability_calendars_save Saves a set of dates associated with the node after editing it.
availability_calendars_theme Implementation of hook_theme().
availability_calendar_getsettings Utility function to get settings related to nodes or administration.
availability_can_edit A callback function to see if allowed to edit the calendar.
theme_availability_calendars_month Implementation of hook_theme(). produces our calendars on the viewing of an availability_calendars enabled node.
theme_availability_calendars_node Themed output to display a list of node dates.