<?php
// $Id: event_views.module,v 1.26 2009/06/28 11:16:06 killes Exp $
/**
 * @file
 * Views-enables the event module.
 */

/**
 * Implementation of hook_help().
 */
function event_views_help($section) {
  switch ($section) {
    case 'admin/help#event_views':
      $output  = t('<p>The Event Views module makes the event fields available to the Views module, and creates default event views that filter dates using a drop-down date selector, the jscalendar selector, or by arguments in the url. Set up a default event view by going to %link and select <strong>add</strong>. Save the default views as-is or make any changes you like.</p>', array('%link' => l('admin/views', 'admin/views')));
      $output .= t('<p>You can change the format of the output. The default views display a list of events, but you can change it to a table or a teaser list in the <strong>Page</strong> settings. You can also add or remove fields from the view.</p>');
      $output .= t('<p>You can filter events in several ways. Use filters to pre-select a date range, expose those filters to allow the viewer to select a date range, or eliminate the filters and give the view year, month, and day arguments to filter the events by the url (i.e. YYYY/MM/DD).</p>');
      return $output;
  }
}

/**
 * Implementation of hook_views_api().
 */
function event_views_views_api() {
  return array(
    'api' => 2,
  );
}

/**
 *  Implementation of hook_views_tables().
 *  Expose event fields and filters to the views module.
 */
function event_views_views_tables() {
  // Create an option list of event content types.
  $event_node_types = array_merge(event_get_types('all'), event_get_types('solo'));
  $type_options = array();
  foreach ($event_node_types as $type) {
    $type_options[$type] = node_get_types('name', $type);
  }

  // Use this to default to current time.
  $current = array('***CURRENT_DATETIME***' => t('now'));

  // Limit years to years that have events.
  $range_prev = variable_get('event_range_prev', array('year' => date('%Y')));
  $range_next = variable_get('event_range_next', array('year' => date('%Y')));
  $years   = $current + drupal_map_assoc(range(intval($range_prev['year']), intval($range_next['year'])));
  $months  = ($current + drupal_map_assoc(range(1, 12), 'map_month'));
  $days    = ($current + drupal_map_assoc(range(1, 31)));

  $tables['event'] = array(
    'name' => 'event',
    'join' => array(
      'left' => array(
        'table' => 'node',
        'field' => 'nid'
        ),
      'right' => array(
        'field' => 'nid'
        ),
      ),
    'fields' => array(
      'event_start' => array(
        'name' => t('Event: Start Time'),
        'sortable' => true,
        'handler' => event_views_handler_field_dates_iso(),
        'option' => 'string',
        'addlfields' => array('event_start', 'event_end', 'timezone'),
        ),
      'event_end' => array(
        'name' => t('Event: End Time'),
        'sortable' => true,
        'handler' => event_views_handler_field_dates_iso(),
        'option' => 'string',
        'addlfields' => array('event_start', 'event_end', 'timezone'),
        ),
      'timezone' => array(
        'name' => t('Event: Timezone'),
        'handler' => 'event_views_timezone_display_handler',
        'query_handler' => 'event_views_query_handler',
        'sortable' => true,
        ),
      ),
    'sorts' => array(
      'event_start' => array('name' => t('Event: Start Time')),
      'event_end' => array('name' => t('Event: End Time')),
      ),
    'filters' => array(
      'event_start' => array(
        'field' => 'event_start',
        'name' => t('Event: Start Date'),
        'operator' => 'views_handler_operator_gtlt',
        'value' => views_handler_filter_date_value_form(),
        'option' => 'string',
        'handler' => 'event_views_handler_filter_datetime',
        'help' => t('This filter allows events to be filtered by their start date. Enter dates in the format: YYYY-MM-DD HH:MM:SS. Enter \'now\' to use the current time. You may enter a delta (in seconds) to the option that will be added to the time; this is most useful when combined with now. If you have the jscalendar module from jstools installed, you can use a popup date picker here.'),
        ),
      'event_end' => array(
        'field' => 'event_end',
        'name' => t('Event: End Date'),
        'operator' => 'views_handler_operator_gtlt',
        'value' => views_handler_filter_date_value_form(),
        'option' => 'string',
        'handler' => 'event_views_handler_filter_datetime',
        'help' => t('This filter allows events to be filtered by their end date. Enter dates in the format: YYYY-MM-DD HH:MM:SS. Enter \'now\' to use the current time. You may enter a delta (in seconds) to the option that will be added to the time; this is most useful when combined with now. If you have the jscalendar module from jstools installed, you can use a popup date picker here.'),
      ),
      'year' => array(
        'field' => 'event_start',
        'name' => t('Event: Start Year'),
        'operator' => 'views_handler_operator_gtlt',
        'list' => $years,
        'list-type' => 'select',
        'handler' => 'event_views_handler_filter_ymd',
        'type' => 'YEAR',
        'help' => t('Filter by year. Use the option to select the date field to filter on.'),
        ),
      'month' => array(
        'field' => 'event_start',
        'name' => t('Event: Start Month'),
        'operator' => 'views_handler_operator_gtlt',
        'list' => $months,
        'list-type' => 'select',
        'handler' => 'event_views_handler_filter_ymd',
        'type' => 'MONTH',
        'help' => t('Filter by month. Use the option to select the date field to filter on.'),
        ),
      'day' => array(
        'field' => 'event_start',
        'name' => t('Event: Start Day'),
        'operator' => 'views_handler_operator_gtlt',
        'list' => $days,
        'list-type' => 'select',
        'handler' => 'event_views_handler_filter_ymd',
        'type' => 'DAYOFMONTH',
        'help' => t('Filter by day. Use the option to select the date field to filter on.'),
        ),
      ),
    );

  // add a pseudo table to set up additional fields and filters for node table
  $tables['event_views'] = array(
    'name' => 'node',
    'join' => array(
      'left' => array(
        'table' => 'node',
        'field' => 'nid'
        ),
      'right' => array(
        'field' => 'nid'
        ),
      ),
    // here's a filter that is limited to event content types to be used for exposed filters that should not contain other values
    'filters' => array(
      'event_type' => array(
        'field' => 'type',
        'name' => t('Event: Type'),
        'list' => $type_options,
        'list-type' => 'list',
        'operator' => 'views_handler_operator_or',
        'value-type' => 'array',
        'help' => t('A node type selector that is limited to event-enabled node types. Use as exposed filter that only contains event node types.'),
        ),
      ),
    );

   return $tables;
}

/**
 *  Implementation of hook_views_arguments()
 */
function event_views_views_arguments() {
  $arguments = array(
    'event_year' => array(
      'name' => t('Event: Start Year'),
      'handler' => 'event_views_handler_arg_year',
      'help' => t('Filter by the event year (YYYY).'),
    ),
    'event_month' => array(
      'name' => t('Event: Start Month'),
      'handler' => 'event_views_handler_arg_month',
      'help' => t("Filter by the event month (1-12). Place this argument after a 'Year' argument."),
    ),
    'event_day' => array(
      'name' => t('Event: Start Day'),
      'handler' => 'event_views_handler_arg_day',
      'help' => t("Filter by the event day (1-31). Place this argument after a 'Year' and a 'Month' argument."),
    ),
    'event_week' => array(
      'name' => t('Event: Start Week'),
      'handler' => 'event_views_handler_arg_week',
      'help' => t("Filter by the week number (1-52). Place this argument after a 'Year' argument."),
    ),
  );
  return $arguments;
}

/**
 *  Handle a timestamp filter.
 *
 *  Would use version in views.module except we have to ensure the
 *  events table is in the query.
 */
function event_views_handler_filter_datetime($op, $filter, $filterinfo, &$query) {
  $value = $filter['value'] == 'now' ? "***CURRENT_DATETIME***" : strtotime($filter['value']);

  $field = $filter['field'];
  $query->add_where("%s %s DATE_ADD('%s', INTERVAL %d SECOND)", $field, $filter['operator'], $value, $filter['options']);
  $query->ensure_table('event');
}

/*
 * Custom views handler for timezone, swap timezone name in for offset
 */
function event_views_timezone_display_handler($fieldinfo, $fielddata, $value, $data) {
  if (!$value) return;
  $zone = event_zonelist_by_id($value);
  return $zone['name'];
}

/**
 *  A handler to insert substitute date values into the calendar query
 */
function event_views_calendar_query_handler(&$field, &$fieldinfo, &$query) {

  $option = event_views_get_field($field['options']);

  // make sure the table is joined into the query and have the selected field serve AS the calendar date
  $query->ensure_table($option['table']);
  $query->add_field($option['field'] ." AS ". $field[field] ."", $option['table']);
}

/*
 * Custom views filter for year, month, day queries
 *
 * @param $filterinfo[operator] is =, >=, >, <=, <
 * @param $filterinfo[type] is year, month, or day
 */
function event_views_handler_filter_ymd($op, $filter, $filterinfo, &$query) {

  switch(trim($filter['value'])) {
    case '':
    case 'all':
      return;
      break;
    case '***CURRENT_DATETIME***':
      $compare = strtoupper($filterinfo['type']) ."($filter[value])";
      break;
    default:
      $compare = intval($filter['value']);
      break;
  }
  $sql = $filterinfo['type'] ."(event.event_start)";
  $query->ensure_table('event');
  $query->add_where("$sql $filter[operator] $compare");
}

/**
* Substitute current time; this works with cached queries.
*/
function event_views_views_query_substitutions($view) {
  return array('***CURRENT_DATETIME***' => gmdate('Y-m-d H:i', time()));
}

/**
 *  Custom views handlers for the events arguments
 */

function event_views_handler_arg_year($op, &$query, $argtype, $arg = '') {
  return event_views_handler_arg_type($op, $query, $argtype, $arg, 'event_year', 'YEAR');
}

function event_views_handler_arg_month($op, &$query, $argtype, $arg = '') {
  return event_views_handler_arg_type($op, $query, $argtype, $arg, 'event_month', 'MONTH');
}

function event_views_handler_arg_day($op, &$query, $argtype, $arg = '') {
  return event_views_handler_arg_type($op, $query, $argtype, $arg, 'event_day', 'DAYOFMONTH');
}

function event_views_handler_arg_week($op, &$query, $argtype, $arg = '') {
  return event_views_handler_arg_type($op, $query, $argtype, $arg, 'event_week', 'WEEK');
}

function event_views_handler_arg_type($op, &$query, $argtype, $arg, $field_name, $field_type) {

  $timezone = _views_get_timezone();

  switch ($field_type) {
  case ('MONTH'):
    $month = $arg ? $arg : $query->event_month ? $query->event_month : '';
    $title = $month ? date('F', strtotime("2006-{$month}-01")) : $query->$field_name;
    $sql_adj = '';
    break;
  case ('WEEK'):
    $title = t('Week ') . $query->$field_name;
    $sql_adj = ', 3';
  default:
    $title = $query->$field_name;
    $sql_adj = '';
    break;
  }

  if ($title == '') {
    $title = t('N/A');
  }

  // not yet postgres compatible, goal is to get it working reliably,
  // then try to figure out postgres support
  $sql = "$field_type(event.event_start$sql_adj)";

  switch($op) {
    case 'summary':
      $fieldinfo['field'] = $sql;
      $fieldinfo['fieldname'] = $field_name;
      $query->ensure_table('event');
      return $fieldinfo;
      break;
    case 'sort':
      $query->add_orderby(NULL, $field_name, $argtype);
      break;
    case 'filter':
      $event_year = intval($arg);
      $query->add_where("$sql = ". $arg);
      $query->ensure_table('event');
      break;
    case 'link':
      if ($title != t('N/A')) {
        return l($title, $arg .'/'. $query->$field_name);
      }
      else {
        return $title;
      }
    case 'title':
      return $title;
  }
}

/**
 *  Implementation of hook_views_default_views()
 *   Create default event views
 */
function event_views_views_default_views() {

  // avoid miscellaneous problems by forcing the cache to clear before creating a default view
  views_invalidate_cache();

  $event_node_types = array_merge(event_get_types('all'), event_get_types('solo'));

  $view = new stdClass();
  $view->name = t('event_select');
  $view->description = t('Events list filtered by drop-down date selector.');
  $view->disabled = TRUE;
  $view->access = array (
    );
  $view->view_args_php = '';
  $view->page = TRUE;
  $view->page_title = t('event list');
  $view->page_header = '';
  $view->page_header_format = '1';
  $view->page_footer = '';
  $view->page_footer_format = '1';
  $view->page_empty = '';
  $view->page_empty_format = '1';
  $view->page_type = 'table';
  $view->url = 'event/select';
  $view->use_pager = TRUE;
  $view->nodes_per_page = '10';
  $view->menu = FALSE;
  $view->menu_title = '';
  $view->menu_tab = FALSE;
  $view->menu_tab_default = FALSE;
  $view->menu_weight = '';
  $view->sort = array (
    );
  $view->argument = array (
    );
  $view->field = array (
    array (
      'tablename' => 'node',
      'field' => 'title',
      'label' => t('Title'),
      'handler' => 'views_handler_field_nodelink',
      'sortable' => '1',
    ),
    array (
      'tablename' => 'event',
      'field' => 'event_start',
      'label' => t('Start Time:'),
      'handler' => 'views_handler_field_date_small_iso',
      'sortable' => '1',
    ),
    array (
      'tablename' => 'event',
      'field' => 'event_end',
      'label' => t('End Time:'),
      'handler' => 'views_handler_field_date_small_iso',
      'sortable' => '1',
    ),
  );
  $view->filter = array (
    array (
      'tablename' => 'node',
      'field' => 'status',
      'operator' => '=',
      'options' => '',
      'value' => '1',
      ),
    array (
      'tablename' => 'node',
      'field' => 'type',
      'operator' => 'OR',
      'options' => '',
      'value' => $event_node_types,
      ),
   array (
      'tablename' => 'event',
      'field' => 'year',
      'operator' => '=',
      'options' => '',
      'value' => '***CURRENT_DATETIME***',
    ),
    array (
      'tablename' => 'event',
      'field' => 'month',
      'operator' => '>=',
      'options' => '',
      'value' => '***CURRENT_DATETIME***',
    ),
    array (
      'tablename' => 'event',
      'field' => 'day',
      'operator' => '>=',
      'options' => '',
      'value' => '1',
    ),

   );
  $view->exposed_filter = array (
     array (
      'tablename' => 'event',
      'field' => 'year',
      'label' => t('Start Year:'),
      'optional' => 0,
      'is_default' => 0,
      'operator' => 0,
      'single' => 0,
    ),
    array (
      'tablename' => 'event',
      'field' => 'month',
      'label' => t('Start Month:'),
      'optional' => 0,
      'is_default' => 0,
      'operator' => 0,
      'single' => 0,
    ),
     array (
      'tablename' => 'event',
      'field' => 'day',
      'label' => t('Start Day:'),
      'optional' => 0,
      'is_default' => 0,
      'operator' => 0,
      'single' => 0,
    ),
  );
  $view->requires = array('node', 'event');
  $views[$view->name] = $view;

  $view = new stdClass();
  $view->name = t('event_js');
  $view->description = t('Events list filtered by js date selector.');
  $view->disabled = TRUE;
  $view->access = array (
    );
  $view->view_args_php = '';
  $view->page = TRUE;
  $view->page_title = t('event list');;
  $view->page_header = '';
  $view->page_header_format = '1';
  $view->page_footer = '';
  $view->page_footer_format = '1';
  $view->page_empty = '';
  $view->page_empty_format = '1';
  $view->page_type = 'table';
  $view->url = 'event/selector';
  $view->use_pager = TRUE;
  $view->nodes_per_page = '10';
  $view->menu = FALSE;
  $view->menu_title = '';
  $view->menu_tab = FALSE;
  $view->menu_tab_default = FALSE;
  $view->menu_weight = '';
  $view->sort = array (
  );
  $view->argument = array (
  );
  $view->field = array (
    array (
    'tablename' => 'node',
    'field' => 'title',
    'label' => t('Title'),
    'handler' => 'views_handler_field_nodelink',
    'sortable' => '1',
    ),
    array (
    'tablename' => 'event',
    'field' => 'event_start',
    'label' => t('Start Time:'),
    'handler' => 'views_handler_field_date_small_iso',
    'sortable' => '1',
    ),
    array (
    'tablename' => 'event',
    'field' => 'event_end',
    'label' => t('End Time:'),
    'handler' => 'views_handler_field_date_small_iso',
    'sortable' => '1',
    ),
  );
  $view->filter = array (
    array (
    'tablename' => 'node',
    'field' => 'status',
    'operator' => '=',
    'options' => '',
    'value' => '1',
    ),
    array (
    'tablename' => 'node',
    'field' => 'type',
    'operator' => 'OR',
    'options' => '',
    'value' => $event_node_types,
    ),
    array (
    'tablename' => 'event',
    'field' => 'event_start',
    'operator' => '>',
    'options' => '',
    'value' => 'now',
    ),
    array (
    'tablename' => 'event',
    'field' => 'event_end',
    'operator' => '>',
    'options' => '',
    'value' => 'now',
    ),
  );
  $view->exposed_filter = array (
  array (
    'tablename' => 'event',
    'field' => 'event_start',
    'label' => t('Start Date'),
    'optional' => 0,
    'is_default' => 0,
    'operator' => 0,
    'single' => 0,
  ),
  array (
    'tablename' => 'event',
    'field' => 'event_end',
    'label' => t('End Date'),
    'optional' => 0,
    'is_default' => 0,
    'operator' => 0,
    'single' => 0,
  ),
  );

  $view->requires = array('node', 'event');
  $views[$view->name] = $view;


  $view = new stdClass();
  $view->name = t('event_date');
  $view->description = t('Events list filtered by url arguments.');
  $view->disabled = TRUE;
  $view->access = array (
    );
  $view->view_args_php = '';
  $view->page = TRUE;
  $view->page_title = t('event list');;
  $view->page_header = '';
  $view->page_header_format = '1';
  $view->page_footer = '';
  $view->page_footer_format = '1';
  $view->page_empty = '';
  $view->page_empty_format = '1';
  $view->page_type = 'table';
  $view->url = 'event/date';
  $view->use_pager = TRUE;
  $view->nodes_per_page = '10';
  $view->menu = FALSE;
  $view->menu_title = '';
  $view->menu_tab = FALSE;
  $view->menu_tab_default = FALSE;
  $view->menu_weight = '';
  $view->sort = array (
    );
  $view->argument = array (
    );
  $view->field = array (
   array (
      'tablename' => 'node',
      'field' => 'title',
      'label' => t('Title'),
      'handler' => 'views_handler_field_nodelink',
      'sortable' => '1',
    ),
     array (
      'tablename' => 'event',
      'field' => 'event_start',
      'label' => t('Start Time:'),
      'handler' => 'views_handler_field_date_small_iso',
      'sortable' => '1',
    ),
    array (
      'tablename' => 'event',
      'field' => 'event_end',
      'label' => t('End Time:'),
      'handler' => 'views_handler_field_date_small_iso',
      'sortable' => '1',
    ),
  );
  $view->filter = array (
    array (
      'tablename' => 'node',
      'field' => 'status',
      'operator' => '=',
      'options' => '',
      'value' => '1',
      ),
    array (
      'tablename' => 'node',
      'field' => 'type',
      'operator' => 'OR',
      'options' => '',
      'value' => $event_node_types,
      ),
  );
  $view->exposed_filter = array ();
  $view->argument = array (
    array (
      'type' => 'event_year',
      'argdefault' => '6',
      'title' => t('Year'),
      'options' => '',
    ),
    array (
      'type' => 'event_month',
      'argdefault' => '6',
      'title' => t('Month'),
      'options' => '',
    ),
    array (
      'type' => 'event_day',
      'argdefault' => '6',
      'title' => t('Day'),
      'options' => '',
    ),
  );
  $view->requires = array('node', 'event');
  $views[$view->name] = $view;

  return $views;
}

/**
* Provide a list of all standard supported iso date output handlers.
*/
function event_views_handler_field_dates_iso() {
  return array(
    'event_views_handler_field_date_small_iso'  => t('As Short Date'),
    'event_views_handler_field_date_iso'        => t('As Medium Date'),
    'event_views_handler_field_date_large_iso'  => t('As Long Date'),
    'event_views_handler_field_date_custom_iso' => t('As Custom Date'),
    'event_views_handler_field_since_iso'       => t('As Time Ago')
  );
}

function event_views_handler_field_date_iso($fieldinfo, $fielddata, $value, $data) {
  return $value ? event_format_date($value) : theme('views_nodate');
}


/**
 * Format a date using small representation.
 */
function event_views_handler_field_date_small_iso($fieldinfo, $fielddata, $value, $data) {
  return $value ? event_format_date($value, 'small') : theme('views_nodate');
}

/**
 * Format a date using large representation.
 */
function event_views_handler_field_date_large_iso($fieldinfo, $fielddata, $value, $data) {
  return $value ? event_format_date($value, 'large') : theme('views_nodate');
}

/**
 * Format a date using custom representation.
 */
function event_views_handler_field_date_custom_iso($fieldinfo, $fielddata, $value, $data) {
  return $value ? event_format_date($value, 'custom', $fielddata['options']) : theme('views_nodate');
}
