You are here

fullcalendar.module in FullCalendar 6

Provides a views style plugin for FullCalendar

File

fullcalendar.module
View source
<?php

/**
 * @file
 * Provides a views style plugin for FullCalendar
 */

/**
 * The default path to the FullCalender plugin.
 */
define('FULLCALENDAR_PATH', 'sites/all/libraries/fullcalendar');

/**
 * The minimum supported version of the FullCalender plugin.
 */
define('FULLCALENDAR_MIN_PLUGIN_VERSION', '1.4.9');

/**
 * The minimum supported jQuery version.
 */
define('FULLCALENDAR_MIN_JQUERY_VERSION', '1.3.2');

/**
 * The maximum supported jQuery version.
 */
define('FULLCALENDAR_MAX_JQUERY_VERSION', '1.4+');

/**
 * The minimum supported jQuery UI version.
 */
define('FULLCALENDAR_MIN_JQUERYUI_VERSION', '1.7.3');

/**
 * The maximum supported jQuery UI version.
 */
define('FULLCALENDAR_MAX_JQUERYUI_VERSION', '1.8+');

/**
 * Implementation of hook_views_api().
 */
function fullcalendar_views_api() {
  return array(
    'api' => '2',
    'path' => drupal_get_path('module', 'fullcalendar'),
  );
}

/**
 * Implementation of hook_init().
 */
function fullcalendar_init() {
  drupal_add_js(fullcalendar_get_js_path());
  drupal_add_css(variable_get('fullcalendar_path', FULLCALENDAR_PATH) . '/fullcalendar.css');
  drupal_add_css(drupal_get_path('module', 'fullcalendar') . '/fullcalendar.custom.css');

  // We need some jQuery UI files.
  $files = array(
    'ui.draggable',
    'ui.droppable',
    'ui.resizable',
    'effects.highlight',
  );
  jquery_ui_add($files);
}

/**
 * Implementation of hook_perm().
 *
 * @return
 *   An array of valid permissions for the FullCalendar module.
 */
function fullcalendar_perm() {
  return array(
    'update any fullcalendar event',
  );
}

/**
 * Implementation of hook_menu().
 *
 * @return
 *   An array of menu items.
 */
function fullcalendar_menu() {
  $items = array();
  $items['admin/settings/fullcalendar'] = array(
    'title' => 'FullCalendar',
    'description' => 'Adjust FullCalendar settings.',
    'file' => 'fullcalendar.admin.inc',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'fullcalendar_admin_settings',
    ),
    'access arguments' => array(
      'administer site configuration',
    ),
  );
  $items['fullcalendar/ajax/update/%/%node'] = array(
    'title' => 'Update event',
    'description' => 'Save the updated event datetime.',
    'page callback' => 'fullcalendar_update',
    'page arguments' => array(
      3,
      4,
    ),
    'access callback' => '_fullcalendar_update_access',
    'access arguments' => array(
      4,
    ),
    'type' => MENU_CALLBACK,
  );
  return $items;
}

/**
 * Saves the updated fullcalendar event's datetime.
 *
 * @param $action
 *   Value can be 'drop' or 'resize'.
 * @param $node
 *   The node that will be updated.
 */
function fullcalendar_update($action, $node) {

  // Retrieve the post vars form the ajax call.
  $field = $_POST['field'];
  $index = $_POST['index'];
  $all_day = isset($_POST['all_day']) ? $_POST['all_day'] : '';
  $day_delta = $_POST['day_delta'];
  $minute_delta = $_POST['minute_delta'];
  if (!empty($field) && isset($index)) {
    $old_start = $node->{$field}[$index]['value'];
    $old_end = $node->{$field}[$index]['value2'];
    switch ($action) {
      case 'drop':
        $node->{$field}[$index]['value'] = date('Y-m-d H:i:s', strtotime($old_start . ' ' . $day_delta . ' days ' . $minute_delta . ' minutes'));
        $node->{$field}[$index]['value2'] = date('Y-m-d H:i:s', strtotime($old_end . ' ' . $day_delta . ' days ' . $minute_delta . ' minutes'));
        break;
      case 'resize':
        $node->{$field}[$index]['value2'] = date('Y-m-d H:i:s', strtotime($old_end . ' ' . $day_delta . ' days ' . $minute_delta . ' minutes'));
        break;
    }

    // Save the new start/end values.
    node_save($node);
    drupal_json(array(
      'msg' => 'The new event time has been saved. [<a href="javascript:void(0);" class="fullcalendar-status-close">close</a>]',
    ));
  }
}

/**
 * Implementation of hook_theme().
 */
function fullcalendar_theme() {
  return array(
    'fullcalendar_classname' => array(
      'arguments' => array(
        'node' => NULL,
      ),
    ),
    'fullcalendar_link' => array(
      'arguments' => array(
        'node' => NULL,
        'attributes' => NULL,
        'index' => NULL,
      ),
    ),
  );
}

/**
 * Constructs CSS classes for a node.
 *
 * @param $node
 *   An object representing the node.
 *
 * @return
 *   A string suitable for use as a CSS class.
 */
function theme_fullcalendar_classname($node) {
  $className = array(
    'fc-event-default',
    $node->type,
  );
  return implode(' ', $className);
}

/**
 * Passes options to the FullCalendar plugin as JavaScript settings.
 */
function template_preprocess_views_view_fullcalendar(&$vars) {
  $settings = array(
    'defaultView' => $vars['options']['display']['fc_view'],
    'firstDay' => $vars['options']['display']['fc_firstday'],
    'weekMode' => $vars['options']['display']['fc_weekmode'],
    'theme' => $vars['options']['modules']['fc_theme'],
    'colorbox' => $vars['options']['modules']['fc_url_colorbox'],
    'left' => $vars['options']['header']['fc_left'],
    'center' => $vars['options']['header']['fc_center'],
    'right' => $vars['options']['header']['fc_right'],
    'year' => $vars['options']['defaults']['fc_year'],
    'month' => $vars['options']['defaults']['fc_month'],
    'day' => $vars['options']['defaults']['fc_day'],
    'agenda' => $vars['options']['times']['fc_timeformat'],
    'clock' => $vars['options']['times']['fc_clock'],
    'monthNames' => array_values(date_month_names(TRUE)),
    'monthNamesShort' => array_values(date_month_names_abbr(TRUE)),
    'dayNames' => date_week_days(TRUE),
    'dayNamesShort' => date_week_days_abbr(TRUE),
    'allDayText' => t('All day'),
    'dayString' => t('Day'),
    'weekString' => t('Week'),
    'monthString' => t('Month'),
    'todayString' => t('Today'),
  );
  drupal_add_js(array(
    'fullcalendar' => $settings,
  ), 'setting');
  drupal_add_js(drupal_get_path('module', 'fullcalendar') . '/fullcalendar.views.js', 'module');
}

/**
 * Prepares variables for template file invoked for node row type.
 */
function template_preprocess_views_view_node_fullcalendar(&$vars) {
  if (isset($vars['view']->empty)) {
    $vars['empty_text'] = $vars['view']->empty;
    return;
  }
  $nid = $vars['row']->{$vars['field_alias']};
  if (!is_numeric($nid)) {
    return;
  }
  $node = node_load($nid);
  if (empty($node)) {
    return;
  }

  // Allow resize/drag/drop of an event if user has proper permissions.
  $node->editable = _fullcalendar_update_access($node);
  $node->class = theme('fullcalendar_classname', $node);
  $vars['node'] = $node;
  $vars['data'] = array();

  // Contains the start, end & allDay values.
  $node->url = 'node/' . $nid;
  if ($url_field = $vars['options']['fullcalendar_url_field']) {
    if (isset($node->{$url_field}[0]['value'])) {
      $node->url = $node->{$url_field}[0]['value'];
    }
  }
  $title_field = $vars['options']['fullcalendar_title_field'];
  if (!empty($title_field) && !empty($node->{$title_field}[0]['value'])) {
    $node->title = $node->{$title_field}[0]['value'];
  }
  $display_field = fullcalendar_date_fields($node);
  $field_names = trim(strip_tags($vars['options']['fullcalendar_date_fields']));
  if (!empty($field_names)) {
    foreach (explode("\n", $field_names) as $field_name) {
      $field_name = trim(strip_tags($field_name));
      if ($field_name == 'created' || $field_name == 'changed') {
        $attributes = _fullcalendar_set_display_times($node, $field_name);
        $vars['data'][] = theme('fullcalendar_link', $node, $attributes);
        $display_field = array();
        break;
      }

      // If a date_type field exists
      if ($display_field[$field_name]) {
        $display_field = array(
          $field_name => $display_field[$field_name],
        );
        break;
      }
    }
  }

  // Iterate through available fields, using the first one found.
  foreach ($display_field as $field_name => $field) {
    foreach ($node->{$field_name} as $index => $item) {
      $attributes = _fullcalendar_set_display_times($node, $field_name, $field, $item);
      $vars['data'][] = theme('fullcalendar_link', $node, $attributes, $index);
    }
    break;
  }
}

/**
 * Translates times to the right display times
 *
 * This is done by passing times through date modules date_formatter_process
 * function.
 *
 * @param $node
 *   The node that will be updated.
 * @param $field_name
 *   The name of the date field.
 * @param $field
 *   The field structure for the date field.
 * @param $item
 *   The field value for the date field.
 *
 * @return
 *   Array of event attributes to pass to the FullCalendar plugin.
 */
function _fullcalendar_set_display_times($node, $field_name, $field = NULL, $item = NULL) {
  if (is_array($node->{$field_name})) {
    $dfp_info = array(
      '#node' => $node,
      '#field_name' => $field_name,
      '#formatter' => NULL,
      '#item' => $item,
    );
    $date = date_formatter_process($dfp_info);
    $date1 = $date['value']['local']['object'];
    $date2 = $date['value2']['local']['object'];
  }
  else {
    $date1 = date_make_date($node->{$field_name}, date_default_timezone(), DATE_UNIX);
    $date2 = $date1;
  }
  return array(
    'field' => $field_name,
    'allDay' => date_field_all_day($field, $date1, $date2),
    'start' => $date1,
    'end' => $date2,
    'nid' => $node->nid,
    'cn' => $node->class,
    'title' => $node->title,
    'class' => 'fullcalendar_event_details',
    'editable' => $node->editable,
  );
}

/**
 * Sets the text for the fallback display.
 */
function theme_fullcalendar_link($node, $attributes, $index = 0) {
  $text = date_format_date($attributes['start']);
  if (!$attributes['allDay']) {
    $text .= ' to ' . date_format_date($attributes['end']);
  }
  $attributes['index'] = $index;
  $attributes['start'] = date_format($attributes['start'], DATE_FORMAT_DATETIME);
  $attributes['end'] = date_format($attributes['end'], DATE_FORMAT_DATETIME);
  return l($text, $node->url, array(
    'attributes' => $attributes,
  ));
}

/**
 * Checks if the user has access to update the given fullcalendar event.
 *
 * @param $node
 *   The node that will be updated.
 *
 * @return
 *   TRUE if the user is allowed to update the node;
 *   FALSE if the user is not permitted to update the node.
 */
function _fullcalendar_update_access($node) {
  global $user;
  if (!empty($node) && (user_access('administer nodes') || user_access('update any fullcalendar event') || user_access('edit any ' . $node->type . ' content') || user_access('edit own ' . $node->type . ' content') && $node->uid == $user->uid)) {
    return TRUE;
  }
  return FALSE;
}

/**
 * Finds all the date fields for a given node.
 *
 * @param $node
 *   The node that will be updated.
 *
 * @return
 *   Array of date fields for the given node.
 */
function fullcalendar_date_fields($node) {
  $type = content_types($node->type);
  $fields = array();
  foreach ($type['fields'] as $field_name => $field) {
    if (in_array($field['type'], array(
      'date',
      'datestamp',
      'datetime',
    ))) {
      $fields[$field_name] = $field;
    }
  }
  return $fields;
}

/**
 * Checks FullCalendar dependencies, jQuery version, and Colorbox plugin.
 *
 * @return
 *   Array with TRUE/FALSE for each dependency.
 *
 * @see fullcalendar_requirements()
 */
function _fullcalendar_status() {
  $status = array();
  $status['jquery_version'] = FALSE;
  $status['fullcalendar_plugin'] = FALSE;
  if (function_exists('jquery_update_get_version')) {
    if (version_compare(jquery_update_get_version(), FULLCALENDAR_MIN_JQUERY_VERSION, '>=')) {
      $status['jquery_version'] = TRUE;
    }
  }
  if (function_exists('jquery_ui_get_version')) {
    if (version_compare(jquery_ui_get_version(), FULLCALENDAR_MIN_JQUERYUI_VERSION, '>=')) {
      $status['jqueryui_version'] = TRUE;
    }
  }
  if (version_compare(fullcalendar_get_version(), FULLCALENDAR_MIN_PLUGIN_VERSION, '>=')) {
    $status['fullcalendar_plugin'] = TRUE;
  }
  return $status;
}

/**
 * Returns the version of Fullcalender plugin that is installed.
 *
 * This can be used by other modules' hook_requirements() to ensure that the
 * proper version of FullCalender plugin is installed.
 *
 * @see version_compare()
 */
function fullcalendar_get_version($fullcalendar_path = NULL) {
  $version = 0;
  $pattern = '#FullCalendar v([0-9\\.a-z]+)#';

  // No file is passed in so use the default location.
  if (is_null($fullcalendar_path)) {
    $fullcalendar_path = fullcalendar_get_js_path();
  }

  // Return the version of Colorbox plugin.
  $fullcalendar_plugin = file_get_contents($fullcalendar_path, NULL, NULL, 0, 40);
  if (preg_match($pattern, $fullcalendar_plugin, $matches)) {
    $version = $matches[1];
  }
  return $version;
}

/**
 * Returns the path to the FullCalander js plugin.
 */
function fullcalendar_get_js_path() {
  $fullcalendar_file = array(
    'none' => 'fullcalendar.js',
    'min' => 'fullcalendar.min.js',
  );
  return variable_get('fullcalendar_path', FULLCALENDAR_PATH) . '/' . $fullcalendar_file[variable_get('fullcalendar_compression_type', 'min')];
}

Functions

Namesort descending Description
fullcalendar_date_fields Finds all the date fields for a given node.
fullcalendar_get_js_path Returns the path to the FullCalander js plugin.
fullcalendar_get_version Returns the version of Fullcalender plugin that is installed.
fullcalendar_init Implementation of hook_init().
fullcalendar_menu Implementation of hook_menu().
fullcalendar_perm Implementation of hook_perm().
fullcalendar_theme Implementation of hook_theme().
fullcalendar_update Saves the updated fullcalendar event's datetime.
fullcalendar_views_api Implementation of hook_views_api().
template_preprocess_views_view_fullcalendar Passes options to the FullCalendar plugin as JavaScript settings.
template_preprocess_views_view_node_fullcalendar Prepares variables for template file invoked for node row type.
theme_fullcalendar_classname Constructs CSS classes for a node.
theme_fullcalendar_link Sets the text for the fallback display.
_fullcalendar_set_display_times Translates times to the right display times
_fullcalendar_status Checks FullCalendar dependencies, jQuery version, and Colorbox plugin.
_fullcalendar_update_access Checks if the user has access to update the given fullcalendar event.

Constants

Namesort descending Description
FULLCALENDAR_MAX_JQUERYUI_VERSION The maximum supported jQuery UI version.
FULLCALENDAR_MAX_JQUERY_VERSION The maximum supported jQuery version.
FULLCALENDAR_MIN_JQUERYUI_VERSION The minimum supported jQuery UI version.
FULLCALENDAR_MIN_JQUERY_VERSION The minimum supported jQuery version.
FULLCALENDAR_MIN_PLUGIN_VERSION The minimum supported version of the FullCalender plugin.
FULLCALENDAR_PATH The default path to the FullCalender plugin.