You are here

date_copy.module in Date 5

File

date_copy.module
View source
<?php

/**
 * Date Copy
 *
 * A module to import and export date data in various ways.
 * Currently only imports date data from events and ical.
 *
 * Importing data from csv files can currently be done using the Node Import module, no need to add that here.
 *
 * @todo
 *  Add export capabilities, possibly add support for csv import.
 */
function date_copy_help($section) {
  switch ($section) {
    case 'admin/modules#description':
      return t('Copy data in and out of Date module.');
      break;
  }
}

/**
 *  Implementation of hook_menu()
 */
function date_copy_menu($may_cache) {
  $items = array();
  if (!$may_cache) {
    $items[] = array(
      'path' => 'admin/content/date',
      'title' => t('Date Import/Export'),
      'description' => t('Import and export date data.'),
      'access' => user_access('administer nodes'),
      'callback' => 'drupal_get_form',
      'callback arguments' => 'date_copy_import_ical_form',
      'type' => MENU_NORMAL_ITEM,
    );
    $items[] = array(
      'path' => 'admin/content/date/import',
      'title' => t('Import'),
      'access' => user_access('administer nodes'),
      'callback' => 'drupal_get_form',
      'callback arguments' => 'date_copy_import_ical_form',
      'type' => MENU_DEFAULT_LOCAL_TASK,
      'weight' => 1,
    );
    $items[] = array(
      'path' => 'admin/content/date/export',
      'title' => t('Export'),
      'access' => user_access('administer nodes'),
      'callback' => 'drupal_get_form',
      'callback arguments' => 'date_copy_export_form',
      'type' => MENU_LOCAL_TASK,
      'weight' => 2,
    );
    $items[] = array(
      'path' => 'admin/content/date/import/ical',
      'title' => t('iCal'),
      'access' => user_access('administer nodes'),
      'callback' => 'drupal_get_form',
      'callback arguments' => 'date_copy_import_ical_form',
      'type' => MENU_DEFAULT_LOCAL_TASK,
      'weight' => 1,
    );
    $items[] = array(
      'path' => 'admin/content/date/import/event',
      'title' => t('Events'),
      'access' => user_access('administer nodes'),
      'callback' => 'drupal_get_form',
      'callback arguments' => 'date_copy_import_event_form',
      'type' => MENU_LOCAL_TASK,
      'weight' => 2,
    );
    $items[] = array(
      'path' => 'admin/content/date/import/csv',
      'title' => t('CSV'),
      'access' => user_access('administer nodes'),
      'callback' => 'drupal_get_form',
      'callback arguments' => '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($type . '_node_form', $subform);
    $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) {
  $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('<p>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.</p><p><strong>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!</strong></p>');
      $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;
  }
}

/**
 * iCal import processing.
 */
function date_copy_import_ical_form_validate($form_id, $form_values) {
  extract($form_values);
  if ($step == 2) {
    return;
  }
  include_once drupal_get_path('module', 'date_api') . '/date.inc';
  include_once drupal_get_path('module', 'date_api') . '/date_api_ical.inc';
  $imported_values = date_ical_import($source_file);
  if (empty($imported_values)) {
    form_set_error('source_file', t('This is an invalid file.'));
  }
}
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.inc';
  include_once drupal_get_path('module', 'date_api') . '/date_api_ical.inc';
  $imported_values = date_ical_import($source_file);

  // 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();
  foreach ($imported_values as $key => $value) {
    if (is_numeric($key) && !empty($value['SUMMARY']) && !empty($value['DTSTART'])) {
      $date = date_ical_date($value['DTSTART'], 'UTC');
      $start = $field['type'] == 'date' ? $date->local->iso : $date->local->timestamp;
      $start_timezone = $date->local->timezone;
      $start_offset = $date->local->offset;
      if (empty($value['DTEND'])) {
        $value['DTEND'] = $value['DTSTART'];
      }
      $date = date_ical_date($value['DTEND'], 'UTC');
      $end = $field['type'] == 'date' ? $date->local->iso : $date->local->timestamp;
      $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,
        ),
      );
      if ($field['tz_handling'] == 'date') {
        $target_node->{$date_field}[0]['timezone'] = $start_timezone;
        $target_node->{$date_field}[0]['offset'] = $start_offset;
      }
      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('<p>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. </p><p><strong>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!</strong></p>');
      $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();
  $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);
    $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) {
  include_once drupal_get_path('module', 'date_api') . '/date.inc';
  include_once './' . drupal_get_path('module', 'date_api') . '/date_timezones.inc';

  // 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');
  $timezones = date_get_timezones();
  $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);
    }
    $timestamp = $event->event_start;
    $timestamp2 = $event->event_end;
    if ($field['type'] == 'date') {
      $data = array(
        0 => array(
          'value' => date_unset_granularity(date_unix2iso($timestamp), date_granularity_array($field), $field['type']),
          'value2' => date_unset_granularity(date_unix2iso($timestamp2), date_granularity_array($field), $field['type']),
          'timezone' => '',
          'offset' => '',
        ),
      );
    }
    else {
      $data = array(
        0 => array(
          'value' => date_unset_granularity($timestamp, date_granularity_array($field), $field['type']),
          'value2' => date_unset_granularity($timestamp2, date_granularity_array($field), $field['type']),
          'timezone' => '',
          'offset' => '',
        ),
      );
    }
    if ($tz_handling == 'date' && $event_tz_handling == 'event') {
      $data[0]['timezone'] = $timezones[$event->timezone]['timezone'];
      $data[0]['offset'] = date_get_offset($event->timezone, $timestamp);
    }
    else {
      unset($data[0]['timezone']);
      unset($data[0]['offset']);
    }
    $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;
}

Functions

Namesort descending Description
date_copy_convert_events
date_copy_export_form
date_copy_help Date Copy
date_copy_import_csv_form
date_copy_import_event_form Event import form.
date_copy_import_event_form_submit Event import processing.
date_copy_import_form Administration page
date_copy_import_ical_form iCal import form.
date_copy_import_ical_form_submit
date_copy_import_ical_form_validate iCal import processing.
date_copy_menu Implementation of hook_menu()
date_copy_type_fields_form A form to select fields from a content type.
date_copy_type_form A form to select a content type.
date_copy_type_misc_form A form to select miscellaneous other options for a content type.
date_import_taxonomy_form2node Convert the taxonomy array from the form form to the node form. Borrowed from the Node Import module.