You are here

commons_events.module in Drupal Commons 7.3

File

modules/commons/commons_events/commons_events.module
View source
<?php

/**
 * @file
 * Drupal Commons Events feature.
 */
include_once 'commons_events.features.inc';

/**
 * Implements hook_forms().
 */
function commons_events_forms($form_id, $args) {

  // The attend and cancel event forms need to have a dynamic form ID to allow
  // for multiple forms to be shown on the same page and have them work.
  foreach (array(
    'commons_events_attend_event_form',
    'commons_events_cancel_event_form',
  ) as $form) {
    if (substr($form_id, 0, strlen($form)) == $form) {
      return array(
        $form_id => array(
          'callback' => $form,
        ),
      );
    }
  }
}

/**
 * Implements hook_menu().
 */
function commons_events_menu() {
  $items['node/%node/attendees'] = array(
    'title' => 'Attendees',
    'page callback' => 'commons_events_event_attendees_page',
    'page arguments' => array(
      1,
    ),
    'access callback' => 'commons_events_attendee_access',
    'access arguments' => array(
      1,
    ),
    'type' => MENU_LOCAL_TASK,
  );
  return $items;
}

/**
 * Implements hook_menu_alter().
 */
function commons_events_menu_alter(&$items) {

  // Hide the "Register" tab since we expose an "Attend" button.
  $items['node/%/register']['access callback'] = FALSE;
}

/**
 * Access callback for showing the Attendees tab.
 */
function commons_events_attendee_access($event_node) {
  if ($event_node->type == 'event' && user_access('view event registration')) {
    return TRUE;
  }
  return FALSE;
}

/**
 * Page callback for full event attendee listing.
 */
function commons_events_event_attendees_page($event_node) {
  return theme('commons_events_event_attendees', array(
    'event_nid' => $event_node->nid,
    'display' => 'full',
  ));
}

/**
 * Implements hook_theme().
 */
function commons_events_theme($existing, $type, $theme, $path) {
  return array(
    'commons_events_attending_event' => array(
      'variables' => array(
        'event' => NULL,
        'attendee_count' => NULL,
      ),
      'file' => '/includes/commons_events.theme.inc',
    ),
    'commons_events_event_attendees' => array(
      'variables' => array(
        'display' => NULL,
        'event_nid' => NULL,
      ),
      'file' => '/includes/commons_events.theme.inc',
    ),
    'commons_events_date_display_range_advanced' => array(
      'variables' => array(
        'date1' => NULL,
        'date2' => NULL,
        'timezone' => NULL,
        'dates' => NULL,
        // HTML attributes that will be applied to both the start and end dates
        // (unless overridden).
        'attributes' => array(),
        // HTML attributes that will be applied to the start date only.
        'attributes_start' => array(),
        // HTML attributes that will be applied to the end date only.
        'attributes_end' => array(),
        'rdf_mapping' => NULL,
        'add_rdf' => NULL,
      ),
    ),
    'commons_events_date_display_range_simple' => array(
      'variables' => array(
        'date1' => NULL,
        'date2' => NULL,
        'timezone' => NULL,
        'dates' => NULL,
        // HTML attributes that will be applied to both the start and end dates
        // (unless overridden).
        'attributes' => array(),
        // HTML attributes that will be applied to the start date only.
        'attributes_start' => array(),
        // HTML attributes that will be applied to the end date only.
        'attributes_end' => array(),
        'rdf_mapping' => NULL,
        'add_rdf' => NULL,
      ),
    ),
  );
}

/**
 * Implements hook_theme_registry_alter().
 * Gives us the ability to insert our pretty date formatter for the dates we choose below.
 */
function commons_events_theme_registry_alter(&$theme_registry) {
  $theme_registry['date_display_range']['theme path'] = drupal_get_path('module', 'commons_events');
  $theme_registry['date_display_range']['function'] = 'theme_commons_events_date_display_range_advanced';
  $theme_registry['date_display_single']['theme path'] = drupal_get_path('module', 'commons_events');
  $theme_registry['date_display_single']['function'] = 'theme_commons_events_date_display_single';
}

/**
 * Implements hook_element_info_alter().
 */
function commons_events_element_info_alter(&$type) {

  // Extend date_combo processing to alter the label text on the fields.
  if (isset($type['date_combo'])) {
    $type['date_combo']['#process'][] = 'commons_events_date_combo_element_process';
  }

  // Extend date_popup processing to remove the redundant descriptions on
  // fields.
  if (isset($type['date_popup'])) {
    $type['date_popup']['#process'][] = 'commons_events_date_popup_element_process';
  }
}

/**
 * Process date_combo field output.
 *
 * This is an extension of the Date module's own processing.
 *
 * @see date_combo_element_process()
 * @see commons_events_element_info_alter()
 */
function commons_events_date_combo_element_process($element, &$form_state, $form) {
  $field = field_widget_field($element, $form_state);

  // Change the labels for from and to dates to something more meaningful.
  if (!empty($field['settings']['todate'])) {
    $element['value']['#title'] = t('Start date');
    $element['value2']['#title'] = t('End date');
  }
  return $element;
}

/**
 * Process date_popup field output.
 *
 * This is an extension of the Date module's own processing.
 *
 * @see date_popup_element_process()
 * @see commons_events_element_info_alter()
 */
function commons_events_date_popup_element_process($element, &$form_state, $form) {

  // If a date_popup field is part of a larger range widget, simplify the
  // output.
  if (!empty($element['#field']['settings']['todate'])) {
    foreach (array(
      'date',
      'time',
    ) as $type) {
      if (!empty($element[$type])) {

        // The description and title are unnecessary.
        unset($element[$type]['#description']);
        $element[$type]['#title'] = '';
      }
    }
  }
  return $element;
}

/**
 * Implements hook_entity_view_alter().
 */
function commons_events_entity_view_alter(&$build, $type) {
  if ($type == 'node' && isset($build['#node'])) {
    $node = $build['#node'];
    list(, , $bundle) = entity_extract_ids('node', $node);
    if ($bundle == 'event') {
      $build['attending'] = array(
        0 => theme('commons_events_attending_event', array(
          'event' => $node,
          'attendee_count' => commons_events_get_raw_attendee_count($node),
        )),
        '#weight' => -10,
      );

      // Don't show the address if the event location is only online.
      $items = field_get_items('node', $node, 'field_location');
      if (is_array($items)) {
        foreach ($items as $item) {
          if ($item['value'] == 'online') {
            $build['field_address']['#access'] = FALSE;
          }
        }
      }
    }
  }
}

/**
 * Implements hook_form_BASE_FORM_ID_alter().
 */
function commons_events_form_node_form_alter(&$form, &$form_state, $form_id) {
  $node = $form_state['node'];
  list(, , $bundle) = entity_extract_ids('node', $node);
  if ($bundle == 'event' && empty($node->nid)) {
    drupal_set_title(t('Create an event'));
  }
  if ($bundle == 'event') {

    // Attach custom CSS and JS to the form.
    $form['#attached']['css'][] = drupal_get_path('module', 'commons_events') . '/css/commons_events.css';
    $form['#attached']['js'][] = drupal_get_path('module', 'commons_events') . '/js/commons_events.js';

    // Remove the '-None-' option from all the dropdowns.
    unset($form['field_location'][$form['field_location']['#language']]['#options']['_none']);
    unset($form['field_registration_type'][$form['field_registration_type']['#language']]['#options']['_none']);

    // Remove the registration type description.
    unset($form['field_registration'][$form['field_registration']['#language']]['0']['registration_type']['#description']);

    // Default the event organizer to the user who is creating the node.
    if (empty($form['field_organizers'][$form['field_organizers']['#language']]['#default_value'])) {
      global $user;
      $form['field_organizers'][$form['field_organizers']['#language']]['#default_value'] = format_username($user) . " (" . $user->uid . ")";
    }

    // Hide the Registration type field.
    $form['field_registration']['#disabled'] = commons_events_event_type_disabled($node);

    // Make the number of attendees field optional.
    $form['field_number_of_attendees'][$form['field_number_of_attendees']['#language']]['0']['value']['#required'] = FALSE;

    // Address field should be hidden if the event is online only.
    $form['field_address']['#states'] = array(
      'invisible' => array(
        ':input[name^="field_location"]' => array(
          'value' => 'online',
        ),
      ),
    );

    // Assume that registration is open by default.
    $node_registration_status = 1;

    // Use saved registration settings for existing events.
    if (isset($node->nid)) {
      $settings = registration_entity_settings('node', $node->nid);
      $node_registration_status = $settings['status'];
    }

    // Add a status field to allow users to open and close registration.
    $form['field_status'] = array(
      '#type' => 'select',
      '#title' => t('Status'),
      '#options' => array(
        1 => t('Open to new attendees'),
        0 => t('Closed'),
      ),
      '#default_value' => $node_registration_status,
      '#states' => array(
        'visible' => array(
          ':input[name^="field_registration_type"]' => array(
            'value' => 'onsite',
          ),
        ),
      ),
      '#weight' => 10,
    );

    // Status and attendee limit are shown if registration is onsite.
    $form['field_registration']['#states'] = $form['field_status']['#states'];
    $form['field_number_of_attendees']['#states'] = $form['field_status']['#states'];
    $form['field_number_of_attendees']['#states']['visible'][':input[name="field_status"]'] = array(
      'value' => 1,
    );
    $form['field_number_of_attendees']['#weight'] = 11;

    // Create a custom fieldset to hold the registration settings and add it to
    // the additional settings group so it will be displayed as a vertical tab.
    $form['event_registration_settings'] = array(
      '#type' => 'fieldset',
      '#collapsible' => TRUE,
      '#collapsed' => FALSE,
      '#title' => t('Registration settings'),
      '#group' => 'additional_settings',
      '#weight' => -10,
    );

    // Copy all of the registration settings into the custom fieldset.
    $form['event_registration_settings']['field_organizers'] = $form['field_organizers'];
    $form['event_registration_settings']['field_registration_type'] = $form['field_registration_type'];
    $form['event_registration_settings']['field_status'] = $form['field_status'];
    $form['event_registration_settings']['field_number_of_attendees'] = $form['field_number_of_attendees'];
    $form['event_registration_settings']['field_registration'] = $form['field_registration'];

    // Unset all of the original registration settings.
    hide($form['field_organizers']);
    hide($form['field_registration_type']);
    hide($form['field_status']);
    hide($form['field_number_of_attendees']);
    hide($form['field_registration']);

    // Add additional submission and validation handlers.
    $form['#validate'][] = 'commons_events_node_form_validate';
    $form['#submit'][] = 'commons_events_node_form_submit';
  }
}

/**
 * Custom node form validation handler.
 */
function commons_events_node_form_validate($form, &$form_state) {
  $language = isset($form_state['language']) ? $form_state['language'] : LANGUAGE_NONE;
  switch ($form_state['values']['field_registration_type'][$language]['0']['value']) {
    case 'onsite':

      // Max attendees must be entered if the registration is open and onsite.
      if ((int) $form_state['values']['field_status'] == 1 && !is_numeric($form_state['values']['field_number_of_attendees'][$language]['0']['value'])) {
        form_set_error('field_number_of_attendees', t('Maximum number of attendees must be set for registrations to be accepted.'));
      }
      break;
  }
}

/**
 * Custom node form submit handler.
 */
function commons_events_node_form_submit($form, $form_state) {
  $node = $form_state['node'];
  $language = isset($form_state['language']) ? $form_state['language'] : LANGUAGE_NONE;
  if (isset($node->nid)) {
    $settings = registration_entity_settings('node', $node->nid);
    $settings['status'] = (int) $form_state['values']['field_status'];
    if ($form_state['values']['field_registration_type'][$language]['0']['value'] == 'external') {
      $settings['status'] = 0;
    }
    if (!empty($form_state['values']['field_number_of_attendees'][$language]['0']['value']) && is_numeric($form_state['values']['field_number_of_attendees'][$language]['0']['value'])) {
      $settings['capacity'] = (int) $form_state['values']['field_number_of_attendees'][$language]['0']['value'];
    }
    $settings['settings'] = serialize($settings['settings']);
    registration_update_entity_settings('node', $node->nid, $settings);

    // Automatically register the node creator for the event.
    $registration_type = registration_get_entity_registration_type('node', $node);
    $values = array(
      'entity_type' => 'node',
      'entity_id' => $node->nid,
      'type' => $registration_type,
    );
    $registration = entity_create('registration', $values);
    if (!isset($node->uid)) {
      global $user;
      $uid = $user->uid;
    }
    else {
      $uid = $node->uid;
    }
    if ($settings['status'] == 1 && !registration_is_registered($registration, NULL, $uid)) {
      $registration->user_uid = $uid;
      $registration->author_uid = $uid;
      $registration->state = NULL;
      $registration->count = 1;
      registration_save($registration);
    }
  }
}

/**
 * Implements hook_tokens().
 */
function commons_events_tokens($type, $tokens, array $data = array(), array $options = array()) {
  $replacements = array();
  if ($type == 'node' && !empty($data['node'])) {
    $event = $data['node'];
    foreach ($tokens as $name => $original) {
      if ($name == 'attend-form') {
        $replacements[$original] = theme('commons_events_attending_event', array(
          'event' => $event,
          'attendee_count' => commons_events_get_raw_attendee_count($event),
        ));
      }
    }
  }
  return $replacements;
}

/**
 * Implements hook_token_info_alter().
 */
function commons_events_token_info_alter(&$tokens) {
  $tokens['tokens']['node']['attend-form'] = array(
    'name' => t('Attending event form'),
    'description' => t('Displays the appropriate form for attending or cancelling event attendance'),
  );
}

/**
 * Generates the 'more' link for upcoming events in a specific group.
 */
function commons_events_upcoming_more_link($group_id) {
  if (module_exists('commons_events_solr')) {
    $options = array(
      'query' => array(
        'f[1]' => 'sm_og_group_ref:node:' . $group_id,
      ),
    );
    return l(t('more'), 'events', $options);
  }
  $options = array(
    'query' => array(
      'gid' => $group_id,
    ),
  );
  return l(t('more'), 'events', $options);
}

/**
 * Implements hook_views_post_execute().
 */
function commons_events_views_post_execute(&$view) {
  if ($view->name == 'commons_events_upcoming' && $view->current_display == 'panel_pane_1') {
    $view->attachment_after = '<div class="more-link">' . commons_events_upcoming_more_link($view->args[0]) . '</div>';
  }
  if ($view->name == 'commons_events_upcoming' && $view->current_display == 'panel_pane_2') {
    $view->attachment_before = '<div class="commons-event-count">' . format_plural(count($view->result), '1 event', '@count events') . '</div>';
  }
}

/**
 * Implements hook_block_info().
 */
function commons_events_block_info() {
  $blocks['commons_events_create_event_link'] = array(
    'info' => t('"Create an event" call to action'),
    'cache' => DRUPAL_CACHE_GLOBAL,
  );
  $blocks['commons_events_anonymous_login'] = array(
    'info' => t('"Login to create an event" call to action'),
    'cache' => DRUPAL_CACHE_GLOBAL,
  );
  return $blocks;
}

/**
 * Implements hook_block_view().
 */
function commons_events_block_view($delta = '') {
  $block = array();
  switch ($delta) {
    case 'commons_events_create_event_link':
      if (node_access('create', 'event')) {
        $block['subject'] = NULL;
        $block['content'] = array(
          '#type' => 'link',
          '#title' => t('List an event'),
          '#href' => 'node/add/event',
          '#options' => array(
            'query' => array(
              'destination' => 'events',
            ),
          ),
        );
      }
      break;
    case 'commons_events_anonymous_login':
      if (user_is_anonymous()) {
        $block['subject'] = NULL;
        $block['content'] = t('You must !login in order to list events.', array(
          '!login' => l(t('log in'), 'user'),
        ));
      }
      break;
  }
  return $block;
}

/**
 * Returns specific HTML for a date element formatted with the event formatter M j Y - g:ia.
 *
 * We the do some awesome stuff with regular expressions to take all the
 * different formats available to us and format it pretty.
 */
function theme_commons_events_date_display_range_simple($variables) {

  // Use the regular date formatter if we are any other date format than below.
  $date1 = $variables['dates']['value']['formatted_date'];
  $date2 = $variables['dates']['value2']['formatted_date'];
  $time1 = $variables['dates']['value']['formatted_time'];
  $time2 = $variables['dates']['value2']['formatted_time'];
  $timezone = $variables['timezone'];
  $attributes_start = $variables['attributes_start'];
  $attributes_end = $variables['attributes_end'];
  if ($date1 == $date2) {

    // Wrap the result with the attributes.
    return t('!start-time - !end-time', array(
      '!start-time' => '<span class="date-display-start"' . drupal_attributes($attributes_start) . '>' . $time1 . '</span>',
      '!end-time' => '<span class="date-display-end"' . drupal_attributes($attributes_end) . '>' . $time2 . $timezone . '</span>',
    ));
  }

  // Wrap the result with the attributes.
  return t('!start-date to !end-date, !start-time - !end-time', array(
    '!start-date' => '<span class="date-display-start"' . drupal_attributes($attributes_start) . '>' . $date1 . '</span>',
    '!end-date' => '<span class="date-display-end"' . drupal_attributes($attributes_end) . '>' . $date2 . '</span>',
    '!start-time' => '<span class="date-display-start"' . drupal_attributes($attributes_start) . '>' . $time1 . '</span>',
    '!end-time' => '<span class="date-display-end"' . drupal_attributes($attributes_end) . '>' . $time2 . $timezone . '</span>',
  ));
}
function theme_commons_events_date_display_single($variables) {
  return $variables['date'];
}

/**
 * Returns specific HTML for a date element formatted with the event formatter.
 *
 * We parse the date segments and return it at the bottom, or use the simple
 * formatter (above) if parts of the date are too complicated.
 */
function theme_commons_events_date_display_range_advanced($variables) {
  $timezone = $variables['timezone'];
  $attributes_start = $variables['attributes_start'];
  $attributes_end = $variables['attributes_end'];

  // Time is always formatted the same.
  $time1 = $variables['dates']['value']['formatted_time'];
  $time2 = $variables['dates']['value2']['formatted_time'];

  // See date.theme line 168 -- regarding formatting time for same date events.
  if ($variables['dates']['value']['formatted_date'] === $variables['dates']['value2']['formatted_date']) {
    return theme('commons_events_date_display_range_simple', $variables);
  }

  // If the difference is over a year, use the simple formatter.
  if ($variables['dates']['value']['db']['object']
    ->difference($variables['dates']['value2']['db']['object'], 'years') > 0) {
    return theme('commons_events_date_display_range_simple', $variables);
  }
  else {
    if ($variables['dates']['value']['db']['object']
      ->difference($variables['dates']['value2']['db']['object'], 'months') > 0) {
      $variables['dates']['value']['db']['object']
        ->limitGranularity(array(
        'day',
        'month',
      ));
      $variables['dates']['value2']['db']['object']
        ->limitGranularity(array(
        'day',
        'month',
        'year',
      ));
      $date1 = $variables['dates']['value']['db']['object']
        ->format($variables['dates']['format'], FALSE);
      $date2 = $variables['dates']['value2']['db']['object']
        ->format($variables['dates']['format'], FALSE);
    }
    else {
      if ($variables['dates']['value']['db']['object']
        ->difference($variables['dates']['value2']['db']['object'], 'days') > 0) {

        // If the Day is before the month (Europe, etc) then switch the granularity.
        if (strpos($variables['dates']['format'], 'M') != 0 || strpos($variables['dates']['format'], 'm') != 0 || strpos($variables['dates']['format'], 'F') != 0 || strpos($variables['dates']['format'], 'n') != 0) {
          $variables['dates']['value']['db']['object']
            ->limitGranularity(array(
            'day',
          ));
          $variables['dates']['value2']['db']['object']
            ->limitGranularity(array(
            'day',
            'month',
            'year',
          ));
        }
        else {
          $variables['dates']['value']['db']['object']
            ->limitGranularity(array(
            'month',
            'day',
          ));
          $variables['dates']['value2']['db']['object']
            ->limitGranularity(array(
            'day',
            'year',
          ));
        }
        $date1 = $variables['dates']['value']['db']['object']
          ->format($variables['dates']['format'], FALSE);
        $date2 = $variables['dates']['value2']['db']['object']
          ->format($variables['dates']['format'], FALSE);

        // If we are match 'D' or 'l', then we have a 'day' in there (eg Monday),
        // we want the whole date because it looks funky otherwise.
        if (strpbrk($variables['dates']['format'], 'Dl')) {
          return theme('commons_events_date_display_range_simple', $variables);
        }
      }
      else {
        return theme('commons_events_date_display_range_simple', $variables);
      }
    }
  }

  // Wrap the result with the attributes.
  return t('!start-date to !end-date, !start-time - !end-time', array(
    '!start-date' => '<span class="date-display-start"' . drupal_attributes($attributes_start) . '>' . $date1 . '</span>',
    '!end-date' => '<span class="date-display-end"' . drupal_attributes($attributes_end) . '>' . $date2 . '</span>',
    '!start-time' => '<span class="date-display-start"' . drupal_attributes($attributes_start) . '>' . $time1 . '</span>',
    '!end-time' => '<span class="date-display-end"' . drupal_attributes($attributes_end) . '>' . $time2 . $timezone . '</span>',
  ));
}

/**
 * Implements hook_commons_notify_message_selection_alter().
 */
function commons_events_commons_notify_message_selection_alter(&$message_type, $hook, $node) {

  // Provide a special message type that describes the event details
  // when a new event node is created.
  if ($hook == 'node_insert' && $node->type == 'event') {
    $message_type = 'commons_events_event_node_created';
  }
}

/**
 * Check whether to allow switching the event's registration type.
 * If there are registrations for the event, disallow changing the type. This
 * keeps registrations for an event consistent.
 */
function commons_events_event_type_disabled($node) {
  if (!isset($node->nid) || empty($node->nid)) {
    return FALSE;
  }
  $registrations = db_select('registration', 'r')
    ->fields('r')
    ->condition('entity_type', 'node')
    ->condition('entity_id', $node->nid)
    ->countQuery()
    ->execute()
    ->fetchCol();
  if ($registrations[0] > 0) {
    return TRUE;
  }
  return FALSE;
}

/**
 * Helper function go get the raw number of attendees.
 */
function commons_events_get_raw_attendee_count($node) {
  $attendee_count = 0;
  $query = db_select('registration', 'r')
    ->fields('r', array(
    'count',
  ))
    ->condition('entity_id', $node->nid)
    ->condition('entity_type', 'node')
    ->execute();

  // Add up all of the attendees going to the event.
  foreach ($query as $record) {
    $attendee_count += $record->count;
  }
  return $attendee_count;
}

Functions

Namesort descending Description
commons_events_attendee_access Access callback for showing the Attendees tab.
commons_events_block_info Implements hook_block_info().
commons_events_block_view Implements hook_block_view().
commons_events_commons_notify_message_selection_alter Implements hook_commons_notify_message_selection_alter().
commons_events_date_combo_element_process Process date_combo field output.
commons_events_date_popup_element_process Process date_popup field output.
commons_events_element_info_alter Implements hook_element_info_alter().
commons_events_entity_view_alter Implements hook_entity_view_alter().
commons_events_event_attendees_page Page callback for full event attendee listing.
commons_events_event_type_disabled Check whether to allow switching the event's registration type. If there are registrations for the event, disallow changing the type. This keeps registrations for an event consistent.
commons_events_forms Implements hook_forms().
commons_events_form_node_form_alter Implements hook_form_BASE_FORM_ID_alter().
commons_events_get_raw_attendee_count Helper function go get the raw number of attendees.
commons_events_menu Implements hook_menu().
commons_events_menu_alter Implements hook_menu_alter().
commons_events_node_form_submit Custom node form submit handler.
commons_events_node_form_validate Custom node form validation handler.
commons_events_theme Implements hook_theme().
commons_events_theme_registry_alter Implements hook_theme_registry_alter(). Gives us the ability to insert our pretty date formatter for the dates we choose below.
commons_events_tokens Implements hook_tokens().
commons_events_token_info_alter Implements hook_token_info_alter().
commons_events_upcoming_more_link Generates the 'more' link for upcoming events in a specific group.
commons_events_views_post_execute Implements hook_views_post_execute().
theme_commons_events_date_display_range_advanced Returns specific HTML for a date element formatted with the event formatter.
theme_commons_events_date_display_range_simple Returns specific HTML for a date element formatted with the event formatter M j Y - g:ia.
theme_commons_events_date_display_single