You are here

agenda.module in Agenda 7.2

Same filename and directory in other branches
  1. 6.2 agenda.module
  2. 6 agenda.module
  3. 7 agenda.module

File

agenda.module
View source
<?php

/**
 * The default path to the Colorbox directory.
 */
define('AGENDA_MIN_API_VERSION', '1.1.2');

/**
 * Implements hook_menu().
 */
function agenda_menu() {
  $items = array();
  $items['admin/config/services/agenda'] = array(
    'title' => 'Agenda',
    'type' => MENU_NORMAL_ITEM,
    'description' => 'Create and configure agenda blocks which utilise Google Calendar\'s web service.',
    'page callback' => 'agenda_admin',
    'page arguments' => array(
      'agenda_admin',
    ),
    'access arguments' => array(
      'configure agenda blocks',
    ),
    'file' => 'agenda.admin.php',
  );
  $items['admin/config/services/agenda/list'] = array(
    'title' => 'List',
    'type' => MENU_DEFAULT_LOCAL_TASK,
  );
  $items['admin/config/services/agenda/googleapi'] = array(
    'title' => 'Settings',
    'description' => 'Set Google API Key',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'agenda_admin_googleapi',
    ),
    'access arguments' => array(
      'configure agenda blocks',
    ),
    'type' => MENU_LOCAL_TASK,
    'file' => 'agenda.admin.php',
  );
  $items['admin/config/services/agenda/0/configure'] = array(
    'title' => 'Add new block',
    'type' => MENU_LOCAL_ACTION,
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'agenda_admin_configure',
      4,
    ),
    'access arguments' => array(
      'configure agenda blocks',
    ),
    'file' => 'agenda.admin.php',
  );
  $items['admin/config/services/agenda/%/configure'] = array(
    'title' => 'Configure agenda block settings',
    'type' => MENU_LOCAL_ACTION,
    'context' => MENU_CONTEXT_INLINE,
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'agenda_admin_configure',
      4,
    ),
    'access arguments' => array(
      'configure agenda blocks',
    ),
    'file' => 'agenda.admin.php',
    'weight' => -4,
  );
  $items['admin/config/services/agenda/%/delete'] = array(
    'title' => 'Delete agenda block',
    'type' => MENU_CALLBACK,
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'agenda_admin_delete',
      4,
    ),
    'access arguments' => array(
      'configure agenda blocks',
    ),
    'file' => 'agenda.admin.php',
    'weight' => -3,
  );
  $items['admin/config/services/agenda/%/debug'] = array(
    'title' => 'Debug agenda block',
    'type' => MENU_LOCAL_ACTION,
    'context' => MENU_CONTEXT_INLINE,
    'page callback' => 'agenda_debug',
    'page arguments' => array(
      4,
    ),
    'access arguments' => array(
      'configure agenda blocks',
    ),
    'file' => 'agenda.admin.php',
    'weight' => -2,
  );
  $items['admin/config/services/agenda/%/clear'] = array(
    'title' => 'Clear agenda block cache',
    'type' => MENU_LOCAL_ACTION,
    'context' => MENU_CONTEXT_INLINE,
    'page callback' => 'agenda_clear',
    'page arguments' => array(
      4,
    ),
    'access arguments' => array(
      'configure agenda blocks',
    ),
    'file' => 'agenda.admin.php',
    'weight' => -1,
  );
  return $items;
}

/**
 * Implements hook_permission().
 */
function agenda_permission() {
  return array(
    'access agenda content' => array(
      'title' => t('access agenda content'),
      'description' => t('View blocks created by Agenda module'),
    ),
    'configure agenda blocks' => array(
      'title' => t('configure agenda blocks'),
      'description' => t('Create and edit Agenda blocks'),
    ),
  );
}

/**
 * Implements hook_libraries_info().
 */
function agenda_libraries_info() {
  $libraries['google-api-php-client'] = array(
    'name' => 'Google API Client Library for PHP',
    'vendor url' => 'https://github.com/google/google-api-php-client',
    'download url' => 'https://github.com/google/google-api-php-client/archive/1.1.2.zip',
    'version arguments' => array(
      'file' => 'src/Google/Client.php',
      'pattern' => '/LIBVER = "([0-9\\.a-z]+)/',
      'lines' => 29,
    ),
    'versions' => array(
      AGENDA_MIN_API_VERSION => array(),
    ),
    'files' => array(
      'php' => array(
        'src/Google/Client.php',
      ),
    ),
  );
  return $libraries;
}

/**
 * Implements hook_block_info().
 */
function agenda_block_info() {
  $res = db_query("SELECT bid, value FROM {agenda} WHERE name = :name", array(
    ':name' => 'title',
  ));
  $blocks = array();
  while ($block = $res
    ->fetchObject()) {
    $blocks[$block->bid] = array(
      'info' => t('Agenda: @title', array(
        '@title' => agenda_variable_get($block->bid, 'title', 'New block'),
      )),
      'cache' => DRUPAL_CACHE_GLOBAL,
    );
  }
  return $blocks;
}

/**
 * Implements hook_block_view().
 */
function agenda_block_view($delta) {
  if (!user_access('access agenda content')) {
    return FALSE;
  }

  // Load the block and do a sanity check
  $markup = agenda_display_block($delta);
  if (!$markup) {
    return FALSE;
  }

  // Render it
  $basepath = drupal_get_path('module', 'agenda');
  return array(
    'subject' => t('Agenda'),
    'content' => array(
      '#contextual_links' => array(
        'agenda' => array(
          'admin/config/services/agenda',
          array(
            $delta,
          ),
        ),
      ),
      '#markup' => $markup,
      '#attached' => array(
        'css' => array(
          $basepath . '/agenda.css',
        ),
        'js' => array(
          $basepath . '/agenda.js',
        ),
      ),
    ),
  );
}

/**
 * Implements hook_theme().
 */
function agenda_theme($existing, $type, $theme, $path) {
  $theme = array(
    'agenda_block' => array(
      'variables' => array(
        'events' => array(),
        'block' => new stdClass(),
      ),
      'template' => 'agenda-block',
    ),
    'agenda_admin' => array(
      'variables' => array(
        'table' => '',
      ),
      'template' => 'agenda-admin',
    ),
  );
  return $theme;
}

/**
 * Generate the themed agenda block.
 *
 * @return string
 */
function agenda_display_block($delta = 0) {

  // Check block exists.
  if ($delta === 0 || !($block = agenda_settings($delta))) {
    return false;
  }

  // Get the events for the block.
  $events = agenda_get_events($block);

  // Group the events by date.
  $events = agenda_group_events_for_block($block, $events);

  // Render.
  if (count($events)) {
    $output = theme('agenda_block', array(
      'events' => $events,
      'block' => $block,
    ));
  }
  elseif (empty($block->noeventstext)) {
    $output = NULL;
  }
  else {
    $output = filter_xss($block->noeventstext);
  }
  return $output;
}

/**
 * Retrieve the settings for a block
 *
 * @param int $delta The ID of the agenda
 * @param string $parameter The parameter required
 */
function agenda_settings($delta) {
  $res = db_query('SELECT name, value FROM {agenda} WHERE bid = :bid', array(
    ':bid' => $delta,
  ));
  $settings = new stdClass();
  $settings->bid = $delta;
  while ($row = $res
    ->fetchAssoc()) {
    $settings->{$row['name']} = $row['value'];
  }
  if (!count($settings) || !isset($settings->title)) {
    $settings = FALSE;
  }
  return $settings;
}

/**
 * Get a variable
 *
 * @param int $delta The ID of the agenda
 * @param string $parameter The parameter required
 * @param string $default_value (optional) The default value
 */
function agenda_variable_get($delta, $parameter, $default_value) {
  $value = db_query("SELECT value FROM {agenda} WHERE bid = :bid AND name = :name", array(
    ':bid' => $delta,
    ':name' => $parameter,
  ))
    ->fetchField();
  if ($value === FALSE) {
    $value = $default_value;
  }
  return $value;
}

/**
 * Set a variable
 *
 * @param int $bid The ID of the agenda block
 * @param string $name The parameter required
 * @param string $value The value
 */
function agenda_variable_set($bid, $name, $value) {
  return db_merge('agenda')
    ->key(array(
    'bid' => $bid,
    'name' => $name,
  ))
    ->fields(array(
    'value' => $value,
  ))
    ->execute();
}

/**
 * Given a list of calendar IDs, parse out and return any event data
 *
 * @param array $block The block settings object
 * @param bool $cacheaction Whether to interact with the cache
 * @access private
 */
function agenda_get_events($block, $cache = TRUE) {

  // Check the cache
  if ($cache) {
    $cache_key = 'agenda_block_' . $block->bid;
    $eventdata = cache_get($cache_key, 'cache');
    if ($eventdata && $eventdata->expire > REQUEST_TIME) {
      return $eventdata->data;
    }
  }

  // Get the calendars
  $calendars = preg_split('@\\r\\n?|\\n@', $block->calendars);
  $calendars = array_map('trim', $calendars);
  if (empty($calendars)) {
    return FALSE;
  }

  // Otherwise parse the calendars
  $eventdata = array();
  foreach ($calendars as $calindex => $googleid) {
    list($address, $token) = _agenda_parse_googleid($googleid);
    $calendar = _agenda_load_google($address, $token, $block);

    // If we fail to load the XML, handle it
    if (!$calendar) {
      watchdog('agenda', 'Unable to load the calendar feed (@feed) for block @bid', array(
        '@feed' => $googleid,
        '@bid' => $block->bid,
      ));
      continue;
    }

    // Parse out the event details
    foreach ($calendar
      ->getItems() as $google_event) {
      $event = _agenda_parse_event($google_event, $block);
      if (!$event) {
        continue;
      }
      $event['index'] = (int) $calindex;
      $event['calendar'] = (string) $calendar->summary;
      $eventdata[] = $event;
    }
  }

  // Sort the events by date
  $timestamps = array();
  foreach ($eventdata as $event) {
    $timestamps[] = $event['start timestamp'];
  }
  array_multisort($timestamps, SORT_NUMERIC, $eventdata);

  // Cache our data
  if ($cache) {
    $expires = REQUEST_TIME + $block->cachetime;
    cache_set($cache_key, $eventdata, 'cache', $expires);
  }
  return $eventdata;
}

/**
 * Read useful event information from XML chunk
 *
 * TODO: Update this docblock.
 *
 * @param SimpleXMLElement $xml An event node from a gData feed
 * @param object $block The settings object
 * @return array Associative array of information about the event
 * @access private
 */
function _agenda_parse_event($google_response, $block) {

  // Timezone
  $tz = new DateTimeZone($block->timezone);

  // Parse the timestamps
  $updated = new DateTime($google_response->updated, $tz);

  //check if event is all day by looking from dateTime
  $start = new DateTime((string) $google_response->start->dateTime ? $google_response->start->dateTime : $google_response->start->date, $tz);

  //need to minus 1 day from end date for all day events because end time is exclusive - https://developers.google.com/google-apps/calendar/v3/reference/events
  $end = new DateTime((string) $google_response->end->dateTime ? $google_response->end->dateTime : date('Y-m-d', strtotime($google_response->end->date . " -1 day")), $tz);
  $event = array();
  $event['title'] = htmlspecialchars((string) $google_response->summary);
  $event['where'] = htmlspecialchars((string) $google_response->location);
  $event['description'] = _filter_autop(filter_xss((string) $google_response->description));
  $event['timezone'] = $block->timezone;
  $event['start original'] = (string) $google_response->start->dateTime;
  $event['start date'] = format_date(strtotime($start
    ->format('c')), 'custom', $block->dateformat, $block->timezone);
  $event['start time'] = format_date(strtotime($start
    ->format('c')), 'custom', $block->timeformat, $block->timezone);
  $event['start timestamp'] = strtotime($start
    ->format('c'));

  // Use strtotime instead of getTimestamp for < PHP5.3
  $event['end original'] = (string) $google_response->end->dateTime;
  $event['end date'] = format_date(strtotime($end
    ->format('c')), 'custom', $block->dateformat, $block->timezone);
  $event['end time'] = format_date(strtotime($end
    ->format('c')), 'custom', $block->timeformat, $block->timezone);
  $event['end timestamp'] = strtotime($end
    ->format('c'));

  // Published date and time are not available in v3.
  $event['updated'] = format_date(strtotime($updated
    ->format('c')), 'custom', $block->dateformat, $block->timezone);
  $event['url'] = (string) $google_response->htmlLink;
  $event['link'] = l($block->linktext, $event['url'] . '&ctz=' . $block->timezone);

  // The day the event occurs on (without time) used for grouping
  $event['when'] = $start
    ->format('Y-m-d');

  //new fields added for google api v3.
  $event['event id'] = $google_response->id;
  $event['hangout url'] = $google_response->hangoutLink;
  $event['hangout link'] = !empty($block->hangoutlinktext) ? l($block->hangoutlinktext, $event['hangout url']) : NULL;
  $event['iCalUID'] = $google_response->iCalUID;
  $event['recurringEventId'] = $google_response->recurringEventId;
  $event['creator email'] = $google_response->creator->email;
  $event['creator displayName'] = $google_response->creator->displayName;
  $event['organizer email'] = $google_response->organizer->email;
  $event['organizer displayName'] = $google_response->organizer->displayName;
  if ($google_response->start->date) {
    $event['start time'] = '';
  }
  if ($google_response->end->date) {
    $event['end time'] = '';
  }
  if ($google_response->start->date && $google_response->end->date) {
    $event['allday'] = true;
  }
  return $event;
}

/**
 * Because we use keys for our public configuration, we want to
 * keep these keys in english, but display them translated. This function
 * provides a wrapper for this.
 */
function _agenda_translate($field) {
  $t['title'] = t('Title');
  $t['where'] = t('Where');
  $t['description'] = t('Description');
  $t['start original'] = t('Start original');
  $t['start timestamp'] = t('Start timestamp');
  $t['start date'] = t('Start date');
  $t['start time'] = t('Start time');
  $t['end original'] = t('End original');
  $t['end timestamp'] = t('End timestamp');
  $t['end date'] = t('End date');
  $t['end time'] = t('End time');

  //$t['published']           = t('Published');
  $t['updated'] = t('Updated');
  $t['url'] = t('URL');
  $t['link'] = t('Link');
  $t['when'] = t('When');
  $t['calendar'] = t('Calendar');
  $t['timezone'] = t('Timezone');
  $t['event id'] = t('Event ID');
  $t['hangout url'] = t('Hangout URL');
  $t['hangout link'] = t('Hangout link');
  $t['iCalUID'] = t('iCal UID');
  $t['recurringEventId'] = t('Recurring Event ID');
  $t['creator email'] = t('Creator Email');
  $t['creator displayName'] = t('Creator Display Name');
  $t['organizer email'] = t('Organizer Email');
  $t['organizer displayName'] = t('Organizer Display Name');
  return $t[$field];
}

/**
 * Fetch the gData feed and parse the XML
 *
 * TODO: Update this docblock.
 *
 * @param string $googleid The calendars Google ID
 * @param object $block The agenda block settings
 * @return object An object containing the status, request and result
 * @access private
 */
function _agenda_load_google($address, $key, $block) {
  $calendar_id = $address;
  $googlekey = !empty($block->googleapi_override) ? $block->googleapi_override : variable_get('agenda_googleapi', '');

  //Including the google-api-php-client library (required).
  $autoload_path = libraries_get_path('google-api-php-client') . '/autoload.php';
  if (!file_exists($autoload_path)) {
    drupal_set_message('Agenda: The google-api-php-client library was not found.
      See admin/reports/status and/or the Agenda module\'s README for more
      information.', 'error');
    return;
  }
  include_once $autoload_path;

  // https://developers.google.com/google-apps/calendar/v3/reference
  $client = new Google_Client();
  $client
    ->setApplicationName('agenda_events_feed');
  $client
    ->setDeveloperKey($googlekey);
  $service = new Google_Service_Calendar($client);
  $optParams = array(
    'orderBy' => 'startTime',
    'singleEvents' => 'true',
    'timeMin' => date('Y-m-d', strtotime($block->start)) . 'T00:00:00' . date('P'),
    'timeMax' => date('Y-m-d', strtotime($block->end)) . 'T00:00:00' . date('P'),
    'maxResults' => $block->maxevents,
    'timeZone' => $block->timezone,
  );
  try {
    $events = $service->events
      ->listEvents($calendar_id, $optParams);
  } catch (Exception $e) {
    drupal_set_message(t('Agenda: Bad call to list events. Check Google API Key or Calendar Address.'), 'error');
    drupal_set_message('<pre>' . $e . '</pre>', 'error');
    return;
  }
  return $events;
}

/**
 * Parse a Google ID into the email address and token components
 *
 * e.g. p54126mdgs4r1jeqrjcrp4big4@group.calendar.google.com/private-181818aa271006f5de8919ac93fc04f7
 */
function _agenda_parse_googleid($googleid) {
  $parts = explode('/', $googleid);
  if (!valid_email_address($parts[0])) {
    return FALSE;
  }
  $token = 'public';
  if (count($parts) === 2) {
    $token = $parts[1];
  }
  return array(
    $parts[0],
    $token,
  );
}

/**
 * Group events by date for a block. Is not a part of agenda_get_events() to
 * allow passing self-constructed events during testing.
 * @see agenda_get_events()
 * @see AgendaBlockTestCase::testNicknamedDays()
 *
 * @param array $block
 *   Block for which events are going to be groupped.
 * @param array $events
 *   An array of events to be groupped.
 * @return array
 *   An array of groupped events.
 */
function agenda_group_events_for_block($block, array $events) {
  $groupedevents = array();
  foreach ($events as $event) {
    $groupedevents[$event['when']][] = $event;
  }
  ksort($groupedevents);

  // Filter the events based on their date.
  $datelimit = $block->datelimit;
  $count = 0;
  $events = array();
  foreach ($groupedevents as $date => $eventdata) {
    if ($count >= $datelimit) {
      break;
    }
    $events[$date] = $eventdata;
    $count++;
  }
  return $events;
}

Functions

Namesort descending Description
agenda_block_info Implements hook_block_info().
agenda_block_view Implements hook_block_view().
agenda_display_block Generate the themed agenda block.
agenda_get_events Given a list of calendar IDs, parse out and return any event data
agenda_group_events_for_block Group events by date for a block. Is not a part of agenda_get_events() to allow passing self-constructed events during testing.
agenda_libraries_info Implements hook_libraries_info().
agenda_menu Implements hook_menu().
agenda_permission Implements hook_permission().
agenda_settings Retrieve the settings for a block
agenda_theme Implements hook_theme().
agenda_variable_get Get a variable
agenda_variable_set Set a variable
_agenda_load_google Fetch the gData feed and parse the XML
_agenda_parse_event Read useful event information from XML chunk
_agenda_parse_googleid Parse a Google ID into the email address and token components
_agenda_translate Because we use keys for our public configuration, we want to keep these keys in english, but display them translated. This function provides a wrapper for this.

Constants

Namesort descending Description
AGENDA_MIN_API_VERSION The default path to the Colorbox directory.