You are here

dsv.inc in Node export 7.3

The Node export DSV format handler.

Adds configurable DSV format to Node export.

File

formats/dsv.inc
View source
<?php

/**
 * @file
 * The Node export DSV format handler.
 *
 * Adds configurable DSV format to Node export.
 */

/**
 * Settings callback.
 */
function node_export_dsv_settings($form, $form_state) {
  $settings['dsv'] = array(
    '#type' => 'fieldset',
    '#title' => t('DSV format settings'),
    '#description' => t('Select how your DSV output will be formatted - this must be configured the
     same on both sites.  By default this is configured to RFC4180 CSV format
     where the delimiter is a comma (,), the enclosure is a double-quote ("),
     and the separator is CRLF (\\r\\n).  Not all configurations may be possible,
     use wisely.  Enclosure will only be used to escape values that contain any
     of the configured strings.  Additionally single-quotes will be used to
     escape values that are equivalent to reserved words (NULL, TRUE, FALSE).'),
    '#collapsible' => TRUE,
    '#collapsed' => TRUE,
  );
  $settings['dsv']['node_export_dsv_delimiter'] = array(
    '#type' => 'textfield',
    '#title' => t('Value delimiter'),
    '#size' => 5,
    '#maxlength' => 255,
    '#default_value' => variable_get('node_export_dsv_delimiter', ','),
    '#required' => TRUE,
  );
  $settings['dsv']['node_export_dsv_enclosure'] = array(
    '#type' => 'textfield',
    '#title' => t('Escape enclosure'),
    '#size' => 5,
    '#maxlength' => 255,
    '#default_value' => variable_get('node_export_dsv_enclosure', '"'),
    '#required' => TRUE,
  );
  $settings['dsv']['node_export_dsv_separator'] = array(
    '#type' => 'textfield',
    '#title' => t('Record separator'),
    '#size' => 5,
    '#maxlength' => 255,
    '#default_value' => variable_get('node_export_dsv_separator', '\\r\\n'),
    '#required' => TRUE,
  );
  $settings['dsv']['node_export_dsv_escape_eol'] = array(
    '#type' => 'checkbox',
    '#title' => t('Always escape values containing line breaks'),
    '#default_value' => variable_get('node_export_dsv_escape_eol', 1),
    '#description' => t('This is to overcome problems where Windows injects CRLF line breaks.'),
  );
  return $settings;
}
function node_export_dsv_string($string) {
  $replace = array(
    '\\n' => "\n",
    '\\r' => "\r",
    '\\t' => "\t",
    '\\v' => "\v",
    '\\e' => "\33",
    '\\f' => "\f",
  );
  return str_replace(array_keys($replace), array_values($replace), $string);
}

/**
 * Export callback.
 */
function node_export_dsv_export($nodes, $format) {
  $delimiter = node_export_dsv_string(variable_get('node_export_dsv_delimiter', ','));
  $enclosure = node_export_dsv_string(variable_get('node_export_dsv_enclosure', '"'));
  $separator = node_export_dsv_string(variable_get('node_export_dsv_separator', '\\r\\n'));
  $escape_eol = variable_get('node_export_dsv_escape_eol', 1);
  return node_export_dsv_encode($nodes, $delimiter, $enclosure, $separator, $escape_eol);
}

/**
 * Build DSV string.
 */
function node_export_dsv_encode($nodes, $delimiter, $enclosure, $separator, $escape_eol) {
  $encoded_nodes = array();
  $dsv_lines = array();
  $node_keys = array();
  foreach (array_keys($nodes) as $node_key) {
    $new_node_key = 'node_' . $node_key;
    $node_keys[] = $new_node_key;
    node_export_dsv_encode_node($encoded_nodes, $new_node_key, $nodes[$node_key]);
  }
  $dsv_lines['node_export_dsv_header'] = array_keys($encoded_nodes);
  foreach (array_keys($encoded_nodes) as $header_value) {
    $encoded_nodes[$header_value] = array_merge(array_fill_keys($node_keys, NULL), $encoded_nodes[$header_value]);
    foreach (array_keys($encoded_nodes[$header_value]) as $encoded_node_key) {
      $dsv_lines[$encoded_node_key][$header_value] = $encoded_nodes[$header_value][$encoded_node_key];
    }
  }
  return node_export_dsv_array_to_dsv($dsv_lines, $delimiter, $enclosure, $separator, $escape_eol);
}

/**
 * Process a node and update $header and $encoded_nodes accordingly.
 */
function node_export_dsv_encode_node(&$encoded_nodes, $node_key, $var, $parent = NULL) {
  foreach ($var as $k => &$v) {

    // Get the new header value.
    $header_value = node_export_dsv_encode_header_value($parent, $var, $k);
    if (is_object($v) || is_array($v)) {

      // Recurse through the structure.
      node_export_dsv_encode_node($encoded_nodes, $node_key, $v, $header_value);
    }
    else {

      // Create a safe text version of this value and store it against the header using a safe key.
      $encoded_nodes[$header_value][$node_key] = node_export_dsv_encode_sanitize_value($v);
    }
  }
}

/**
 * Encode a value.
 */
function node_export_dsv_encode_sanitize_value($var) {
  if (is_numeric($var)) {
    return $var;
  }
  elseif (is_bool($var)) {
    return $var ? 'TRUE' : 'FALSE';
  }
  elseif (is_null($var)) {
    return 'NULL';
  }
  elseif (is_string($var) && !empty($var)) {

    // Single-quote strings that could be confused for null or boolean.
    if (in_array(strtoupper($var), array(
      'TRUE',
      'FALSE',
      'NULL',
    ))) {
      $var = "'" . $var . "'";
    }
    return $var;
  }
  else {
    return '';
  }
}

/**
 * Decode a value.
 */
function node_export_dsv_decode_sanitize_value($var) {

  // Allow numeric, bool, and null values to pass right back as is.
  if (is_numeric($var) || is_bool($var) || is_null($var)) {
    return $var;
  }
  elseif (in_array(strtoupper($var), array(
    "'TRUE'",
    "'FALSE'",
    "'NULL'",
  ))) {
    return $var;
  }

  // Assume this is a string.
  return "'" . str_replace("'", "\\'", $var) . "'";
}

/**
 * Create header value from $parents, $var, and $k.
 */
function node_export_dsv_encode_header_value($parents, $var, $k) {
  if (is_null($parents)) {

    // Special case; on the first level do not prefix the key.
    $header_value = $k;
  }
  elseif (is_object($var)) {
    $header_value = $parents . "->" . $k;
  }
  elseif (is_array($var)) {
    $header_value = $parents . "['" . $k . "']";
  }
  return $header_value;
}

/**
 * Import callback.
 */
function node_export_dsv_import($code_string) {
  $delimiter = node_export_dsv_string(variable_get('node_export_dsv_delimiter', ','));
  $enclosure = node_export_dsv_string(variable_get('node_export_dsv_enclosure', '"'));
  $separator = node_export_dsv_string(variable_get('node_export_dsv_separator', '\\r\\n'));
  return node_export_dsv_decode($code_string, $delimiter, $enclosure, $separator);
}

/**
 *  Interpret a DSV string.
 */
function node_export_dsv_decode($code_string, $delimiter, $enclosure, $separator) {

  // Get array data from DSV.
  $array = @node_export_dsv_dsv_to_array($code_string, $delimiter, $enclosure, $separator);

  // If the first two rows are of equal length, we can assume this is a DSV.
  // Also checks there are a decent number of fields.
  if (!empty($array[0]) && !empty($array[1]) && count($array[0]) > 10 && count($array[0]) == count($array[1])) {
    $nodes = array();

    // Assume row 0 is the header, and the rest of the rows are the nodes.
    $header = array_shift($array);

    // Build the nodes.
    foreach ($array as &$row) {
      $node = (object) array();
      foreach ($row as $key => $item) {
        $item = node_export_dsv_decode_sanitize_value($item);
        eval('$node->' . $header[$key] . ' = ' . $item . ';');
      }
      $nodes[] = $node;
    }
    return $nodes;
  }
}

/**
 *  Encode DSV.
 */
function node_export_dsv_array_to_dsv($array, $delimiter, $enclosure, $separator, $escape_eol) {
  $lines = array();
  foreach ($array as $line) {
    $out_item = array();
    foreach ($line as $item) {
      if (stripos($item, $enclosure) !== FALSE) {
        $item = str_replace($enclosure, $enclosure . $enclosure, $item);
      }
      if (stripos($item, $delimiter) !== FALSE || stripos($item, $enclosure) !== FALSE || stripos($item, $separator) !== FALSE || $escape_eol && stripos($item, "\n") !== FALSE) {
        $item = $enclosure . $item . $enclosure;
      }
      $out_item[] = $item;
    }
    $lines[] = implode($delimiter, $out_item);
  }
  return implode($separator, $lines);
}

/**
 *  Decode DSV.
 */
function node_export_dsv_dsv_to_array($string, $delimiter, $enclosure, $separator) {
  $lines = array();
  $out_item = array();
  $count = strlen($string);
  $escape = FALSE;
  $double_escape = FALSE;
  $position = 0;
  $i = 0;
  $separators = str_split($separator);
  while ($i < $count) {
    $c = $string[$i];

    // Determine whether this is an EOL.
    $is_eol = TRUE;
    for ($j = 0; $j < count($separators); $j++) {
      if (!isset($string[$i + $j]) || $string[$i + $j] != $separators[$j]) {
        $is_eol = FALSE;
        break;
      }
    }
    if ($is_eol) {
      if ($escape) {
        $out_item[$position] .= $c;
      }
      else {
        $i += count($separators);
        $lines[] = $out_item;
        $out_item = array();
        $position = 0;
        continue;
      }
    }
    elseif ($c == $delimiter) {
      if ($escape) {
        $out_item[$position] .= $c;
      }
      else {
        if ($string[$i - 1] == $delimiter) {
          $out_item[$position] .= '';
        }
        $position++;
        $escape = FALSE;
        $double_escape = FALSE;
      }
    }
    elseif ($c == $enclosure) {
      if ($double_escape) {
        $out_item[$position] .= $enclosure;
        $double_escape = FALSE;
      }
      if ($escape) {
        $escape = FALSE;
        $double_escape = TRUE;
      }
      else {
        $escape = TRUE;
        $double_escape = FALSE;
      }
    }
    else {
      if ($double_escape) {
        $out_item[$position] .= $enclosure;
        $double_escape = FALSE;
      }
      $out_item[$position] .= $c;
    }
    $i++;
  }
  if (!empty($out_item)) {
    $lines[] = $out_item;
  }
  return $lines;
}

/**
 * Callback for actions.
 */
function node_export_dsv_action_form($context, &$form_state) {
  return node_export_action_form($context, $form_state, 'dsv');
}

Functions

Namesort descending Description
node_export_dsv_action_form Callback for actions.
node_export_dsv_array_to_dsv Encode DSV.
node_export_dsv_decode Interpret a DSV string.
node_export_dsv_decode_sanitize_value Decode a value.
node_export_dsv_dsv_to_array Decode DSV.
node_export_dsv_encode Build DSV string.
node_export_dsv_encode_header_value Create header value from $parents, $var, and $k.
node_export_dsv_encode_node Process a node and update $header and $encoded_nodes accordingly.
node_export_dsv_encode_sanitize_value Encode a value.
node_export_dsv_export Export callback.
node_export_dsv_import Import callback.
node_export_dsv_settings Settings callback.
node_export_dsv_string