node_export.module in Node export 6.3
Same filename and directory in other branches
The Node export module.
Allows users to export nodes and then import them into another Drupal installation.
File
node_export.moduleView source
<?php
/**
* @file
* The Node export module.
*
* Allows users to export nodes and then import them into another Drupal installation.
*/
/**
* Implementation of hook_perm().
*/
function node_export_perm() {
return array(
'export nodes',
'export own nodes',
'use PHP to import nodes',
);
}
/**
* Implementation of hook_menu().
*/
function node_export_menu() {
$items['admin/settings/node_export'] = array(
'access arguments' => array(
'administer site configuration',
),
'page callback' => 'drupal_get_form',
'page arguments' => array(
'node_export_settings',
),
'title' => 'Node export',
'description' => 'Configure the settings for Node export.',
'file' => 'node_export.pages.inc',
);
$selected_formats = variable_get('node_export_format', array(
'node_code',
));
if (count(array_filter($selected_formats)) > 1) {
$format_handlers = node_export_format_handlers();
foreach ($format_handlers as $format_handler => $format) {
if (in_array($format_handler, $selected_formats)) {
$items['node/%node/node_export/' . $format_handler] = array(
'access callback' => 'node_export_access_export',
'access arguments' => array(
1,
),
'page callback' => 'node_export_gui',
'page arguments' => array(
1,
$format_handler,
),
'title' => 'Node export (' . $format['#title'] . ')',
'weight' => 5,
'type' => MENU_LOCAL_TASK,
'file' => 'node_export.pages.inc',
);
}
}
}
else {
$items['node/%node/node_export'] = array(
'access callback' => 'node_export_access_export',
'access arguments' => array(
1,
),
'page callback' => 'node_export_gui',
'page arguments' => array(
1,
),
'title' => 'Node export',
'weight' => 5,
'type' => MENU_LOCAL_TASK,
'file' => 'node_export.pages.inc',
);
}
$items['admin/content/node_export'] = array(
'access arguments' => array(
'export nodes',
),
'page callback' => 'node_export_gui',
'page arguments' => array(
NULL,
NULL,
),
'title' => 'Node export',
'type' => MENU_CALLBACK,
'file' => 'node_export.pages.inc',
);
$items['node/add/node_export'] = array(
'title' => 'Node export: import',
'page callback' => 'drupal_get_form',
'page arguments' => array(
'node_export_import_form',
),
'access callback' => 'node_export_access_import',
'description' => 'Import content using <em>Node export</em>.',
'file' => 'node_export.pages.inc',
);
return $items;
}
/**
* Check access to export a node.
*/
function node_export_access_export($node) {
global $user;
if (is_numeric($node)) {
$node = node_load($node);
}
if (function_exists('drush_main')) {
// Always allow drush to export nodes.
$access = TRUE;
}
else {
// Check basic role permissions first.
$access = user_access('export nodes') || $user->uid && $node->uid == $user->uid && user_access('export own nodes');
// Make sure the user can view the original node content.
$access = $access && node_access('view', $node);
}
// Let other modules alter this - for example to only allow some users
// to export specific nodes or types.
drupal_alter("node_export_access_export", $access, $node);
return $access;
}
/**
* Check access to import a node.
*/
function node_export_access_import($node = NULL) {
global $user;
if (function_exists('drush_main')) {
// Always allow drush to import nodes.
$access = TRUE;
}
elseif (defined('MAINTENANCE_MODE') && MAINTENANCE_MODE == 'install') {
// During the install phase $user is the Anonymous user; however, in
// practice $user is performing tasks only granted to the admin user
// (eg: installing modules, changing site settings). For this reason
// it seems sensible to allow this "Anonymous admin" user to import
// any nodes they wish.
$access = TRUE;
}
else {
// Check basic role permissions first.
$access = user_access('use PHP to import nodes');
if (!is_null($node) && $access) {
// Check node conditions.
$access = node_access('create', $node->type) && filter_access($node->format);
}
}
// Let other modules alter this - for example to only allow some users
// to import specific nodes or types.
drupal_alter("node_export_access_import", $access, $node);
return $access;
}
/**
* Check access to export an array of nodes.
*/
function node_export_access_export_nodes($nodes) {
// Convert to array if it isn't already.
if (is_object($nodes)) {
$nodes = array(
$nodes,
);
}
foreach ($nodes as &$node) {
if (!node_export_access_export($node)) {
return FALSE;
}
}
return TRUE;
}
/**
* Check access to import an array of nodes.
*/
function node_export_access_import_nodes($nodes) {
// Convert to array if it isn't already.
if (is_object($nodes)) {
$nodes = array(
$nodes,
);
}
foreach ($nodes as &$node) {
if (!node_export_access_import($node)) {
return FALSE;
}
}
return TRUE;
}
/**
* Implementation of hook_node_type().
*/
function node_export_node_type($op, $type_obj) {
switch ($op) {
case 'delete':
variable_del('node_export_reset_' . $type_obj->type);
break;
case 'update':
if (!empty($type_obj->old_type) && $type_obj->old_type != $type_obj->type) {
if (variable_get('node_export_reset_' . $type_obj->old_type, FALSE)) {
variable_del('node_export_reset_' . $type_obj->old_type);
variable_set('node_export_reset_' . $type_obj->type, TRUE);
}
}
break;
}
}
/**
* Implementation of hook_views_api().
*/
function node_export_views_api() {
return array(
'api' => 2,
'path' => drupal_get_path('module', 'node_export') . '/views',
);
}
/**
* Implementation of hook_node_operations().
*/
function node_export_node_operations() {
$operations = array();
if (user_access('export nodes')) {
$selected_formats = variable_get('node_export_format', array(
'node_code',
));
if (count(array_filter($selected_formats)) > 1) {
$format_handlers = node_export_format_handlers();
foreach ($format_handlers as $format_handler => $format) {
if ($selected_formats[$format_handler]) {
$operations['node_export_' . $format_handler] = array(
'label' => t('Node export') . " (" . $format['#title'] . ")",
'callback' => 'node_export_bulk_operation',
'callback arguments' => array(
$format_handler,
NULL,
),
);
}
}
}
else {
$operations = array(
'node_export' => array(
'label' => t('Node export'),
'callback' => 'node_export_bulk_operation',
'callback arguments' => array(
NULL,
NULL,
),
),
);
}
}
return $operations;
}
/**
* Callback for use with hook_node_operations().
*/
function node_export_bulk_operation($nodes = NULL, $format = NULL, $delivery = NULL) {
module_load_include('inc', 'node_export', 'node_export.pages');
return node_export_gui($nodes, $format, $delivery);
}
/**
* Export nodes.
*
* @param $nids
* A node ID or array of node IDs to export.
* @param $format
* The format to use for export.
* @param $msg_t
* Function used to translate.
* @return
* An array with keys 'success' which is a boolean value representing whether
* the export was successful and 'output' which contains the code string or an
* array of translated error messages to be shown to the user.
*/
function node_export($nids, $format = NULL, $msg_t = 't') {
global $user;
// Make $nids an array if it isn't.
if (is_numeric($nids)) {
$nids = array(
$nids,
);
}
elseif (is_object($nids)) {
$nids = array(
$nids->nid,
);
}
$nodes = array();
foreach ($nids as $nid) {
$original_node = node_load($nid);
if (!node_export_access_export($original_node)) {
// Halt exporting.
$error = $msg_t("You do not have permission to perform a Node export on one or more of these nodes. No nodes exported.");
return array(
'success' => FALSE,
'output' => array(
$error,
),
);
}
$node = node_export_prepare_node($original_node);
$nodes[] = $node;
}
// Get the node code from the format handler
$format_handlers = node_export_format_handlers();
$format_handler = $format ? $format : reset(variable_get('node_export_format', array(
'node_code',
)));
if (!isset($format_handlers[$format_handler])) {
$format_handler = 'node_code';
}
// Let other modules do special fixing up.
drupal_alter('node_export', $nodes, 'export', $format_handler);
// If any nodes are set to FALSE, then an error was triggered in another module.
// Currently modules doing this should also leave a watchdog warning.
if (in_array(FALSE, $nodes)) {
// Halt exporting.
$error = $msg_t('An error occurred when processing nodes, please check your logs. No nodes exported.');
return array(
'success' => FALSE,
'output' => array(
$error,
),
);
}
$code_string = module_invoke($format_handlers[$format_handler]['#module'], 'node_export', $nodes, $format_handler);
// Let modules modify the node code.
drupal_alter('node_export_encode', $code_string, $nodes, $format_handler);
return array(
'success' => TRUE,
'output' => $code_string,
'format' => $format_handler,
);
}
/**
* Prepare a single node during export.
*/
function node_export_prepare_node(&$original_node) {
// Create UUID if it's not there.
// Currently this uses a module_exists() until UUID becomes a dependency.
if (!uuid_get_uuid('node', 'nid', $original_node->nid)) {
$original_node->uuid = uuid_set_uuid('node', 'nid', $original_node->nid);
// Save it so future node exports are consistent.
node_save($original_node);
}
$node = drupal_clone($original_node);
// Fix taxonomy array
if (isset($node->taxonomy) && count($node->taxonomy)) {
$vocabularies = taxonomy_get_vocabularies();
$new_taxonomy = array();
foreach ($node->taxonomy as $term) {
// Free-tagging vocabularies need a different format
if ($vocabularies[$term->vid]->tags) {
$new_taxonomy['tags'][$term->vid][] = $term->name;
}
else {
$new_taxonomy[$term->vid][$term->tid] = $term->tid;
}
}
if (isset($new_taxonomy['tags']) && count($new_taxonomy['tags'])) {
// Comma seperate the tags
foreach ($new_taxonomy['tags'] as $vid => $tags) {
$new_taxonomy['tags'][$vid] = implode(', ', $tags);
}
}
$node->taxonomy = $new_taxonomy;
}
// Fix menu array
$node->menu = node_export_get_menu($original_node);
$node = node_export_remove_recursion($node);
// Let other modules do special fixing up.
drupal_alter('node_export_node', $node, $original_node, 'export');
return $node;
}
/**
* Check if all types in the import exist.
*
* @return
* TRUE if all types exist, otherwise an array of missing type names.
*/
function node_export_import_types_check($nodes) {
$missing_types = array();
foreach ($nodes as $node) {
if (node_get_types('name', $node) == FALSE) {
$missing_types[$node->type] = $node->type;
}
}
return !empty($missing_types) ? $missing_types : TRUE;
}
/**
* Import Function
*
* @param $code_string
* The string of export code.
* @param $msg_t
* Function used to translate.
* @return
* An array with keys 'success' which is a boolean value representing whether
* the import was successful and 'output' which contains an array of
* translated strings to be shown to the user as messages.
*/
function node_export_import($code_string, $msg_t = 't') {
// Early check to avoid letting hooligans and the elderly pass data to the
// eval() function call.
if (!node_export_access_import()) {
$error = $msg_t('You do not have permission to import any nodes.');
return array(
'success' => FALSE,
'output' => array(
$error,
),
);
}
// Allow modules to manipulate the $code_string.
drupal_alter('node_export_decode', $code_string);
// Pass the string to each format handler until one returns something useful.
$format_handlers = node_export_format_handlers();
$nodes = array();
$used_format = "";
foreach ($format_handlers as $format_handler => $format) {
$nodes = module_invoke($format_handlers[$format_handler]['#module'], 'node_export_import', $code_string);
if (!empty($nodes)) {
$used_format = $format_handler;
break;
}
}
if (isset($nodes['success']) && !$nodes['success']) {
// Instead of returning nodes, the format handler returned an error array.
// Translate the errors and return them.
foreach ($nodes['output'] as $key => $output) {
$nodes['output'][$key] = $msg_t($output);
}
return array(
'success' => FALSE,
'output' => $nodes['output'],
);
}
if ($used_format == "") {
$error = $msg_t('Node export was unable to recognize the format of the supplied code. No nodes imported.');
return array(
'success' => FALSE,
'output' => array(
$error,
),
);
}
$nodes = node_export_restore_recursion($nodes);
$types_exist = node_export_import_types_check($nodes);
if ($types_exist !== TRUE) {
// There was a problem with the content types check.
$error = $msg_t('Error encountered during import. Node types unknown on this site: %t. No nodes imported.', array(
'%t' => implode(", ", $types_exist),
));
return array(
'success' => FALSE,
'output' => array(
$error,
),
);
}
if (!node_export_access_import_nodes($nodes)) {
// There was a problem with permissions.
$error = $msg_t('You do not have permission to perform a Node export: import on one or more of these nodes. No nodes imported.');
return array(
'success' => FALSE,
'output' => array(
$error,
),
);
}
$count = 0;
$total = count($nodes);
// Let other modules do special fixing up.
drupal_alter('node_export', $nodes, 'import', $used_format);
$new_nodes = array();
$new_nid = 0;
$messages = array();
foreach ($nodes as $original_node) {
$skip = FALSE;
$node = node_export_node_clone($original_node);
// Handle existing nodes.
$uuid_nid = node_export_node_get_by_uuid($node->uuid, TRUE);
if (!empty($uuid_nid)) {
$existing = variable_get('node_export_existing', 'new');
switch ($existing) {
case 'new':
$node->is_new = TRUE;
unset($node->nid);
unset($node->uuid);
break;
case 'revision':
$node->nid = $uuid_nid;
$node->is_new = FALSE;
$node->revision = 1;
break;
case 'skip':
$skip = TRUE;
break;
}
}
// Let other modules do special fixing up.
drupal_alter('node_export_node', $node, $original_node, 'import');
if (!$skip) {
node_export_save($node);
$new_nodes[] = $node;
$messages[] = $msg_t("Imported node !nid: !node", array(
'!nid' => $node->nid,
'!node' => l($node->title, 'node/' . $node->nid),
));
$count++;
}
}
drupal_alter('node_export', $new_nodes, 'after import', $used_format);
$messages[] = $msg_t("!count of !total nodes were imported. Some values may have been reset depending on Node export's configuration.", array(
'!total' => $total,
'!count' => $count,
));
// Clear the page and block caches.
cache_clear_all();
return array(
'success' => TRUE,
'output' => $messages,
);
}
/**
* Save a node object into the database.
*
* A modified version of node_save().
*/
function node_export_save(&$node) {
// Let modules modify the node before it is saved to the database.
node_invoke_nodeapi($node, 'presave');
global $user;
$node->is_new = FALSE;
// Apply filters to some default node fields:
if (empty($node->nid)) {
// Insert a new node.
$node->is_new = TRUE;
// When inserting a node, $node->log must be set because
// {node_revisions}.log does not (and cannot) have a default
// value. If the user does not have permission to create
// revisions, however, the form will not contain an element for
// log so $node->log will be unset at this point.
if (!isset($node->log)) {
$node->log = '';
}
// For the same reasons, make sure we have $node->teaser and
// $node->body. We should consider making these fields nullable
// in a future version since node types are not required to use them.
if (!isset($node->teaser)) {
$node->teaser = '';
}
if (!isset($node->body)) {
$node->body = '';
}
}
elseif (!empty($node->revision)) {
$node->old_vid = $node->vid;
}
else {
// When updating a node, avoid clobberring an existing log entry with an empty one.
if (empty($node->log)) {
unset($node->log);
}
}
// Set some required fields:
if (empty($node->created)) {
$node->created = time();
}
// The update of the changed value is forced in the original node_save().
if (empty($node->changed)) {
$node->changed = time();
}
$node->timestamp = time();
$node->format = isset($node->format) ? $node->format : FILTER_FORMAT_DEFAULT;
// Generate the node table query and the node_revisions table query.
if ($node->is_new) {
_node_save_revision($node, $user->uid);
drupal_write_record('node', $node);
db_query('UPDATE {node_revisions} SET nid = %d WHERE vid = %d', $node->nid, $node->vid);
$op = 'insert';
}
else {
drupal_write_record('node', $node, 'nid');
if (!empty($node->revision)) {
_node_save_revision($node, $user->uid);
db_query('UPDATE {node} SET vid = %d WHERE nid = %d', $node->vid, $node->nid);
}
else {
_node_save_revision($node, $user->uid, 'vid');
}
$op = 'update';
}
// Call the node specific callback (if any).
node_invoke($node, $op);
node_invoke_nodeapi($node, $op);
// Update the node access table for this node.
node_access_acquire_grants($node);
}
/**
* Generates a pristine cloned node such that it is ready for import. Note, the
* node_export_features module uses this function to generate cleaner exports.
*/
function node_export_node_clone($original_node) {
global $user;
$node = drupal_clone($original_node);
$node->nid = NULL;
$node->vid = NULL;
$node->name = $user->name;
$node->uid = $user->uid;
if (variable_get('node_export_reset_created_' . $node->type, TRUE)) {
$node->created = NULL;
}
if (variable_get('node_export_reset_changed_' . $node->type, TRUE)) {
$node->changed = NULL;
}
if (variable_get('node_export_reset_revision_timestamp_' . $node->type, TRUE)) {
$node->revision_timestamp = NULL;
}
if (variable_get('node_export_reset_last_comment_timestamp_' . $node->type, TRUE)) {
$node->last_comment_timestamp = NULL;
}
if (variable_get('node_export_reset_menu_' . $node->type, TRUE)) {
$node->menu = NULL;
}
if (variable_get('node_export_reset_path_' . $node->type, TRUE)) {
$node->path = NULL;
}
else {
if (is_array($node->path) && isset($node->path['pid'])) {
unset($node->path['pid']);
}
if (module_exists('pathauto')) {
// Prevent pathauto from creating a new path alias.
$node->pathauto_perform_alias = FALSE;
}
}
if (variable_get('node_export_reset_book_mlid_' . $node->type, TRUE) && isset($node->book['mlid'])) {
$node->book['mlid'] = NULL;
}
$node->files = array();
if (variable_get('node_export_reset_' . $node->type, FALSE)) {
$node_options = variable_get('node_options_' . $node->type, array(
'status',
'promote',
));
// Fill in the default values.
foreach (array(
'status',
'moderate',
'promote',
'sticky',
'revision',
) as $key) {
$node->{$key} = in_array($key, $node_options);
}
}
return $node;
}
/**
* Create a new menu entry with title, parent and weight exported from
* another nodes menu. Returns NULL if the node has no menu title.
*/
function node_export_get_menu($node) {
// This will fetch the existing menu item if the node had one.
node_invoke_nodeapi($node, 'prepare');
// Only keep the values we care about.
if (!empty($node->menu)) {
// Store a copy of the old menu
$old_menu = $node->menu;
// Now fetch the defaults for a new menu entry.
$node = NULL;
node_invoke_nodeapi($node, 'prepare');
// Make a list of values to attempt to copy.
$menu_fields = array(
'link_title',
'plid',
'menu_name',
'weight',
// These should import properly always.
'hidden',
'expanded',
'has_children',
);
// Copy those fields from the old menu over the new menu defaults.
foreach ($menu_fields as $menu_field) {
$node->menu[$menu_field] = $old_menu[$menu_field];
}
// Return the menu.
return $node->menu;
}
}
/**
* Remove recursion problem from an object or array.
*/
function node_export_remove_recursion($o) {
static $replace;
if (!isset($replace)) {
$replace = create_function('$m', '$r="\\x00{$m[1]}ecursion_export_node_";return \'s:\'.strlen($r.$m[2]).\':"\'.$r.$m[2].\'";\';');
}
if (is_array($o) || is_object($o)) {
$re = '#(r|R):([0-9]+);#';
$serialize = serialize($o);
if (preg_match($re, $serialize)) {
$last = $pos = 0;
while (false !== ($pos = strpos($serialize, 's:', $pos))) {
$chunk = substr($serialize, $last, $pos - $last);
if (preg_match($re, $chunk)) {
$length = strlen($chunk);
$chunk = preg_replace_callback($re, $replace, $chunk);
$serialize = substr($serialize, 0, $last) . $chunk . substr($serialize, $last + ($pos - $last));
$pos += strlen($chunk) - $length;
}
$pos += 2;
$last = strpos($serialize, ':', $pos);
$length = substr($serialize, $pos, $last - $pos);
$last += 4 + $length;
$pos = $last;
}
$serialize = substr($serialize, 0, $last) . preg_replace_callback($re, $replace, substr($serialize, $last));
$o = unserialize($serialize);
}
}
return $o;
}
/**
* Restore recursion to an object or array.
*/
function node_export_restore_recursion($o) {
return unserialize(preg_replace('#s:[0-9]+:"\\x00(r|R)ecursion_export_node_([0-9]+)";#', '\\1:\\2;', serialize($o)));
}
/**
* Get a list of possible format handlers (other than the default).
*
* @return
* An array of format handlers from hook implementations.
* @see hook_node_export_format_handlers()
*/
function node_export_format_handlers() {
static $format_handlers;
// Get default format handler.
module_load_include('inc', 'node_export', 'node_export.node_code');
if (empty($format_handlers)) {
$format_handlers = module_invoke_all('node_export_format_handlers');
}
return $format_handlers;
}
// Remove once http://drupal.org/node/858274 is resolved.
if (!function_exists('uuid_set_uuid')) {
/**
* API function to set the UUID of an object based on its serial ID.
*
* @param $table
* Base table of the object. Currently, one of node, revision_revisions,
* users, vocabulary or term_data.
* @param $key
* The name of the serial ID column.
* @param $serial_id
* The serial ID of the object.
* @param $uuid
* Optional UUID. If omitted, a UUID will be generated.
* @return
* The UUID on success, FALSE if the uuid provided is not valid.
*/
function uuid_set_uuid($table, $key, $serial_id, $uuid = FALSE) {
if (empty($uuid)) {
$uuid = uuid_uuid();
}
if (!uuid_is_valid($uuid)) {
return FALSE;
}
$uuid_table = 'uuid_' . $table;
db_query("UPDATE {" . $uuid_table . "} SET uuid = '%s' WHERE " . $key . " = %d", $uuid, $serial_id);
if (!db_affected_rows()) {
@db_query("INSERT INTO {" . $uuid_table . "} (" . $key . ", uuid) VALUES (%d, '%s')", $serial_id, $uuid);
}
return $uuid;
}
}
// Remove once http://drupal.org/node/858274 is resolved.
if (!function_exists('uuid_get_uuid')) {
/**
* API function to get the UUID of an object based on its serial ID.
*
* @param $table
* Base table of the object. Currently, one of node, revision_revisions,
* users, vocabulary or term_data.
* @param $key
* The name of the serial ID column.
* @param $id
* The serial ID of the object.
* @return
* The UUID of the object, or FALSE if not found.
*/
function uuid_get_uuid($table, $key, $id) {
$uuid_table = 'uuid_' . $table;
return db_result(db_query("SELECT uuid FROM {{$uuid_table}} WHERE {$key} = %d", $id));
}
}
/**
* Returns the node associated with a UUID. Has the same functionality as
* node_get_by_uuid() in uuid.module but does NOT uses db_rewrite_sql.
*
* @return
* Either the $node object, nid, or FALSE on failure.
*/
function node_export_node_get_by_uuid($uuid, $nid_only = FALSE) {
$nid = db_result(db_query("SELECT n.nid\n FROM {node} AS n\n INNER JOIN {uuid_node} AS un ON n.nid = un.nid\n WHERE un.uuid = '%s'", $uuid));
if ($nid && !$nid_only) {
return node_load($nid);
}
else {
if ($nid) {
return $nid;
}
else {
return FALSE;
}
}
}
Functions
Name![]() |
Description |
---|---|
node_export | Export nodes. |
node_export_access_export | Check access to export a node. |
node_export_access_export_nodes | Check access to export an array of nodes. |
node_export_access_import | Check access to import a node. |
node_export_access_import_nodes | Check access to import an array of nodes. |
node_export_bulk_operation | Callback for use with hook_node_operations(). |
node_export_format_handlers | Get a list of possible format handlers (other than the default). |
node_export_get_menu | Create a new menu entry with title, parent and weight exported from another nodes menu. Returns NULL if the node has no menu title. |
node_export_import | Import Function |
node_export_import_types_check | Check if all types in the import exist. |
node_export_menu | Implementation of hook_menu(). |
node_export_node_clone | Generates a pristine cloned node such that it is ready for import. Note, the node_export_features module uses this function to generate cleaner exports. |
node_export_node_get_by_uuid | Returns the node associated with a UUID. Has the same functionality as node_get_by_uuid() in uuid.module but does NOT uses db_rewrite_sql. |
node_export_node_operations | Implementation of hook_node_operations(). |
node_export_node_type | Implementation of hook_node_type(). |
node_export_perm | Implementation of hook_perm(). |
node_export_prepare_node | Prepare a single node during export. |
node_export_remove_recursion | Remove recursion problem from an object or array. |
node_export_restore_recursion | Restore recursion to an object or array. |
node_export_save | Save a node object into the database. |
node_export_views_api | Implementation of hook_views_api(). |