You are here

fullcalendar.module in FullCalendar 7

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.10');

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

/**
 * Implements hook_library().
 */
function fullcalendar_library() {
  $libraries['fullcalendar'] = array(
    'title' => 'FullCalendar',
    'website' => 'http://arshaw.com/fullcalendar',
    'version' => FULLCALENDAR_MIN_PLUGIN_VERSION,
    'js' => array(
      fullcalendar_get_js_path() => array(),
    ),
    'css' => array(
      variable_get('fullcalendar_path', FULLCALENDAR_PATH) . '/fullcalendar.css' => array(
        'type' => 'file',
        'media' => 'screen',
      ),
      drupal_get_path('module', 'fullcalendar') . '/fullcalendar.custom.css' => array(
        'type' => 'file',
        'media' => 'screen',
      ),
    ),
    'dependencies' => array(
      array(
        'system',
        'ui.draggable',
      ),
      array(
        'system',
        'ui.droppable',
      ),
      array(
        'system',
        'ui.resizable',
      ),
      array(
        'system',
        'effects.highlight',
      ),
    ),
  );
  return $libraries;
}

/**
 * Implements hook_permission().
 *
 * @return
 *   An array of valid permissions for the FullCalendar module.
 */
function fullcalendar_permission() {
  return array(
    'update any fullcalendar event' => array(
      'title' => t('Update any FullCalendar event'),
      'description' => t('Allow user to edit events, ignoring other permissions.'),
    ),
  );
}

/**
 * Implements hook_menu().
 *
 * @return
 *   An array of menu items.
 */
function fullcalendar_menu() {
  $items = array();
  $items['admin/config/calendar'] = array(
    'title' => 'Calendar',
    'description' => 'Settings for calendars.',
    'page callback' => 'system_admin_menu_block_page',
    'access arguments' => array(
      'access administration pages',
    ),
    'file' => 'system.admin.inc',
    'file path' => drupal_get_path('module', 'system'),
  );
  $items['admin/config/calendar/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)) {
    $langcode = field_language('node', $node, $field);
    $old_start = $node->{$field}[$langcode][$index]['value'];
    $old_end = $node->{$field}[$langcode][$index]['value2'];
    switch ($action) {
      case 'drop':
        $node->{$field}[$langcode][$index]['value'] = date('Y-m-d H:i:s', strtotime($old_start . ' ' . $day_delta . ' days ' . $minute_delta . ' minutes'));
        $node->{$field}[$langcode][$index]['value2'] = date('Y-m-d H:i:s', strtotime($old_end . ' ' . $day_delta . ' days ' . $minute_delta . ' minutes'));
        break;
      case 'resize':
        $node->{$field}[$langcode][$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_output(array(
      'msg' => 'The new event time has been saved. [<a href="javascript:void(0);" class="fullcalendar-status-close">close</a>]',
    ));
  }
}

/**
 * Implements hook_theme().
 */
function fullcalendar_theme() {
  return array(
    'fullcalendar_classname' => array(
      'variables' => array(
        'node' => NULL,
      ),
    ),
    'fullcalendar_link' => array(
      'variables' => 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($variables) {
  $node = $variables['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_library('fullcalendar', 'fullcalendar');
  drupal_add_js(array(
    'fullcalendar' => $settings,
  ), 'setting');
  drupal_add_js(drupal_get_path('module', 'fullcalendar') . '/fullcalendar.views.js');
}

/**
 * Prepares variables for template file invoked for node row type.
 */
function template_preprocess_views_view_node_fullcalendar(&$vars) {
  $entity_type = 'node';
  if (isset($vars['view']->empty_text)) {
    $vars['empty_text'] = $vars['view']->empty_text;
    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', array(
    'node' => $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 ($url_field = field_get_items('node', $node, $url_field)) {
      $vars['url'] = $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($entity_type, $node, $field_name);
        $vars['data'][] = theme('fullcalendar_link', array(
          'node' => $node,
          'attributes' => $attributes,
          'index' => 0,
        ));
        $display_field = array();
        break;
      }

      // If a date_type field exists
      if (isset($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) {
    $instance = field_info_instance($entity_type, $field_name, $node->type);
    $items = field_get_items($entity_type, $node, $field_name);
    if (!empty($items)) {
      foreach ($items as $index => $item) {
        $vars['data'][] = theme('fullcalendar_link', array(
          'node' => $node,
          'attributes' => _fullcalendar_set_display_times($entity_type, $node, $field_name, $instance, $field, $item),
          'index' => $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($entity_type, $entity, $field_name, $instance = NULL, $field = NULL, $item = NULL) {
  if (is_array($entity->{$field_name})) {
    $date = date_formatter_process(NULL, $entity_type, $entity, $field, $instance, $entity->language, $item, NULL);
    $date1 = $date['value']['local']['object'];
    $date2 = $date['value2']['local']['object'];
  }
  else {
    $date1 = new DateObject($entity->{$field_name}, date_default_timezone(), DATE_FORMAT_UNIX);
    $date2 = $date1;
  }
  return array(
    'field' => $field_name,
    'allDay' => date_field_all_day($field, $instance, $date1, $date2),
    'start' => $date1,
    'end' => $date2,
    'nid' => $entity->nid,
    'cn' => $entity->class,
    'title' => $entity->title,
    'class' => 'fullcalendar_event_details',
    'editable' => $entity->editable,
  );
}

/**
 * Sets the text for the fallback display.
 */
function theme_fullcalendar_link($variables) {
  $node = $variables['node'];
  $attributes = $variables['attributes'];
  $index = $variables['index'];
  $text = date_format_date($attributes['start']);
  if (!$attributes['allDay']) {
    $text .= ' to ' . date_format_date($attributes['end']);
  }
  $attributes['index'] = $index;
  $attributes['start'] = $attributes['start']
    ->format(DATE_FORMAT_DATETIME);
  $attributes['end'] = $attributes['end']
    ->format(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;
}

/**
 * Find all date fields in this instance.
 *
 * Field type is not in the $field array we get from
 * field_info_instances(), we need to call
 * field_info_field() to find that.
 */
function fullcalendar_date_fields($entity, $entity_type = 'node') {
  $bundle = '';
  switch ($entity_type) {
    case 'node':
      $bundle = $entity->type;
      break;
  }
  $fields = array();
  foreach (array_keys(field_info_instances($entity_type, $bundle)) as $field_name) {
    $field = field_info_field($field_name);
    if (in_array($field['type'], array(
      'date',
      'datestamp',
      'datetime',
    ))) {
      $fields[$field_name] = $field;
    }
  }
  return $fields;
}

/**
 * Checks FullCalendar dependencies.
 *
 * @return
 *   Array with TRUE/FALSE for each dependency.
 *
 * @see fullcalendar_requirements()
 */
function _fullcalendar_status() {
  $status = array();
  $status['fullcalendar_plugin'] = FALSE;
  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 Colorbox 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 FullCalendar 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 FullCalender 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 Find all date fields in this instance.
fullcalendar_get_js_path Returns the path to the FullCalender plugin.
fullcalendar_get_version Returns the version of FullCalender plugin that is installed.
fullcalendar_library Implements hook_library().
fullcalendar_menu Implements hook_menu().
fullcalendar_permission Implements hook_permission().
fullcalendar_theme Implements hook_theme().
fullcalendar_update Saves the updated FullCalendar event's datetime.
fullcalendar_views_api Implements 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.
_fullcalendar_update_access Checks if the user has access to update the given FullCalendar event.

Constants

Namesort descending Description
FULLCALENDAR_MIN_PLUGIN_VERSION The minimum supported version of the FullCalender plugin.
FULLCALENDAR_PATH The default path to the FullCalender plugin.