You are here

fullcalendar.module in FullCalendar 7.2

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 FullCalendar plugin.
 */
function fullcalendar_default_path() {
  return module_exists('libraries') ? libraries_get_path('fullcalendar') : 'sites/all/libraries/fullcalendar';
}

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

/**
 * The recommended version of the FullCalendar plugin.
 */
define('FULLCALENDAR_RECOMMENDED_PLUGIN_VERSION', '1.5.1');

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

/**
 * Implements hook_theme().
 */
function fullcalendar_theme($existing, $type, $theme, $path) {
  return array(
    'fullcalendar_event' => array(
      'variables' => array(
        'event' => NULL,
        'entity' => NULL,
      ),
      'file' => 'theme.inc',
      'path' => $path . '/theme',
    ),
  );
}

/**
 * Implements hook_library().
 */
function fullcalendar_library() {
  $path = variable_get('fullcalendar_path', fullcalendar_default_path());
  $css_files = array(
    $path . '/fullcalendar.css' => array(
      'media' => 'screen',
    ),
  );

  // Add fullcalendar.print.css if it is available.
  if (version_compare(fullcalendar_get_version(), '1.5.0', '>=')) {
    $css_files[$path . '/fullcalendar.print.css'] = array(
      'media' => 'print',
    );
  }
  $libraries['fullcalendar'] = array(
    'title' => 'FullCalendar',
    'website' => 'http://arshaw.com/fullcalendar',
    'version' => FULLCALENDAR_MIN_PLUGIN_VERSION,
    'js' => array(
      fullcalendar_get_js_path() => array(),
      $path . '/gcal.js' => array(),
    ),
    'css' => $css_files,
    'dependencies' => array(
      array(
        'system',
        'ui.draggable',
      ),
      array(
        'system',
        'ui.droppable',
      ),
      array(
        'system',
        'ui.resizable',
      ),
      array(
        'system',
        'effects.highlight',
      ),
    ),
  );
  return $libraries;
}

/**
 * Implements hook_permission().
 *
 * @return array
 *   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 array
 *   An array of menu items.
 */
function fullcalendar_menu() {
  $items = array();
  $items['admin/config/user-interface/fullcalendar'] = array(
    'title' => 'FullCalendar',
    'description' => 'Adjust FullCalendar settings.',
    'file' => 'fullcalendar.admin.inc',
    'file path' => drupal_get_path('module', 'fullcalendar') . '/includes',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'fullcalendar_admin_settings',
    ),
    'access arguments' => array(
      'administer site configuration',
    ),
    'type' => MENU_NORMAL_ITEM,
    'weight' => 0,
  );
  $items['admin/config/user-interface/fullcalendar/default'] = array(
    'title' => 'FullCalendar',
    'type' => MENU_DEFAULT_LOCAL_TASK,
    'weight' => 0,
  );
  $items['fullcalendar/ajax/update/%/%'] = 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,
  );
  $items['fullcalendar/ajax/results'] = array(
    'title' => 'Events',
    'description' => 'Get events from views with arguments',
    'page callback' => 'fullcalendar_results',
    'access arguments' => array(
      'access content',
    ),
    'delivery callback' => 'ajax_deliver',
    'theme callback' => 'ajax_base_page_theme',
    'type' => MENU_CALLBACK,
  );
  return $items;
}

/**
 * Saves the updated FullCalendar event's datetime.
 *
 * @param string $action
 *   Value can be 'drop' or 'resize'.
 * @param int $eid
 *   The id of the entity that will be updated.
 */
function fullcalendar_update($action, $eid) {
  if (empty($_POST['field']) || !isset($_POST['index'])) {
    return;
  }

  // Retrieve the post vars form the ajax call.
  $field = check_plain($_POST['field']);
  $index = check_plain($_POST['index']);
  $all_day = isset($_POST['all_day']) ? check_plain($_POST['all_day']) : '';
  $delta = ' ' . check_plain($_POST['day_delta']) . ' days ' . check_plain($_POST['minute_delta']) . ' minutes';
  $entity_type = check_plain($_POST['entity_type']);
  $entity = entity_load($entity_type, array(
    $eid,
  ));
  $entity = reset($entity);
  $langcode = field_language($entity_type, $entity, $field);
  $item =& $entity->{$field}[$langcode][$index];
  $old_start = $item['value'];
  $old_end = $item['value2'];

  // Adjust for different date formats.
  $format = date_type_format($item['date_type']);

  // Datestamp can't combine with words for strtotime, convert to ISO for now.
  if ($format == DATE_FORMAT_UNIX) {
    $old_start = date(DATE_FORMAT_ISO, $old_start);
    $old_end = date(DATE_FORMAT_ISO, $old_end);
  }

  // No break after 'drop' so it will reuse the code of 'resize'.
  switch ($action) {
    case 'drop':
      $item['value'] = date($format, strtotime($old_start . $delta));
    case 'resize':
      $item['value2'] = date($format, strtotime($old_end . $delta));
      break;
  }

  // Save the new start/end values.
  if (module_exists('entity')) {
    entity_save($entity_type, $entity);
  }
  else {
    field_attach_presave($entity_type, $entity);
    field_attach_update($entity_type, $entity);
  }
  drupal_json_output(array(
    'msg' => t('The new event time has been saved.') . ' [' . l(t('Close'), NULL, array(
      'attributes' => array(
        'class' => array(
          'fullcalendar-status-close',
        ),
      ),
    )) . ']',
    'dom_id' => $_POST['dom_id'],
  ));
}

/**
 * Implements hook_fullcalendar_classes().
 */
function fullcalendar_fullcalendar_classes($entity) {
  $classes = array(
    'fc-event-default',
    $entity->bundle,
  );

  // Add a class for the date field being used.
  if (isset($entity->fullcalendar_date_field)) {
    $classes[] = "fc-event-field-{$entity->fullcalendar_date_field}";
  }
  return $classes;
}

/**
 * Implements hook_form_FORM_ID_alter() for views_ui_edit_display_form().
 *
 * Since we force the query to be distinct, reflect that in the UI.
 */
function fullcalendar_form_views_ui_edit_display_form_alter(&$form, &$form_state, $form_id) {
  if ($form_state['view']->display_handler
    ->get_option('style_plugin') != 'fullcalendar' || empty($form['options']['query']['options']['distinct'])) {
    return;
  }
  $distinct =& $form['options']['query']['options']['distinct'];
  if (!isset($distinct['#description'])) {
    $distinct['#description'] = '';
  }
  else {
    $distinct['#description'] .= '<br>';
  }
  $distinct['#disabled'] = TRUE;
  $distinct['#description'] .= '<strong>' . t('FullCalendar requires that the query be distinct.') . '</strong>';
}

/**
 * Returns events for FullCalendar.
 */
function fullcalendar_results($view_name = NULL, $view_display = NULL) {

  // Bail out if no view_name or view_display is passed.
  if (empty($view_name) || empty($view_display) || empty($_POST['dom_id'])) {
    return;
  }

  // Find Views arguments.
  $args = func_get_args();
  unset($args[0], $args[1]);
  $args = array_values($args);
  $dom_id = check_plain($_POST['dom_id']);
  $view_name = check_plain($view_name);
  $view_display = check_plain($view_display);

  // Add all $_POST data, because AJAX is always a post and many things,
  // such as tablesorts, exposed filters and paging assume $_GET.
  $_GET += $_POST;

  // Get the view and check access.
  $view = views_get_view($view_name);
  if (!$view || !$view
    ->access($view_display)) {
    return;
  }
  if (!$view
    ->set_display($view_display)) {
    return;
  }
  $view->dom_id = $dom_id;
  $view->fullcalendar_ajax = TRUE;
  $view
    ->pre_execute($args);
  $view
    ->init_style();
  $view
    ->execute($view_display);
  $output = $view->style_plugin
    ->render();
  $view
    ->post_execute();
  return array(
    '#type' => 'ajax',
    '#commands' => array(
      array(
        'command' => 'fullcalendar_results_response',
        'data' => $output,
      ),
    ),
  );
}

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

/**
 * Implements hook_fullcalendar_editable().
 *
 * Use our access callback as the editable setting.
 */
function fullcalendar_fullcalendar_editable($entity, $view) {
  return _fullcalendar_update_access($entity);
}

/**
 * Determines if a given field is a date field.
 *
 * @param object $field
 *   A Views field handler object.
 * @param bool $include_gcal
 *   Boolean indicating whether or not to count gcal fields as a date field.
 *
 * @return bool
 *   Boolean, TRUE if the field is a date field, FALSE otherwise.
 */
function fullcalendar_field_is_date($field, $include_gcal = FALSE) {
  if ($include_gcal && $field->field == 'gcal') {
    return TRUE;
  }
  return !empty($field->definition['is date']) && isset($field->field_info);
}

/**
 * Checks FullCalendar dependencies.
 *
 * @return bool
 *   Array with TRUE/FALSE for each dependency.
 *
 * @see fullcalendar_requirements()
 */
function _fullcalendar_status() {
  $status = array();
  $status['fullcalendar_plugin'] = FALSE;
  $path = fullcalendar_get_js_path();
  if (!file_exists($path)) {
    return FALSE;
  }
  if (version_compare(fullcalendar_get_version($path), FULLCALENDAR_MIN_PLUGIN_VERSION, '>=')) {
    $status['fullcalendar_plugin'] = TRUE;
  }
  return $status;
}

/**
 * Returns the version of FullCalendar plugin that is installed.
 *
 * This can be used by other modules' hook_requirements() to ensure that the
 * proper version of FullCalendar plugin is installed.
 *
 * @see version_compare()
 */
function fullcalendar_get_version($fullcalendar_path = NULL) {
  $version =& drupal_static(__FUNCTION__);
  if (empty($version)) {
    $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 FullCalendar plugin.
 */
function fullcalendar_get_js_path() {
  $fullcalendar_file = array(
    'none' => 'fullcalendar.js',
    'min' => 'fullcalendar.min.js',
  );
  return variable_get('fullcalendar_path', fullcalendar_default_path()) . '/' . $fullcalendar_file[variable_get('fullcalendar_compression_type', 'min')];
}

/**
 * Includes all FullCalendar API plugins.
 */
function fullcalendar_include_api() {
  ctools_include('plugins');
  return ctools_plugin_api_include('fullcalendar', 'fullcalendar', fullcalendar_api_version(), fullcalendar_api_minimum_version());
}

/**
 * Implements hook_ctools_plugin_api_hook_name().
 */
function fullcalendar_ctools_plugin_api_hook_name() {
  return 'fullcalendar_api';
}

/**
 * Declares the current FullCalendar API version.
 */
function fullcalendar_api_version() {
  return '1';
}

/**
 * Declares the minimum FullCalendar API version.
 */
function fullcalendar_api_minimum_version() {
  return '1';
}

/**
 * Implements hook_fullcalendar_api().
 */
function fullcalendar_fullcalendar_api() {
  return array(
    'api' => fullcalendar_api_version(),
    'path' => drupal_get_path('module', 'fullcalendar') . '/includes',
  );
}

Functions

Namesort descending Description
fullcalendar_api_minimum_version Declares the minimum FullCalendar API version.
fullcalendar_api_version Declares the current FullCalendar API version.
fullcalendar_ctools_plugin_api_hook_name Implements hook_ctools_plugin_api_hook_name().
fullcalendar_default_path The default path to the FullCalendar plugin.
fullcalendar_field_is_date Determines if a given field is a date field.
fullcalendar_form_views_ui_edit_display_form_alter Implements hook_form_FORM_ID_alter() for views_ui_edit_display_form().
fullcalendar_fullcalendar_api Implements hook_fullcalendar_api().
fullcalendar_fullcalendar_classes Implements hook_fullcalendar_classes().
fullcalendar_fullcalendar_editable Implements hook_fullcalendar_editable().
fullcalendar_get_js_path Returns the path to the FullCalendar plugin.
fullcalendar_get_version Returns the version of FullCalendar plugin that is installed.
fullcalendar_include_api Includes all FullCalendar API plugins.
fullcalendar_library Implements hook_library().
fullcalendar_menu Implements hook_menu().
fullcalendar_permission Implements hook_permission().
fullcalendar_results Returns events for FullCalendar.
fullcalendar_theme Implements hook_theme().
fullcalendar_update Saves the updated FullCalendar event's datetime.
fullcalendar_views_api Implements hook_views_api().
_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 FullCalendar plugin.
FULLCALENDAR_RECOMMENDED_PLUGIN_VERSION The recommended version of the FullCalendar plugin.