'Date Import/Export', 'description' => 'Import and export date data.', 'access arguments' => array('administer nodes'), 'page callback' => 'drupal_get_form', 'page arguments' => array('date_copy_import_ical_form'), 'type' => MENU_NORMAL_ITEM, ); $items['admin/content/date/import'] = array( 'title' => 'Import', 'access arguments' => array('administer nodes'), 'page callback' => 'drupal_get_form', 'page arguments' => array('date_copy_import_ical_form'), 'type' => MENU_DEFAULT_LOCAL_TASK, 'weight' => 1, ); $items['admin/content/date/export'] = array( 'title' => 'Export', 'access arguments' => array('administer nodes'), 'page callback' => 'drupal_get_form', 'page arguments' => array('date_copy_export_form'), 'type' => MENU_LOCAL_TASK, 'weight' => 2, ); $items['admin/content/date/import/ical'] = array( 'title' => 'iCal', 'access arguments' => array('administer nodes'), 'page callback' => 'drupal_get_form', 'page arguments' => array('date_copy_import_ical_form'), 'type' => MENU_DEFAULT_LOCAL_TASK, 'weight' => 1, ); $items['admin/content/date/import/event'] = array( 'title' => 'Events', 'access arguments' => array('administer nodes'), 'page callback' => 'drupal_get_form', 'page arguments' => array('date_copy_import_event_form'), 'type' => MENU_LOCAL_TASK, 'weight' => 2, ); $items['admin/content/date/import/csv'] = array( 'title' => 'CSV', 'access arguments' => array('administer nodes'), 'page callback' => 'drupal_get_form', 'page arguments' => array('date_copy_import_csv_form'), 'type' => MENU_LOCAL_TASK, 'weight' => 3, ); return $items; } /** * A form to select a content type. */ function date_copy_type_form($target = TRUE) { $form = array(); $node_types = node_get_types('names'); $fields = content_fields(); $type_options = array(); // Find out what content types contain date fields and set them up as target options. foreach ($fields as $field_name => $field) { if ($field['type'] == 'date' || $field['type'] == 'datestamp') { $type_options[$field['type_name']] = $node_types[$field['type_name']]; } } if (sizeof($type_options) < 1) { drupal_set_message(t('There are no date fields in this database to import the data into. Please add a date field to the desired node types and be sure to indicate it uses both a "from" and a "to" date.')); return $form; } $type = $target ? 'target_type' : 'source_type'; $label = $target ? t('Target type') : t('Source type'); $form[$type] = array( '#type' => 'select', '#options' => $type_options, '#title' => $label, '#default_value' => '', ); return $form; } /** * A form to select fields from a content type. */ function date_copy_type_fields_form($type, $extended = FALSE) { $form = array(); $fields = content_fields(); $date_options = array(); $description_options = array('' => ''); $uid_options = array('' => ''); $url_options = array('' => ''); $location_options = array('' => ''); // Find out what content types contain date fields and set them up as target options. foreach ($fields as $field_name => $field) { if ($field['type_name'] == $type) { if ($field['type'] == 'date' || $field['type'] == 'datestamp') { $date_options[$field_name] = $field['widget']['label']; } if ($field['type'] == 'text') { $description_options[$field_name] = $field['widget']['label']; $location_options[$field_name] = $field['widget']['label']; $uid_options[$field_name] = $field['widget']['label']; $url_options[$field_name] = $field['widget']['label']; } if ($field['type'] == 'link') { $url_options[$field_name] = $field['widget']['label']; } } } // The body field is also available as an option for the description. $description_options['body'] = t('body'); if (sizeof($date_options) < 1) { drupal_set_message(t('There are no date fields in this database to import the data into. Please add a date field to the desired node types and be sure to indicate it uses both a "from" and a "to" date.')); return $form; } $form['date_field'] = array( '#type' => 'select', '#options' => $date_options, '#title' => t('Date field'), '#default_value' => '', '#description' => t('The field which will contain the source dates in target content type.'), ); $form['description_field'] = array( '#type' => 'select', '#options' => $description_options, '#title' => t('Description field'), '#default_value' => '', '#description' => t('The text or body field which will contain the source description in the target content type.'), ); if ($extended) { $form['url_field'] = array( '#type' => 'select', '#options' => $url_options, '#title' => t('Url field'), '#default_value' => '', '#description' => t('The text or link field which will contain the source url in the target content type.'), ); $form['location_field'] = array( '#type' => 'select', '#options' => $location_options, '#title' => t('Location field'), '#default_value' => '', '#description' => t('The text field which will contain the source location text in the target content type.'), ); $form['uid_field'] = array( '#type' => 'select', '#options' => $uid_options, '#title' => t('Uid field'), '#default_value' => '', '#description' => t('The text field which will contain the source uid in the target content type.'), ); } return $form; } /** * A form to select miscellaneous other options for a content type. */ function date_copy_type_misc_form($type) { $form = array(); $vocabs = taxonomy_get_vocabularies($type); if ($vocabs && count($vocabs) > 0) { $taxonomy = isset($taxonomy) ? $taxonomy : array(); $node = (object)array( 'type' => $type, 'taxonomy' => date_import_taxonomy_form2node($taxonomy), ); $subform = array( 'type' => array( '#value' => $type, ), '#node' => $node, ); taxonomy_form_alter($subform, array(), $type .'_node_form'); $form['taxonomy'] = array( '#type' => 'fieldset', '#title' => t('Categories'), '#description' => t('Select the categories that should be used for the imported nodes.'), ); $form['taxonomy'] += $subform['taxonomy']; } if (module_exists('og')) { og_form_add_og_audience($form_id, $form); } $form['name'] = array( '#type' => 'textfield', '#title' => t('Authored by'), '#maxlength' => 60, '#autocomplete_path' => 'user/autocomplete', '#default_value' => $node->name ? $node->name : '', '#description' => t('Leave blank for %anonymous.', array( '%anonymous' => variable_get('anonymous', t('Anonymous'))))); $form['status'] = array( '#type' => 'checkbox', '#title' => t('Published'), '#default_value' => $node->status); $form['promote'] = array( '#type' => 'checkbox', '#title' => t('Promoted to front page'), '#default_value' => $node->promote); $form['sticky'] = array( '#type' => 'checkbox', '#title' => t('Sticky at top of lists'), '#default_value' => $node->sticky); $form['revision'] = array( '#type' => 'checkbox', '#title' => t('Create new revision'), '#default_value' => $node->revision); return $form; } /** * Convert the taxonomy array from the form form to the node form. * Borrowed from the Node Import module. */ function date_import_taxonomy_form2node($taxonomy) { $tids = array(); foreach ($taxonomy as $key => $value) { if ($key != 'tags') { $value = is_array($value) ? $value : array($value); foreach ($value as $tid) { $tids[$tid] = taxonomy_get_term($tid); } } else { $tids[$key] = $value; } } return $tids; } /** * Administration page */ function date_copy_import_form() { // PLACEHOLDER drupal_set_message(t('Import dates into CCK from various sources.')); } function date_copy_export_form() { // PLACEHOLDER drupal_set_message(t('This feature is not yet functional.')); } function date_copy_import_csv_form() { // PLACEHOLDER drupal_set_message(t('Importing dates into CCK from a comma separated file can be done using the !link.', array('!link' => l('Node Import module', 'http://drupal.org/project/node_import')))); } /** * iCal import form. */ function date_copy_import_ical_form($form_values = NULL) { if (!empty($form_values)) { foreach ($form_values as $key => $value) { $form[$key] = array('#type' => 'hidden', '#default_value' => $value); } } $step = intval($form_values['step'] + 1); $form['step'] = array( '#type' => 'hidden', '#value' => $step, ); $form['#multistep'] = TRUE; $form['#redirect'] = FALSE; switch ($step) { case 1: // Select a content type to import into. $form['#prefix'] = t('

Create a new CCK content type to import your events into. Make sure it has a date field that can allows a To date so it can accept the From date and To date of the iCal feed. If you are importing dates that have their own timezones, make sure you set the timezone handling of the date to \'date\'. Test the new type by trying to create a node manually and make sure all the right options are available in the form before attempting an import.

The import will create new nodes and trigger all related hooks, so you may want to turn off automatic email messaging for this node type while performing the import!

'); $form['source_file'] = array( '#type' => 'textfield', '#title' => t('Source file'), '#default_value' => '', ); $form += date_copy_type_form(TRUE); $form['submit'] = array('#type' => 'submit', '#value' => t('Submit')); return $form; case 2: // Select the fields to import into. $node_types = node_get_types('names'); $type = $form_values['target_type']; $form['target_type'] = array( '#value' => $type, '#type' => 'hidden', ); $form['source_file'] = array( '#value' => $form_values['source_file'], '#type' => 'hidden', ); $form['fields'] = array( '#type' => 'fieldset', '#title' => t('!type Fields', array('!type' => $node_types[$type])), '#weight' => -1, ); $form['fields'] += date_copy_type_fields_form($type, TRUE); $form += date_copy_type_misc_form($type); $form['submit'] = array('#type' => 'submit', '#value' => t('Submit')); return $form; } return $form; } function date_copy_import_ical_form_submit($form_id, $form_values) { extract($form_values); if ($step < 2) { return; } include_once(drupal_get_path('module', 'date_api') .'/date_api_ical.inc'); $imported_values = date_ical_import($source_file); if (empty($imported_values)) { drupal_set_message(t('This is an invalid file.')); return; } // workaround to disable drupal messages when nodes are created or deleted $messages = drupal_get_messages(); $node_types = node_get_types('names'); $field = content_fields($date_field, $target_type); $account = user_load(array('name' => $name)); $rows = array(); switch ($field['type']) { case DATE_UNIX: $format = 'U'; break; case DATE_ISO: $format = DATE_FORMAT_ISO; break; } foreach ($imported_values as $calendars => $calendar) { foreach ($calendar['VEVENT'] as $key => $value) { if (empty($value['DTEND'])) { $value['DTEND'] = $value['DTSTART']; } $start_date = date_ical_date($value['DTSTART']); $end_date = date_ical_date($value['DTEND']); $timezone = timezone_name_get(date_timezone_get($start_date)); $offset = date_offset_get($start_date); $offset2 = date_offset_get($end_date); date_timezone_set($start_date, 'UTC'); date_timezone_set($end_date, 'UTC'); $start = date_limit_value(date_format($start_date, $format), date_granularity($field), $field['type']); $end = date_limit_value(date_format($end_date, $format), date_granularity($field), $field['type']); $target_node = new stdClass(); $target_node->nid = 0; $target_node->type = $target_type; $target_node->name = $name; $target_node->uid = $account->uid; $target_node->status = $status; $target_node->promote = $promote; $target_node->sticky = $sticky; $target_node->revision = $revision; if (module_exists('og')) { $target_node->og_public = $og_public; $target_node->og_groups = $og_groups; } $target_node->title = stripslashes($value['SUMMARY']); $target_node->$date_field = array(0 => array( 'value' => $start, 'value2' => $end, 'timezone' => $timezone, 'offset' => $offset, 'offset2' => $offset2, )); if ($description_field == 'body') { $target_node->body = stripslashes($value['DESCRIPTION']); } elseif (!empty($description_field)) { $target_node->$description_field = array(0 => array('value' => stripslashes($value['DESCRIPTION']))); } if (!empty($uid_field)) { $target_node->$uid_field = array(0 => array('value' => stripslashes($value['UID']))); } if (!empty($location_field)) { $target_node->$location_field = array(0 => array('value' => stripslashes($value['LOCATION']))); } if (!empty($url_field)) { $target_node->$url_field = array(0 => array('url' => stripslashes($value['URL']), 'title' => stripslashes($value['SUMMARY']))); } $target_node->taxonomy = $taxonomy; node_save($target_node); watchdog('date_copy', t('!type: created %title.', array( '!type' => t($target_type), '%title' => $target_node->title)), WATCHDOG_NOTICE, l(t('view'), 'node/'. $target_node->nid)); $new_field = $target_node->$date_field; $rows[] = array( l($target_node->title, 'node/'. $target_node->nid), $target_node->nid, $new_field[0]['value'], $new_field[0]['value2'], ); } } // write back the old messages $_SESSION['messages'] = $messages; if (!empty($rows)) { drupal_set_message(t('%limit ical events have been added.', array('%limit' => sizeof($rows)))); drupal_set_message(theme('table', array(t('Title'), t('Id'), t('Start'), t('End')), $rows)); } else { drupal_set_message(t('No ical events have been added.')); } return; } /** * Event import form. */ function date_copy_import_event_form($form_values = NULL) { // We can do an import if there are event fields available whether or not the event module is enabled // so we just check whether the table exists. if (!db_table_exists('event')) { drupal_set_message(t('There is no event table in this database. No event import options are available.')); return array(); } $step = intval($form_values['step'] + 1); $form['step'] = array( '#type' => 'hidden', '#value' => $step, ); $form['#multistep'] = TRUE; $form['#redirect'] = FALSE; switch ($step) { case 1: // Select a content type to import into. $node_types = node_get_types('names'); $form['#prefix'] = t('

Create a new CCK content type to import your events into, or, if you do not want to create new nodes for your events, add a date field to the existing event type. Make sure the target content type has a date field that has an optional or required To date so it can accept the From date and To date of the event. If your source event has its own timezone field, make sure you set the target date timezone handling to \'date\'. Test the target type by trying to create a node manually and make sure all the right options are available in the form before attempting an import.

The import will create new nodes and trigger all related hooks, so you may want to turn off automatic email messaging for this node type while performing the import!

'); $source_type_options = array(); $result = db_query("SELECT DISTINCT n.type FROM {event} e INNER JOIN {node} n ON e.nid=n.nid"); while ($arr = db_fetch_array($result)) { $source_type_options[$arr['type']] = $node_types[$arr['type']]; } if (sizeof($source_type_options) < 1) { drupal_set_message(t('There are no event nodes in this database. No event import options are available.')); return array(); } $form['source_type'] = array( '#type' => 'select', '#options' => $source_type_options, '#title' => t('Source type'), '#default_value' => '', ); $form += date_copy_type_form(TRUE); $form['submit'] = array('#type' => 'submit', '#value' => t('Submit')); return $form; case 2: // Select the fields to import into. $type = $form_values['target_type']; $form['target_type'] = array( '#value' => $type, '#type' => 'hidden', ); $form['source_type'] = array( '#value' => $form_values['source_type'], '#type' => 'hidden', ); $form['fields'] = array( '#type' => 'fieldset', '#title' => t('!type Fields', array('!type' => $node_types[$type])), '#weight' => -1, ); $form['fields'] += date_copy_type_fields_form($type); $form['delete_old'] = array('#type' => 'select', '#options' => array(1 => t('Yes'), 0 => t('No')), '#title' => t('Delete original event?'), '#description' => t('Should the original entry be deleted once it has been copied to the new content type? If so, be sure to back up your database first.')); $form['max'] = array('#type' => 'textfield', '#title' => t('Limit'), '#description' => t('The maximum number of nodes to convert in this pass.'), '#required' => TRUE); $form['start_nid'] = array('#type' => 'textfield', '#title' => t('Starting nid'), '#default_value' => 0, '#description' => t('Convert nodes with nids greater than or equal to this number.')); $form['submit'] = array('#type' => 'submit', '#value' => t('Submit')); return $form; } } /** * Event import processing. */ function date_copy_import_event_form_submit($form_id, $form_values) { extract($form_values); if ($step != 2) return; // workaround to disable drupal messages when nodes are created or deleted //$messages = drupal_get_messages(); // The array that maps event timezone zids to timezone names is in // date_php4_tz_map.inc, need to reverse it so the zid is the key. include_once(drupal_get_path('module', 'date_php4') .'/date_php4_tz_map.inc'); $timezones = array('' => ''); $map = $timezone_map; foreach ($map as $zone => $values) { if (!empty($values['zid'])) { $timezones[$values['zid']] = $zone; } } $rows = array(); $i = 0; // Get $max records, 10 at a time. $limit = min(10, intval($max)); while ($i < intval($max)) { $new_rows = date_copy_convert_events($source_type, $target_type, $date_field, $description_field, $limit, $i, $delete_old, $start_nid, $timezones); $rows = array_merge($rows, $new_rows); $i += $limit; } // write back the old messages //$_SESSION['messages'] = $messages; if (!empty($rows)) { drupal_set_message(t('%limit events have been converted.', array('%limit' => sizeof($rows)))); drupal_set_message(theme('table', array(t('Title'), t('Source Id'), t('Target Id'), t('Start'), t('End')), $rows)); } else { drupal_set_message(t('No events have been converted.')); } return; } function date_copy_convert_events( $source_type, $target_type, $date_field, $description_field, $limit, $start = 0, $delete_old, $start_nid, $timezones) { // Get info about the field we are importing into $field = content_fields($date_field); // Get date tz handling, could be date, site, GMT, or none. $tz_handling = $field['tz_handling']; // Get event tz handling, could be event, site, or user. $event_tz_handling = variable_get('event_timezone_display', 'event'); // Check which version of the Event module this database was built in. $event_version = 1; if (db_column_exists('event', 'has_time')) { $event_version = 2; } $rows = array(); if ($start_nid) { $where = " AND n.nid >= $start_nid "; } if (!$result = db_query_range("SELECT * FROM {event} e INNER JOIN {node} n ON e.nid=n.nid WHERE n.type = '%s' $where ORDER BY n.nid", array($source_type, $start_nid), $start, $limit)) { return array(); } while ($event = db_fetch_object($result)) { $source_nid = $event->nid; $event_node = node_load($source_nid, NULL, TRUE); // Creating new nodes or converting existing ones?? if ($target_type != $source_type) { $target_node = new stdClass(); $target_node->nid = 0; $target_node->type = $target_type; foreach ($event_node as $key => $val) { if ($key != 'nid' && $key != 'type') { $target_node->$key = $val; } } } else { $target_node = $event_node; } if ($description_field != 'body') { $target_node->$description_field = array(0 => array('value' => $event_node->body)); unset($target_node->body); } $timezone = !empty($event->timezone) && $tz_handling == 'date' && $event_tz_handling == 'event' ? $timezones[$event->timezone] : date_default_timezone_name(); if ($event_version == 1) { // Version 1 stores the UTC value in the database as a timestamp. $date = array(0 => array()); $data[0]['timezone'] = $timezone; $start = date_make_date($event->event_start, 'UTC', DATE_UNIX); date_timezone_set($start, timezone_open($timezone)); $data[0]['offset'] = date_offset_get($start); $end = date_make_date($event->event_end, 'UTC', DATE_UNIX); date_timezone_set($end, timezone_open($timezone)); $data[0]['offset2'] = date_offset_get($end); // If the original event had the wrong offset, the 'UTC' value it // created will also be wrong, correct it here. if ($event_node->start_offset != date_offset_get($start) || $event_node->end_offset != date_offset_get($end)) { $adj = $event_node->start_offset - date_offset_get($start); date_timezone_set($start, timezone_open('UTC')); date_modify($start, $adj .' seconds'); $adj = $event_node->end_offset - date_offset_get($end); date_timezone_set($end, timezone_open('UTC')); date_modify($end, $adj .' seconds'); } $data[0]['value'] = date_format($start, date_type_format($field['type'])); $data[0]['value2'] = date_format($end, date_type_format($field['type'])); } else { // Version 2 stores the local value in the database as a datetime field. $date = array(0 => array()); $data[0]['timezone'] = $timezone; $start = date_make_date($event->event_start, $timezone, DATE_DATETIME); $data[0]['offset'] = date_offset_get($start); $end = date_make_date($event->event_end, $timezone, DATE_DATETIME); $data[0]['offset2'] = date_offset_get($end); date_timezone_set($start, timezone_open('UTC')); date_timezone_set($end, timezone_open('UTC')); $data[0]['value'] = date_format($start, date_type_format($field['type'])); $data[0]['value2'] = date_format($end, date_type_format($field['type'])); } $target_node->$date_field = $data; $event_fields = array( 'event_start', 'event_end', 'timezone', 'start_offset', 'start_format', 'start_time_format', 'end_offset', 'end_format', 'end_time_format', 'event_node_title'); foreach ($event_fields as $e) { unset($target_node->$e); } node_save($target_node); if ($target_type != $source_type) { watchdog('date_copy', t('!type: created %title.', array( '!type' => t($target_type), '%title' => $target_node->title)), WATCHDOG_NOTICE, l(t('view'), 'node/'. $target_node->nid)); if ($delete_old) { node_delete($source_nid); } } else { watchdog('date_copy', t('!type: updated %title.', array( '!type' => t($target_type), '%title' => $target_node->title)), WATCHDOG_NOTICE, l(t('view'), 'node/'. $target_node->nid)); } $new_field = $target_node->$date_field; $rows[] = array( l($target_node->title, 'node/'. $target_node->nid), $source_nid, $target_node->nid, $new_field[0]['value'], $new_field[0]['value2']); } return $rows; }