You are here

availability_calendars.module in Availability Calendars 5

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>

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>
 */

/**
 * Implementation of hook_menu()
 */
function availability_calendars_menu($may_cache) {
  $items = array();
  if ($may_cache) {
    $items[] = array(
      'path' => 'admin/settings/availability-calendars',
      'title' => t('Availability Calendars'),
      'description' => t('Configure global settings for availability calendars module.'),
      'callback' => 'drupal_get_form',
      'callback arguments' => array(
        'availability_calendars_admin_settings',
      ),
    );
  }
  else {
    $items[] = array(
      'path' => 'availability-calendars',
      'title' => t('Availability calendar'),
      'callback' => 'availability_calendars_page',
      'access' => user_access('edit availability calendars') || user_access('edit own availability calendars'),
      'type' => MENU_CALLBACK,
    );
  }
  return $items;
}

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

/**
 * Settings page callback.
 */
function availability_calendars_admin_settings() {
  $form = array();
  $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_display_nodeview'] = array(
    '#type' => 'checkbox',
    '#title' => t('Node view page'),
    '#default_value' => variable_get('availability_calendars_display_nodeview', TRUE),
  );
  $form['display']['availability_calendars_display_monthcount'] = array(
    '#type' => 'textfield',
    '#title' => t('Number of months to display'),
    '#default_value' => variable_get('availability_calendars_display_monthcount', 12),
    '#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."),
  );

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

/**
 * Create tab to show node availability
 */
function availability_calendars_menu_callback() {
  $node = node_load(arg(1));
  if (availability_calendars_enabled($node->type)) {
    return theme('availability_calendars_node', $node);
  }
  else {
    return FALSE;
  }
}

/**
 * Availability calendars page
 */
function availability_calendars_page($nid = NULL, $year = NULL, $month = NULL, $edit = FALSE) {
  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)) {
      drupal_set_title(t('Availability for %name in %date', array(
        '%name' => $node->title,
        '%date' => format_date(strtotime("{$year}-{$month}-01 12:00"), 'custom', 'M 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 = variable_get('availability_display_monthcount', 12);
      return theme('availability_calendars_node', $node, $year, $month, $monthstodisplay);
    }
  }
  else {
    print drupal_not_found();
  }
}
function availability_calendars_node_edit() {
  return drupal_get_form('availability_calendars_node_edit_form');
}
function availability_calendars_node_edit_form() {
  $nid = check_plain(arg(1));
  $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,
  );
  $startofweek = variable_get('availability_calendars_' . $nid . '_startofweek', 1);
  $month_meta = availability_calendars_month_meta($year, $month, $startofweek);

  // 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' => t("@date", array(
          '@date' => format_date(strtotime("{$year}-{$month}-{$day} 12:00"), 'custom', 'l d F'),
        )),
        '#options' => availability_calendars_options(),
        '#default_value' => $day_status[$day],
      );
      $day++;
      $daysinweekremaining--;
    }
  }
  $form['submit'] = array(
    '#type' => 'submit',
    '#value' => t('Submit'),
  );
  return $form;
}
function availability_calendars_node_edit_form_submit($form_id, $form_values) {
  $nid = check_plain($form_values['nid']);
  $year = check_plain($form_values['year']);
  $month = check_plain($form_values['month']);

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

  // save $days
  $day = 1;
  $nomoredays = FALSE;
  while (!$nomoredays) {
    if (isset($form_values['day-' . $day])) {
      $days[$day] = $form_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) VALUES (%d, %d, %d, %d, %d)', $nid, $year, $month, $day, $status);
  }
  drupal_set_message('Availability information saved.');
}

/**
 * Themed output to display a lits of node dates.
 *
 * @param $node
 * @return HTML output.
 */
function theme_availability_calendars_node($node, $year = 2008, $month = 01, $monthstodisplay = 12) {
  drupal_add_css(drupal_get_path('module', 'availability_calendars') . '/availability_calendars.css');
  $rows = array();

  // Fill availability array

  /* foreach ($node->availability_dates as $k => $v) {
      foreach ($v as $i) {
        $booked[date("Y", $i['date'])][date("m", $i['date'])][date("d", $i['date'])] = 1;
      }
    } */

  // Calendar code based on example at http://evolt.org/node/60673 :
  $day = 1;
  $startofweek = variable_get('availability_calendars_' . $node->nid . '_startofweek', 1);
  $monthsremaining = $monthstodisplay;
  while ($monthsremaining > 0) {
    $output .= theme('availability_calendars_month', $node, $year, $month, $startofweek, $booked);
    $monthsremaining--;
    $month++;
    if ($month > 12) {
      $month = 1;
      $year++;
    }
  }

  // add an extra three months for editors only
  if (user_access('edit availability calendars')) {
    $monthsremaining = 3;
    while ($monthsremaining > 0) {
      $output .= theme('availability_calendars_month', $node, $year, $month, $startofweek, $booked);
      $monthsremaining--;
      $month++;
      if ($month > 12) {
        $month = 1;
        $year++;
      }
    }
  }
  return $output;
}
function availability_calendars_month_meta($year, $month, $startofweek) {
  $month_meta['daysinmonth'] = date("t", mktime(0, 0, 0, $month, 1, $year));
  $month_meta['firstday'] = date("w", mktime(0, 0, 0, $month, 1, $year)) + $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;
}
function theme_availability_calendars_month($node, $year, $month, $startofweek, $booked) {
  $month_meta = availability_calendars_month_meta($year, $month, $startofweek);
  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] = "";
      }
    }
  }
  $output = "<table class='cal'>\n";
  $output .= "<tr class='calmonth'>\n";
  $output .= "<th colspan='8'>" . t("@date", array(
    '@date' => format_date(mktime(12, 0, 0, $month, 1, $year), 'custom', 'F Y'),
  ));
  if (user_access('edit availability calendars')) {
    $output .= ' ' . l('edit', 'availability-calendars/' . $node->nid . '/' . date('Y/m', mktime(0, 0, 0, $month, 1, $year)) . '/edit', NULL, 'destination=node/' . $node->nid);
  }
  $output .= "</th>\n";
  $output .= "</tr>\n";

  /*  $days = array(
        6 => array("M", "Mon", "Monday"),
        5 => array("T", "Tue", "Tuesday"),
        4 => array("W", "Wed", "Wednesday"),
        3 => array("T", "Thu", "Thursday"),
        2 => array("F", "Fri", "Friday"),
        1 => array("S", "Sat", "Saturday"),
        0 => array("S", "Sun", "Sunday"),
      );

   // Figure out how to generate the header - see archive.module in 4.7
    $output .= "<tr class='caldays'>\n";
    $output .= "<th></th>\n";
    $output .= "<th>S</th>\n";
    $output .= "<th>S</th>\n";
    $output .= "<th>M</th>\n";
    $output .= "<th>T</th>\n";
    $output .= "<th>W</th>\n";
    $output .= "<th>T</th>\n";
    $output .= "<th>F</th>\n";
    $output .= "</tr>\n";
   */

  // 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();
  foreach ($week as $key => $val) {
    $weeknumber = $key + 1;
    $output .= "<tr class='calweek'>\n";
    $output .= "<td class='calnote'>" . $notes[$weeknumber] . "</td>\n";
    for ($i = 0; $i < 7; $i++) {

      // if there's a date, it's part of this month
      if ($week[$key][$i]) {
        if ($day_status[$week[$key][$i]] == 1) {

          // booked
          $output .= " <td class='calnotavailable'>" . $week[$key][$i] . "</td>\n";
        }
        elseif ($day_status[$week[$key][$i]] == 2) {

          // provisionally booked
          $output .= " <td class='calnotavailableprov'>" . $week[$key][$i] . "</td>\n";
        }
        else {

          // available
          $output .= " <td class='calavailable'>" . $week[$key][$i] . "</td>\n";
        }
      }
      else {
        $output .= " <td class='calothermonth'>" . $number . "</td>\n";
      }
    }
    $output .= "</tr>\n";
  }
  if ($weeknumber == 5) {
    $output .= "<tr>\n";
    $output .= "<td colspan='8'>&nbsp;</td>\n";
    $output .= "</tr>\n";
  }
  $output .= "</table>";
  return $output;
}
function availability_calendars_options() {

  // TODO: make these configurable
  return array(
    0 => t('Available'),
    1 => t('Fully booked'),
    2 => t('Provisionally booked'),
  );
}

/**
 * Implementation of hook_form_alter();
 */
function availability_calendars_form_alter($form_id, &$form) {
  global $user;

  // Alter node type form to allow availability support to be enabled/disabled
  if ($form_id == 'node_type_form') {
    $form['workflow']['availability_calendars'] = array(
      '#type' => 'radios',
      '#title' => t('Availability calendar support'),
      '#default_value' => variable_get('availability_calendars_' . $form['#node_type']->type, 0),
      '#options' => array(
        0 => t('Disabled'),
        1 => t('Enabled'),
      ),
      '#description' => t('Enable or disable availability support for this content type. If enabled, node owner will be able to specify node availability.'),
    );
  }
  elseif (isset($form['type']) && $form['type']['#value'] . '_node_form' == $form_id && availability_calendars_enabled($form['type']['#value'])) {
    $form['availability'] = array(
      '#type' => 'fieldset',
      '#title' => t('Availability calendar settings'),
    );
    $form['availability']['startofweek'] = array(
      '#type' => 'select',
      '#title' => t('First day of week'),
      '#default_value' => variable_get('availability_calendars_' . $form['nid']['#value'] . '_startofweek', 1),
      '#options' => array(
        6 => t('Monday'),
        5 => t('Tuesday'),
        4 => t('Wednesday'),
        3 => t('Thursday'),
        2 => t('Friday'),
        1 => t('Saturday'),
        0 => t('Sunday'),
      ),
    );
  }
}

/**
 * Implementation hook node_nodeapi();
 */
function availability_calendars_nodeapi(&$node, $op, $a3 = null, $a4 = null) {
  global $user;
  if (!availability_calendars_enabled($node->type)) {
    return;
  }
  switch ($op) {
    case 'view':
      if (variable_get('availability_calendars_display_nodeview', TRUE)) {
        $year = date('Y');
        $month = date('m');
        $monthstodisplay = variable_get('availability_calendars_display_monthcount', 12);
        $node->content['availability_calendars'] = array(
          '#value' => theme('availability_calendars_node', $node, $year, $month, $monthstodisplay),
          '#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_' . $node->nid . '_startofweek', $node->startofweek);
}

/**
 * Checks if availability is enabled for specified content type.
 *
 * @param string $node_type
 */
function availability_calendars_enabled($node_type) {
  return variable_get('availability_calendars_' . $node_type, 0);
}

Functions

Namesort descending Description
availability_calendars_admin_settings Settings page callback.
availability_calendars_enabled Checks if availability is enabled for specified content type.
availability_calendars_form_alter Implementation of hook_form_alter();
availability_calendars_menu Implementation of hook_menu()
availability_calendars_menu_callback Create tab to show node availability
availability_calendars_month_meta
availability_calendars_nodeapi Implementation hook node_nodeapi();
availability_calendars_node_edit
availability_calendars_node_edit_form
availability_calendars_node_edit_form_submit
availability_calendars_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.
theme_availability_calendars_month
theme_availability_calendars_node Themed output to display a lits of node dates.