date.inc in Signup 7
Same filename and directory in other branches
Code to support using CCK date fields for time-based signup functionality.
File
includes/date.incView source
<?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 string $type
* The node type to generate the form element for.
*
* @return array
* 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 string $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 string $type
* The node type to check signup and CCK date field configuration on.
* @param string $name
* Human readable name of the node type to check.
*
* @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 string $type
* Content type whose date fields should be listed.
*
* @return array
* 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;
}
Functions
Name![]() |
Description |
---|---|
signup_content_type_fields | Returns a list of all cck fields that have been set for use in signups |
signup_date_check_node_types | Check the signup and date configuration on node types depending on the URL. |
signup_date_field | @todo Please document this function. |
signup_date_field_check_config | Check that the date and signup configuration for a node type makes sense. |
signup_field_names | @todo Please document this function. |
signup_form_content_field_edit_form_alter | Alter the form for configuring CCK date fields on node types. |
_signup_date_admin_query | Alters the SQL query for the admin overview page. |
_signup_date_alter_node_type_form | Alter the node type form to add a setting to select the signup date field. |
_signup_date_autoclose_sql | |
_signup_date_field_element | Create the FAPI form element for the signup date field. |
_signup_date_field_form_submit | Custom submit handler for the CCK date field editing form. |
_signup_date_format_date | |
_signup_date_get_node_scheduler | Determine if the specific node is date-enabled. |
_signup_date_node_completed | Returns TRUE if the given node is event-enabled, and the start time has already passed the "Close x hours before" setting. |
_signup_date_reminder_sql | |
_signup_get_date_field_options | Helper function for the date field select to build its options. |
_signup_get_sql_handler | Generates a Date API SQL handler for the given date field. |