I was trying to update my calendar module, which also required the Date module, ctools, and views to be updated. I now get this when I go to the status link (http://localhost/drupal2/admin/reports/status) This error occurs on several other screens also.

Fatal error: Call to undefined function date_field_get_sql_handler() in C:\wamp\www\drupal2\sites\all\modules\signup\includes\date.inc on line 330

Here is the call stack:

1 0.0005 368064 {main}( ) ..\index.php:0
2 0.8805 42878000 menu_execute_active_handler( ) ..\index.php:21
3 2.7808 54013424 drupal_deliver_page( ) ..\menu.inc:518
4 2.7809 54013688 drupal_deliver_html_page( ) ..\common.inc:2438
5 2.9910 60055456 drupal_page_footer( ) ..\common.inc:2550
6 2.9971 60055560 system_run_automated_cron( ) ..\common.inc:2577
7 2.9972 60055640 drupal_cron_run( ) ..\system.module:3472
8 3.6982 60716712 module_invoke( ) ..\common.inc:5050
9 3.6982 60716920 call_user_func_array ( ) ..\module.inc:794
10 3.6982 60717056 signup_cron( ) ..\module.inc:0
11 3.6993 60756512 _signup_cron_send_reminders( ) ..\signup.module:177
12 3.6994 60756952 signup_reminder_sql( ) ..\cron.inc:22
13 3.6996 60757000 _signup_date_reminder_sql( ) ..\scheduler.inc:125

CommentFileSizeAuthor
#7 1452920.patch2.61 KBBTMash
#2 signup_quick_hack.patch2.82 KBpezia
Support from Acquia helps fund testing for Drupal Acquia logo

Comments

pezia’s picture

According to date changelog: "Remove date_field_get_sql_handler() function which isn't being used anywhere."
This is causing the problem.

As a quick hack I copied the original function, so the website can function until a proper fix is done.

pezia’s picture

FileSize
2.82 KB
flightrisk’s picture

Where will the fix need to happen? In the signup module? Will it have to look through how the date module is now doing things and find an all new way to handle the timezone and date calculations to send the reminders? Who is going to have to make then change and when?

hyerstay’s picture

I went thru the same thing. You have to update sites/all/modules/signup/includes/date.inc

dww’s picture

Ugh. Yeah, I guess signup needs to figure out the new way to do date + timezone calculations given the latest date.module release. If anyone can do a little more Git archeology or other research in the date.module codebase to figure out how to achieve the same things, that'd be great. I'd be reluctant to just put a clone of date_field_get_sql_handler() into signup.module since perhaps date.module's underlying data representation is going to change and break us again. I'd rather just try to keep up with date.module's recommended API (if possible).

Thanks,
-Derek

VanD’s picture

Confirming that this bug exists and that the patch from #2 solved the problem for me.

BTMash’s picture

Status: Active » Reviewed & tested by the community
FileSize
2.61 KB

I can confirm the patch works as well. The code also looks good. I am rerolling the patch so it should actually apply but am also marking it at RTBC.

BTMash’s picture

Priority: Major » Critical

And I am changing to critical since it results in cron being unable to run on the server.

linuxbcn’s picture

I apply the patch (in local first and update by ftp) and the error don't apear in the bottom of the page.
But when I try to send an mail by Forward options apears this message:

Recoverable fatal error: Argument 1 passed to drupal_http_build_query() must be an array, string given, called in /home/MySERVER/public_html/dev/includes/common.inc on line 2192 and defined a drupal_http_build_query() (línia 477 de /home/MySERVER/public_html/dev/includes/common.inc).

Is just another error?

BTMash’s picture

@linuxbcn, sigh - seems like this is another error though, unfortunately, it is not related to this particular error :( Would you mind posting an issue for the error you get?

linuxbcn’s picture

No problem, i'll create a brand new issue.
But i'm making some test and I think is just an error with forward Module, not this.

I sent-it to apropiate bug report fo this.

sorry

rurri’s picture

Had same issue. Patch fixed it for me.

I think this is the best way to handle this for an immediate fix, that is pretty safe.

mbeenon’s picture

Heres the patched version:

<?php
/**
 * @file
 * Code to support using CCK date fields for time-based signup functionality.
 */

/**
 * @todo Please document this function.
 * @see http://drupal.org/node/1354
 */
function signup_field_names($content_type = NULL) {
  $fields = array();
  /*$content_type_info = _content_type_info();
   if ($content_type_info['content types'][$content_type]) {
    foreach ($content_type_info['content types'][$content_type]['fields'] as $field) {
      if (in_array($field['type'], array('date', 'datestamp', 'datetime'))) {
        $fields[$field['field_name']] = $field['widget']['label'];
      }
    }
   }*/

  /* TODO: Test if this works, and if this is the optimum way of retrieving
   * this information.
   *
   * @see http://api.drupal.org/api/drupal/modules--field--field.info.inc/7
   */
  $content_type_info = field_info_instances('node');
  if ($content_type_info[$content_type]) {
    foreach ($content_type_info[$content_type] as $field_name => $field) {
      if ($field['widget']['module'] == 'date') {
        $fields[$field_name] = $field['label'];
      }
    }
  }
  return $fields;
}

/**
 * @todo Please document this function.
 * @see http://drupal.org/node/1354
 */
function signup_date_field($content_type) {
  $field_name = variable_get('signup_date_field_' . $content_type, 0);
  if ($field_name === 0 || $field_name == '0') {
    // PHP is completely evil and 'none' == 0 is TRUE, hence the extra checks.
    return FALSE;
  }
  if ($field_name == 'none') {
    return 'none';
  }
  $field = field_info_field($field_name);
  if (empty($field)) {
    return array();
  }

	// Store the database table for this field.
	// @TODO: this probably could be done in a much, much more elegant and robust fashion....
	if (!isset($field['database'])) {
		$field['database'] = array('table' => 'field_data_' . $field_name);
	}

  return $field;
}

/**
 * Returns a list of all cck fields that have been set for use in signups
 */
function signup_content_type_fields() {
  $fields = array();
  foreach (signup_content_types() as $content_type) {
    $field = signup_date_field($content_type);
    if (!empty($field) && $field != 'none') {
      $fields[] = $field;
    }
  }
  return $fields;
}

/**
 * Determine if the specific node is date-enabled.
 */
function _signup_date_get_node_scheduler($node) {
  $field = signup_date_field($node->type);
  if (!empty($node->{$field['field_name']}[$node->language][0]['value'])) {
    return 'date';
  }
  if (isset($node->{$field['field_name'] . '_value'})) {
    return 'date';
  }
  return 'none';
}

/**
 * Alter the form for configuring CCK date fields on node types.
 *
 * Hooks into CCK Date fields to provide an option to use the current
 * field as the Signup date field (for autoclose and reminder emails).
 *
 */
function signup_form_content_field_edit_form_alter(&$form, &$form_state) {
  $type = $form['type_name']['#value'];
  if (in_array($form['#field']['type'], array('date', 'datestamp', 'datetime')) && variable_get('signup_node_default_state_' . $type, 'disabled') != 'disabled') {
    $form['signup'] = array(
      '#type' => 'fieldset',
      '#title' => t('Signup settings'),
      '#collapsible' => TRUE,
      '#weight' => 1,
    );
    $form['signup']['signup_date_field'] = _signup_date_field_element($type);
    $form['#submit'][] = '_signup_date_field_form_submit';
    // Make sure the submit button comes after the signup settings fieldset.
    $form['submit']['#weight'] = 50;
  }
}

/**
 * Custom submit handler for the CCK date field editing form.
 *
 * @see _signup_date_field_form_alter()
 */
function _signup_date_field_form_submit($form, &$form_state) {
  $type = $form_state['values']['type_name'];
  if (empty($form_state['values']['signup_date_field'])) {
    variable_del('signup_date_field_' . $type);
  }
  else {
    variable_set('signup_date_field_' . $type, $form_state['values']['signup_date_field']);
  }
}

/**
 * Alter the node type form to add a setting to select the signup date field.
 *
 * @see signup_alter_node_type_form()
 */
function _signup_date_alter_node_type_form(&$form, &$form_state) {
  drupal_add_js(drupal_get_path('module', 'signup') . '/js/admin.content_types.js');

  $type = $form['#node_type']->type;
  $default_signup_state = variable_get('signup_node_default_state_' . $type, 'disabled');

  // Add a div to the 'Signup options' radios for signup.date.js.
  $form['signup']['signup_node_default_state']['#prefix'] = '<div class="signup-node-default-state-radios">';
  $form['signup']['signup_node_default_state']['#suffix'] = '</div>';

  // If event.module is enabled, add a div for those settings, too.
  if (!empty($form['workflow']['event_nodeapi'])) {
    $form['workflow']['event_nodeapi']['#prefix'] = '<div class="event-nodeapi-radios">';
    $form['workflow']['event_nodeapi']['#suffix'] = '</div>';
    $event_enabled = $form['workflow']['event_nodeapi']['#default_value'] != 'never';
  }
  else {
    $event_enabled = FALSE;
  }

  // Figure out if we should hide the date field selector by default.
  $class = 'signup-date-field-setting';
  if ($default_signup_state == 'disabled' || $event_enabled) {
    $class .= ' js-hide';
  }

  $form['signup']['signup_date_field'] = _signup_date_field_element($type);
  $form['signup']['signup_date_field']['#prefix'] = '<div class="' . $class . '">';
  $form['signup']['signup_date_field']['#suffix'] = '</div>';
}

/**
 * Create the FAPI form element for the signup date field.
 *
 * @param $type
 *   The node type to generate the form element for.
 *
 * @return
 *   FAPI form array for the signup date field element.
 *
 * @see _signup_date_field_form_alter()
 * @see _signup_date_alter_node_type_form()
 */
function _signup_date_field_element($type) {
  return array(
    '#type' => 'select',
    '#title' => t('Date field to use with signup'),
    '#options' => _signup_get_date_field_options($type),
    '#default_value' => variable_get('signup_date_field_' . $type, 0),
    '#description' => t('Select the date field of this content type to use for signup time-based functionality, such as automatically closing signups when the start time has passed and sending reminder emails. Select "%none" to not use a date field for signup functionality at all.', array('%none' => t('None'))),
  );
}

/**
 * Check the signup and date configuration on node types depending on the URL.
 *
 * This function is invoked from signup_help() so that we can check the
 * configuration of any signup-enabled node types to ensure that the CCK date
 * field and signup settings make sense.
 *
 * @param $type
 *   The 4th element in the URL which specifies which node type is currently
 *   being configured.  If this is empty, it means we're at the node type
 *   overview listing and we should test all node types.
 *
 * @see signup_help()
 * @see signup_date_field_check_config()
 */
function signup_date_check_node_types($type = NULL) {
  $names = node_type_get_names();
  if (!empty($type)) {
    signup_date_field_check_config($type, $names[$type]);
  }
  else {
    foreach ($names as $type => $name) {
      signup_date_field_check_config($type, $name);
    }
  }
}

/**
 * Check that the date and signup configuration for a node type makes sense.
 *
 * This validates that if a node type is signup enabled, that it either has a
 * signup date field selected (for autoclose and reminder emails), or that the
 * signup date field has been explicitly set to 'None'.  It warns the site
 * administrator if they have signup-enabled a node type and not defined any
 * date fields at all, or if they have date fields but haven't selected the
 * one to use for signup functionality.
 *
 * @param $type
 *   The node type to check signup and CCK date field configuration on.
 * @param $name
 *   Human readable name of the node type to check.
 *
 * @return
 *   Nothing -- configuration errors are reported via drupal_set_message().
 *
 * @see signup_help()
 */
function signup_date_field_check_config($type, $name) {
  $signup_default_state = variable_get('signup_node_default_state_' . $type, 'disabled');
  $signup_scheduler = _signup_get_node_type_scheduler($type);
  if ($signup_scheduler != 'event' && $signup_default_state != 'disabled') {
    // Signups aren't disabled on this node type, see if there's a date field.
    $signup_date_field = signup_date_field($type);
    if ($signup_date_field != 'none') {
      $type_url = str_replace('_', '-', $type);
      $placeholders = array(
        '%node_type' => $name,
        '%signup_date_field' => t('Date field to use with signup'),
        '@type_admin_url' => url('admin/content/node-type/' . $type_url),
        '@type_add_field_url' => url('admin/content/node-type/' . $type_url . '/fields'),
        '%none' => '<' . t('none') . '>',
      );
      // Administrator hasn't specifically turned off date support...
      if (signup_field_names($type)) {
        // Node type has some date fields...
        if ($signup_date_field == 0) {
          drupal_set_message(t('You have enabled the %node_type content type for signups, and have added one or more date fields, but have not selected a date field for use with signup. You can modify the %signup_date_field setting at the <a href="@type_admin_url">%node_type configuration page</a> to select a date field to use, or disable this warning by selecting %none.', $placeholders), 'warning');
        }
      }
      else {
        // No date fields at all.
        drupal_set_message(t('You have enabled the %node_type content type for signups but have not added a date field. You can either <a href="@type_add_field_url">add a date field</a>, or disable this warning by selecting %none for the %signup_date_field setting at the <a href="@type_admin_url">%node_type configuration page</a>.', $placeholders), 'warning');
      }
    }
  }
}

/**
 * Helper function for the date field select to build its options.
 *
 * @param $type
 *   Content type whose date fields should be listed.
 *
 * @return
 *   Associative array with all date fields of the given content type plus
 *   'None' and an optional 'Not specified' if the user never selected a
 *   value.
 */
function _signup_get_date_field_options($type) {
  $options = array();
  // Add "Not specified" if the user never selected a field.
  if (variable_get('signup_date_field_' . $type, 0) == 0) {
    $options = array(0 => '<' . t('Not specified') . '>');
  }
  // Add any date fields from this node type.
  $options += signup_field_names($type);
  // Always add 'None' as the final choice.
  $options += array('none' => '<' . t('None') . '>');
  return $options;
}

/**
 *
 * @return Array of SQL clauses for cron reminder email query builder.
 */
function _signup_date_reminder_sql($content_type) {
  // Get the date field information for this content type.
  $field = signup_date_field($content_type);
  $start_field = $field['field_name'] . '_value';

  // Figure out what TZ we want to do the date comparisons in.
  $compare_tz = $field['settings']['tz_handling'] == 'none' ? date_default_timezone() : 'UTC';
  // Get a DateAPI SQL handler class for this field.
  $handler = _signup_get_sql_handler($field, $compare_tz);

  // Find the current time in the appropriate TZ for this field.
  $now_date = date_now($compare_tz);
  // Need to enclose this in ' marks to use directly in the SQL.
  $now = "'" . date_format($now_date, DATE_FORMAT_DATETIME) . "'";

  // Extract the correct SQL to represent the start time.
  $start_time = $handler->sql_field($start_field);

  // Create SQL to represent the time we should start sending reminders, based
  // on the SQL for the start time and the reminder_days_before field.
  $reminder_start = $handler->sql_date_math($start_time, 'SUB', 's.reminder_days_before', 'DAY');
  $reminder_stop = $handler->sql_date_math($start_time, 'ADD', 1, 'HOUR');

  // The WHERE clauses are now trivial: We want to make sure a) the current
  // time is after the time we should start sending reminders, but before the
  // actual start time itself.
  $where = array(
    "$now >= $reminder_start",
    "$now <= $reminder_stop",
  );

  // See what fields to SELECT.
  $fields[] = $start_field;
  if (isset($field['columns']['timezone'])) {
    $fields[] = $field['field_name'] . '_timezone';
  }
  $table = '{' . $field['database']['table'] . '}';
  return array(
    'fields' => $fields,
    'joins' => array("INNER JOIN $table ON $table.revision_id = n.vid"),
    'where' => $where,
  );
}

/**
 * Generates a Date API SQL handler for the given date field.
 *
 * The handler will be set up to make the correct timezone adjustments
 * for the field settings.
 *
 * @param array $field
 *   The $field array.
 * @param string $compare_tz
 *   The timezone used for comparison values in the SQL.
 */
function _signup_get_sql_handler($field, $compare_tz = NULL) {
  module_load_include('inc', 'date_api', 'date_api_sql');

  $db_info = date_api_database_info($field);

  // Create a DateAPI SQL handler class for this field type.
  $handler = new date_sql_handler($field['type']);

  // If this date field stores a timezone in the DB, tell the handler about it.
  if ($field['settings']['tz_handling'] == 'date') {
    $handler->db_timezone_field = $db_info['columns']['timezone']['column'];
  }
  else {
    $handler->db_timezone = date_get_timezone_db($field['settings']['tz_handling']);
  }

  if (empty($compare_tz)) {
    $compare_tz = date_get_timezone($field['settings']['tz_handling']);
  }
  $handler->local_timezone = $compare_tz;

  // Now that the handler is properly initialized, force the DB
  // to use UTC so no timezone conversions get added to things like
  // NOW() or FROM_UNIXTIME().
  $handler->set_db_timezone();

  return $handler;
}

/**
 *
 * @return Array of SQL clauses for cron auto-close query builder.
 */
function _signup_date_autoclose_sql($content_type) {
  // Get the date field information for this content type.
  $field = signup_date_field($content_type);
  $start_field = $field['field_name'] . '_value';

  // Figure out what TZ we want to do the date comparisons in.
  $compare_tz = $field['settings']['tz_handling'] == 'none' ? date_default_timezone() : 'UTC';
  // Get a DateAPI SQL handler class for this field.
  $handler = _signup_get_sql_handler($field, $compare_tz);

  // Compute a string representing the moment when signups should start
  // auto-closing.  If the field has no TZ handling, we just want to grab the
  // current local time.  If the field has any TZ handling, the date will be
  // stored in the DB in UTC time, so start from current UTC time.  Once we
  // have the right current time, we need to add our close-in-advance offset.
  $close_early_hours = variable_get('signup_close_early', 1);
  $close_date = date_now($compare_tz);
  date_modify($close_date, "+$close_early_hours hours");
  $close_date_str = date_format($close_date, DATE_FORMAT_DATETIME);

  // Use the DateAPI SQL handler to construct an appropriate WHERE clause.
  // Make sure that the start time is <= NOW plus the auto-close window.
  $where = $handler->sql_where_date('DATE', $start_field, '<=', $close_date_str);

  // See what fields to SELECT.
  $fields[] = $start_field;
  if (isset($field['columns']['timezone'])) {
    $fields[] = $field['field_name'] . '_timezone';
  }
  $table = '{' . $field['database']['table'] . '}';
  return array(
    'fields' => $fields,
    'joins' => array("INNER JOIN $table ON $table.revision_id = n.vid"),
    'where' => $where,
  );
}

/**
 * Alters the SQL query for the admin overview page.
 *
 * @param object $query
 *   The database query object.
 * @param string $content_type
 *   A string containing the content type of the node.
 */
function _signup_date_admin_query(&$query, $content_type) {
  // Get the date field information for this content type.
  $field = signup_date_field($content_type);

  // In the case where the same date field is being reused on multiple
  // content types, we'll potentially be JOINing on the same tables and
  // columns for different content types. To defend against duplicate table
  // names or ambiguous columns in the query, use the content type to alias.
  // Also, prefix it to make sure it's never a reserved word.
  $alias = 'signup_alias_' . db_escape_table($content_type);
  $table = $field['database']['table'];

  $date = $field['field_name'] . '_value';
  $query->leftJoin($table, $alias, $alias . '.revision_id = n.vid');
  $query->addField($alias, $date, $date);
  $query->groupBy($date);

  if (isset($field['columns']['timezone'])) {
    $timezone = $field['field_name'] . '_timezone';
    $query->addField($alias, $timezone, $timezone);
    $query->groupBy($timezone);
  }
}

/**
 * Returns TRUE if the given node is event-enabled, and the start time
 * has already passed the "Close x hours before" setting.
 */
function _signup_date_node_completed($node) {
  $field = signup_date_field($node->type);
  if ($field && $field != 'none' && isset($node->{$field['field_name']})) {
    // Grab whatever date value we actually have, regardless of format.
    $date_value = $node->{$field['field_name']}[$node->language][0]['value'];
    // Figure out the timezone handling for this date.
    if ($field['settings']['tz_handling'] == 'date') {
      $tz = $node->{$field['field_name']}[$node->language][0]['timezone'];
    }
    else {
      $tz = date_default_timezone();
    }
    $db_tz = date_get_timezone_db($field['settings']['tz_handling'], $tz);
    // Create a date object
    $date = new DateObject($date_value, $db_tz, NULL);
    // Make sure the date object is going to print UTC values.
    $date->setTimezone(timezone_open('UTC'));
    // Find out how early signups should be automatically closed.
    $close_early_hours = variable_get('signup_close_early', 1);
    $date->modify("-$close_early_hours hours");
    $close_time = $date->format('U');
    // Find the current UTC time.
    $now = date_now('UTC');
    if (date_format($now, 'U') >= $close_time) {
      // It's now later than when this node would automatically close signups.
      return TRUE;
    }
  }
  return FALSE;
}

function _signup_date_format_date($node, $include_to_date = FALSE) {
  $field = signup_date_field($node->type);
  if (!$field || $field == 'none') {
    return '';
  }
  if ($field['settings']['tz_handling'] == 'date') {
    if (isset($node->{$field['field_name']})) {
      $tz = $node->{$field['field_name']}[$node->language][0]['timezone'];
    }
    else {
      $tz = $node->{$field['field_name'] . '_timezone'};
    }
  }
  else {
    $tz = date_default_timezone();
  }
  $display_tz = date_get_timezone($field['settings']['tz_handling'], $tz);
  $db_tz = date_get_timezone_db($field['settings']['tz_handling'], $tz);

  if (isset($node->{$field['field_name']})) {
    $date_value = $node->{$field['field_name']}[$node->language][0]['value'];
  }
  else {
    $date_value = $node->{$field['field_name'] . '_value'};
  }

  $date = new DateObject($date_value, $db_tz, NULL);
  if ($db_tz != $display_tz) {
    $date->setTimezone(timezone_open($display_tz));
  }
  $format = date_formatter_format('default', $field['field_name']);
  $date_out = $date->format($format);

  if ($include_to_date) {
    if (isset($node->{$field['field_name']})) {
      $date_value = $node->{$field['field_name']}[$node->language][0]['value2'];
    }
    else {
      $date_value = $node->{$field['field_name'] . '_value2'};
    }
    $date = new DateObject($date_value, $db_tz, $field['type']);
    if ($db_tz != $display_tz) {
      $date->setTimezone(timezone_open($display_tz));
    }
    //@TODO: shouldn't this be $date_out?
    $date = $date->format($format);
    if ($date_value) {
      $date = new DateObject($date_value);
      $date_out .= t(' to ') . $date->format($format);
    }
  }

  return $date_out;
}

daniel-san’s picture

Applied patch #2 with success. Thanks for the work on this module.

Correction, I did not get success with the patch.

Hoping the following updates can help this issue along.

I continue getting 500 errors on nodes that have signup enabled and a date field. I can create nodes with a date, enable signup on the node and everything seems fine, but then if I come back to the node, click edit and then click View tab or save the node, I get a 500 error. I applied the patch in #2 and, at first, I thought everything was working fine, but then it started happening again. I will continue testing my setup to see if I can pinpoint a bit more.

As of July 24, 2012, I'm using:

Signup 7.x-1.x-dev
Date 7.x-2.5
Views 7.x-3.3

UPDATE
Ok, I can have everything working great until I add the date field to a view that lists my events. After that, if I go to the view and click the node title I get a 500 error. I can go directly to the edit page of the node, but from there if I save or click the View tab for the node, it errors.

Hope this helps.

daniel-san’s picture

Issue summary: View changes

noted that the error happens on page load for many admin screens

sgabe’s picture

Title: Fatal error on status report screen after updating date and views module » Fatal error caused by undefined date_field_get_sql_handler function
Issue summary: View changes
Status: Reviewed & tested by the community » Fixed

Lets just get this in to save some headache for people.

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.