You are here

agenda.module in Agenda 6.2

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

File

agenda.module
View source
<?php

/**
 * Implementation of hook_help().
 */
function agenda_help($path, $arg) {
  $output = '';

  //declare your output variable
  switch ($path) {
    case 'admin/help#agenda':
      $output = '<p>' . t('Displays an agenda of events from a google calendar') . '</p>';
      break;
    case 'admin/settings/agenda/debug':
      $output = '<p>' . t('The agenda block will not show when there are no applicable events to display. If the block is not showing, and you think it should, this page may help you debug the situation.') . '</p>';
      break;
    case 'admin/build/block/agenda':
      $output = '<p>' . t('Allows the creation of an agenda block.') . '</p>';
      break;
  }
  return $output;
}

/**
 * Implementation of hook_menu().
 */
function agenda_menu() {
  $items = array();
  $items['admin/settings/agenda'] = array(
    'title' => 'Agenda',
    'description' => 'Create new and manage existing agenda calendar blocks',
    'page callback' => 'agenda_admin',
    'page arguments' => array(
      'agenda_admin',
    ),
    'access arguments' => array(
      'configure agenda blocks',
    ),
    'file' => 'agenda.admin.php',
  );
  $items['admin/settings/agenda/list'] = array(
    'title' => 'List',
    'type' => MENU_DEFAULT_LOCAL_TASK,
  );
  $items['admin/settings/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/settings/agenda/0/configure'] = array(
    'title' => 'Add new block',
    'type' => MENU_LOCAL_TASK,
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'agenda_admin_configure',
      3,
    ),
    'access arguments' => array(
      'configure agenda blocks',
    ),
    'file' => 'agenda.admin.php',
  );
  $items['admin/settings/agenda/%/configure'] = array(
    'title' => 'Configure agenda block settings',
    'type' => MENU_CALLBACK,
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'agenda_admin_configure',
      3,
    ),
    'access arguments' => array(
      'configure agenda blocks',
    ),
    'file' => 'agenda.admin.php',
    'weight' => -4,
  );
  $items['admin/settings/agenda/%/delete'] = array(
    'title' => 'Delete agenda block',
    'type' => MENU_CALLBACK,
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'agenda_admin_delete',
      3,
    ),
    'access arguments' => array(
      'configure agenda blocks',
    ),
    'file' => 'agenda.admin.php',
    'weight' => -3,
  );
  $items['admin/settings/agenda/%/debug'] = array(
    'title' => 'Debug agenda block',
    'type' => MENU_CALLBACK,
    'page callback' => 'agenda_debug',
    'page arguments' => array(
      3,
    ),
    'access arguments' => array(
      'configure agenda blocks',
    ),
    'file' => 'agenda.admin.php',
    'weight' => -2,
  );
  $items['admin/settings/agenda/%/clear'] = array(
    'title' => 'Clear agenda block cache',
    'type' => MENU_CALLBACK,
    'page callback' => 'agenda_clear',
    'page arguments' => array(
      3,
    ),
    'access arguments' => array(
      'configure agenda blocks',
    ),
    'file' => 'agenda.admin.php',
    'weight' => -1,
  );
  return $items;
}

/**
 * Implementation of hook_perm().
 */
function agenda_perm() {
  return array(
    'access agenda content',
    'configure agenda blocks',
  );
}

/**
 * Implementation of hook_init().
 *
 * We have to add the CSS to every page otherwise our CSS gets
 * left out with block caching.
 */
function agenda_init() {
  $basepath = drupal_get_path('module', 'agenda');
  drupal_add_css($basepath . '/agenda.css');
  drupal_add_js($basepath . '/agenda.js');
}

/**
 * Implementation of hook_block().
 */
function agenda_block($op = 'list', $delta = 0) {
  switch ($op) {
    case 'list':
      $res = db_query("SELECT bid, value FROM {agenda} WHERE name = 'title'");
      $blocks = array();
      while ($block = db_fetch_object($res)) {
        $blocks[$block->bid] = array(
          'info' => t('Agenda: @title', array(
            '@title' => agenda_variable_get($block->bid, 'title', 'New block'),
          )),
          'cache' => BLOCK_CACHE_GLOBAL,
        );
      }
      return $blocks;
      break;
    case 'view':
      if (!user_access('access agenda content')) {
        return FALSE;
      }
      $block = array(
        'subject' => t('Upcoming'),
        'content' => agenda_display_block($delta),
      );
      return $block;
      break;
  }
}

/**
 * Implementation of hook_theme().
 */
function agenda_theme($existing, $type, $theme, $path) {
  $theme = array(
    'agenda_block' => array(
      'arguments' => array(
        'events' => array(),
        'block' => new stdClass(),
      ),
      'template' => 'agenda-block',
    ),
    'agenda_admin' => array(
      'arguments' => 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;
  }

  // Group the events by date
  $events = agenda_get_events($block);
  $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++;
  }

  // Render
  if (count($events)) {
    $output = theme('agenda_block', $events, $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=%d', $delta);
  $settings = new stdClass();
  $settings->bid = $delta;
  while ($row = db_fetch_array($res)) {
    $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_result(db_query("SELECT value FROM {agenda} WHERE bid=%d AND name='%s'", $delta, $parameter));
  if ($value === FALSE) {
    $value = $default_value;
  }
  return $value;
}

/**
 * Set a variable
 *
 * @param int $delta The ID of the agenda
 * @param string $parameter The parameter required
 * @param string $value The value
 */
function agenda_variable_set($delta, $parameter, $value) {
  $bid = db_result(db_query("SELECT bid FROM {agenda} WHERE bid=%d AND name='%s'", $delta, $parameter));
  if (!$bid) {
    $query = "INSERT INTO {agenda} (value,bid,name) VALUES ('%s', %d, '%s')";
  }
  else {
    $query = "UPDATE {agenda} SET value='%s' WHERE bid=%d AND name='%s'";
  }
  return db_query($query, $value, $delta, $parameter);
}

/**
 * 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->expire > 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 = time() + $block->cachetime;
    cache_set($cache_key, $eventdata, 'cache', $expires);
  }
  return $eventdata;
}

/**
 * Read useful event information from XML chunk
 *
 * @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(isset($block->timezone) ? $block->timezone : variable_get('date_default_timezone_name', date_default_timezone_get()));

  // 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.', '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 Implementation of hook_block().
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_help Implementation of hook_help().
agenda_init Implementation of hook_init().
agenda_menu Implementation of hook_menu().
agenda_perm Implementation of hook_perm().
agenda_settings Retrieve the settings for a block
agenda_theme Implementation of 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.