' . t("This module allows to convert one or many nodes between different node types. It can transfer most fields, and node-specific options for book and forum types. Support of more basic types will be in future releases. Also the module provides an API for converting nodes and fields, hooks for processing additional options of custom node types, integrates with hook_node_operations and Drupal's Action API.") . '
';
$output .= '' . t('I. Single node conversion:') . '
';
$output .= t("- Go to permissions page and set 'administer conversion' and 'convert to x', 'convert from y' permissions.
- Go to /node/x/convert and follow the provided steps to convert the node.
", array('@permissions' => url('admin/user/permissions')));
$output .= '' . t('II. Multiple node conversion (using hook_node_operations):') . '
';
$output .= t('- Set appropriate permissions.
- Go to Node Convert templates.
- Create a new template following the the provided steps.
- Go to the content page.
- Select the correct nodes.
- Choose "Convert template: x" (based on the template name created above) from the update options.
- Click Update.
', array('@node_convert_template' => url('admin/structure/node_convert_template'), '@content' => url('admin/content/node')));
$output .= '' . t('III. Multiple node conversion (using Actions API + Views Bulk Operations):
Note: This requires the contributed modules Views and Views Bulk Operations') . '
';
$output .= t('- Set appropriate permissions.
- Go to Node Convert templates.
- Create a new template following the the provided steps (also check Create Action).
- Create a new view with the options you require.
- Select Views Bulk Operations as the style.
- Configure all options as necessary
- Select as an operation one of the convert templates.
Note: Most probably there will be duplicates of the same template, this is because VBO uses both Actions API and hook_node_operations to show possible operations - Save the view. View it.
- Select the necessary nodes and click the Convert x button.
', array('@node_convert_template' => url('admin/structure/node_convert_template'), '@view' => url('admin/structure/views')));
$output .= '' . t('Useful API calls:
node_convert_node_convert($nid, $dest_node_type, $source_fields, $dest_fields, $no_fields_flag, $hook_options = NULL);
node_convert_field_convert($nid, $source_field, $dest_field);
hook_node_convert_change($data, $op);') . '
';
return $output;
}
}
/**
* Implements hook_init().
*/
function node_convert_init() {
// TODO Fix loading of includes that implement hook_node_convert_change.
/*foreach (array('book', 'forum', 'uc_product', 'panels_node') as $module) {
if (module_exists($module)) {
module_load_include('node_convert.inc', 'node_convert', 'includes/' . $module);
}
}*/
}
/**
* Implements hook_permission().
*/
function node_convert_permission() {
$types = node_type_get_types();
$permissions = array(
'administer conversion' => array(
'title' => t('administer conversion'),
'description' => t('Grants full permissions for converting between types.'),
),
);
foreach ($types as $type => $parameters) {
$permissions['convert from ' . $type] = array(
'title' => t('convert from ' . $type),
'description' => t('Grants pemission for conversion from @type node types.', array('@type' => $type)),
);
$permissions['convert to ' . $type] = array(
'title' => t('convert to ' . $type),
'description' => t('Grants pemission for conversion to @type node types.', array('@type' => $type)),
);
}
return $permissions;
}
/**
* Implements hook_menu().
*/
function node_convert_menu() {
$items = array();
$items['node/%node/convert'] = array(
'title' => 'Convert',
'page callback' => 'drupal_get_form',
'page arguments' => array('node_convert_conversion_form', 1),
'access callback' => 'node_convert_check_access',
'access arguments' => array(1),
'weight' => 6,
'type' => MENU_LOCAL_TASK,
);
$items['admin/structure/node_convert_templates'] = array(
'title' => 'Node Convert templates',
'description' => 'List of templates used for converting nodes using Actions and Node Operations.',
'page callback' => 'node_convert_templates',
'access arguments' => array('administer conversion'),
'type' => MENU_NORMAL_ITEM,
);
$items['admin/structure/node_convert_templates/list'] = array(
'title' => 'List',
'access arguments' => array('administer conversion'),
'type' => MENU_DEFAULT_LOCAL_TASK,
);
$items['admin/structure/node_convert_templates/add'] = array(
'title' => 'Add template',
'page callback' => 'drupal_get_form',
'page arguments' => array('node_convert_add_template'),
'access arguments' => array('administer conversion'),
'type' => MENU_LOCAL_TASK,
'weight' => 1,
);
$items['admin/structure/node_convert_templates/%'] = array(
'title' => 'Template info',
'page callback' => 'node_convert_template_info',
'page arguments' => array(3),
'access arguments' => array('administer conversion'),
'type' => MENU_CALLBACK,
);
$items['admin/structure/node_convert_templates/delete/%'] = array(
'title' => 'Delete template',
'page callback' => 'drupal_get_form',
'page arguments' => array('node_convert_template_delete_confirm', 4),
'access arguments' => array('administer conversion'),
'type' => MENU_CALLBACK,
);
return $items;
}
/**
* Implements hook_node_operations().
*/
function node_convert_node_operations() {
$operations = array();
$result = db_query("SELECT name, nctid FROM {node_convert_templates}");
foreach ($result as $row) {
$access = node_convert_check_template_permission_user(array('template_id' => $row->nctid));
if ($access) {
$operations['node_convert_' . $row->nctid] = array(
'label' => 'Convert template: ' . $row->name,
'callback' => 'node_convert_convert_nodes_using_template',
'callback arguments' => array($row->nctid),
);
}
}
return $operations;
}
/**
* Implements hook_action_info().
*/
function node_convert_action_info() {
return array(
'node_convert_convert_action' => array(
'label' => t("Convert a node"),
'type' => 'node',
'configurable' => TRUE,
'triggers' => array('any' => TRUE),
),
);
}
/**
* Implements hook_theme().
*/
function node_convert_theme() {
return array(
'node_convert_conversion_form' => array(
'render element' => 'form',
),
'node_convert_add_template' => array(
'render element' => 'form',
),
);
}
/* ----------- The forms ------------ */
/**
* @todo Please document this function.
* @see http://drupal.org/node/1354
*/
function node_convert_conversion_form($form, $form_state, $node) {
$form = array();
/* Setting the steps */
if (!isset($form_state['values']['step'])) {
$op = 'choose_destination_type';
}
elseif ($form_state['values']['step'] == 'choose_destination_type') {
$op = 'choose_destination_fields';
}
$form['step'] = array(
'#type' => 'value',
'#value' => $op,
);
$form['node'] = array(
'#type' => 'value',
'#value' => $node,
);
/* Form steps */
if ($op == 'choose_destination_type') {
$type = node_type_get_name($node);
// Remember current node type, used in theme_ function
$form['current_type'] = array(
'#markup' => $type,
);
// Get available content types
$types = node_convert_return_access_node_types('to');
if ($types != FALSE) {
$key = array_search($form['current_type']['#markup'], $types);
// Delete the current content type from the list
if ($key !== FALSE) {
unset($types[$key]);
}
$options = $types;
// Populate the select with possible content types
$form['destination_type'] = array(
'#type' => 'select',
'#options' => $options,
'#title' => t("To what content type should this node be converted"),
);
}
else {
// Just used as a message, not sure if it's the best implementation
$form['destination_type'] = array(
'#markup' => t("You don't have access to any node types."),
);
}
}
elseif ($op == 'choose_destination_fields') {
// $source_fields = content_types($node->type);
// Get source type fields
$source_fields = field_info_instances('node', $node->type);
$fields_info = field_info_fields();
// In case there are no fields, just convert the node type
if (count($source_fields) == 0) {
$no_fields = TRUE;
}
// Otherwise
else {
$no_fields = FALSE;
// Get destination type fields
$dest_fields = field_info_instances('node', $form_state['storage']['destination_type']);
$i = 0;
foreach ($source_fields as $source_field_name => $source_field) {
$i++;
$options = array();
$options['discard'] = 'discard';
$options[APPEND_TO_BODY] = t('Append to body');
$options[REPLACE_BODY] = t('Replace body');
// Insert destination type fields into $options that are of the same type as the source.
foreach ($dest_fields as $dest_field_name => $dest_value) {
if ($fields_info[$source_field_name]['type'] == $fields_info[$dest_field_name]['type']) {
$options[$dest_value['field_name']] = $dest_value['field_name'];
}
}
// Remember the source fields to be converted
$form['source_field_' . $i] = array(
'#type' => 'value',
'#value' => $source_field['field_name'],
);
// The select populated with possible destination fields for each source field
// If the destination node type has the same field as the source node type, the default value is set to it.
$form['dest_field_' . $i] = array(
'#type' => 'select',
'#options' => $options,
'#default_value' => $source_field['field_name'],
'#title' => $source_field['field_name'] . " " . t("should be inserted into"),
);
// Print the current value of the source field
$temp_value = node_convert_get_field_value($node, $fields_info[$source_field_name]);
$form['current_field_value_' . $i] = array(
'#markup' => '' . t("Current value is:") . " " . $temp_value . '
',
);
}
$form['number_of_fields'] = array(
'#type' => 'value',
'#value' => $i,
);
}
$form['no_fields'] = array(
'#type' => 'value',
'#value' => $no_fields,
);
$hook_options = module_invoke_all('node_convert_change', array('dest_node_type' => $form_state['storage']['destination_type']), 'options');
if (!empty($hook_options)) {
$form['hook_options'] = $hook_options;
array_unshift($form['hook_options'], array('#value' => '
' . t("Also the following parameters are available:") . ''));
$form['hook_options']['#tree'] = TRUE;
}
}
if ($op != 'choose_destination_fields' && isset($types) && $types != FALSE) {
$form['submit'] = array(
'#type' => 'submit',
'#value' => t("Next"),
);
}
elseif ($op == 'choose_destination_fields') {
$form['submit'] = array(
'#type' => 'submit',
'#value' => t("Convert"),
'#weight' => 100,
);
}
return $form;
}
/**
* @todo Please document this function.
* @see http://drupal.org/node/1354
*/
function node_convert_conversion_form_validate($form, &$form_state) {
if ($form_state['values']['step'] == 'choose_destination_fields') {
module_invoke_all('node_convert_change', array('dest_node_type' => $form_state['storage']['destination_type'], 'form_state' => $form_state), 'options validate');
}
}
/**
* @todo Please document this function.
* @see http://drupal.org/node/1354
*/
function node_convert_conversion_form_submit($form, &$form_state) {
// Remember the destination type
if ($form_state['values']['step'] == 'choose_destination_type') {
$form_state['rebuild'] = TRUE;
$form_state['storage']['destination_type'] = $form_state['values']['destination_type'];
}
elseif ($form_state['values']['step'] == 'choose_destination_fields') {
// Information needed in the convert process: nid, vid, source type, destination type
$dest_node_type = $form_state['storage']['destination_type'];
$node = $form_state['values']['node'];
$nid = $node->nid;
$no_fields = $form_state['values']['no_fields'];
$number_of_fields = $form_state['values']['number_of_fields'];
// If there are fields that can to be converted
if ($no_fields == FALSE) {
for ($i = 1; $i <= $number_of_fields; $i++) {
$source_fields[] = $form_state['values']['source_field_' . $i]; // Source fields
$dest_fields[] = $form_state['values']['dest_field_' . $i]; // Destination fields
}
}
if (!empty($form['hook_options'])) {
$hook_options = $form_state['values']['hook_options'];
}
else {
$hook_options = NULL;
}
$result = node_convert_node_convert($nid, $dest_node_type, $source_fields, $dest_fields, $no_fields, $hook_options);
// We display errors if any, or the default success message.
node_convert_messages($result, array('nid' => $nid));
// We clear the storage so redirect works
$form_state['storage'] = array();
$form_state['redirect'] = "node/" . $nid;
}
}
/**
* @todo Please document this function.
* @see http://drupal.org/node/1354
*/
function theme_node_convert_conversion_form($variables) {
$form = $variables['form'];
$output = '';
if (isset($form['current_type'])) {
$output .= '' . t("The current node type is:") . ' ' . drupal_render($form['current_type']) . '
';
}
// If there are no fields to convert, we notify the user
if (isset($form['no_fields']['#value']) && $form['no_fields']['#value'] == TRUE) {
$output .= '' . t("There are no fields to convert. Please press Convert.") . '
';
}
$output .= drupal_render_children($form);
return $output;
}
/**
* @todo Please document this function.
* @see http://drupal.org/node/1354
*/
function node_convert_add_template($form, $form_state) {
$form = array();
/* Setting the steps */
if (!isset($form_state['values']['step'])) {
$op = 'choose_destination_type';
}
elseif ($form_state['values']['step'] == 'choose_destination_type') {
$op = 'choose_destination_fields';
}
$form['step'] = array(
'#type' => 'value',
'#value' => $op,
);
if ($op == 'choose_destination_type') {
// Get available content types
$to_types = node_convert_return_access_node_types('to');
$from_types = node_convert_return_access_node_types('from');
if ($to_types != FALSE && $from_types != FALSE) {
$form['template_name'] = array(
'#type' => 'textfield',
'#title' => t("Template name"),
'#required' => TRUE,
);
$form['source_type'] = array(
'#type' => 'select',
'#title' => t("Source type"),
'#options' => $from_types,
);
$form['dest_type'] = array(
'#type' => 'select',
'#title' => t("Destination type"),
'#options' => $to_types,
);
$form['create_action'] = array(
'#type' => 'checkbox',
'#title' => t("Create action?"),
'#description' => t("If the option is checked, an action named Convert *Template name* will be created."),
);
}
else {
$form['no_types'] = array(
'#type' => 'markup',
'#value' => t("You don't have access to any node types."),
);
}
}
elseif ($op == 'choose_destination_fields') {
// Get the fields of the source type
$source_fields = field_info_instances('node', $form_state['storage']['source_type']);
$fields_info = field_info_fields();
// In case there are no fields, just convert the node type
if (count($source_fields) == 0) {
$no_fields = TRUE;
}
else {
$no_fields = FALSE;
// Get the destination type fields
$dest_fields = field_info_instances('node', $form_state['storage']['dest_type']);
$i = 0;
foreach ($source_fields as $source_field_name => $source_field) {
$i++;
$options = array();
$options['discard'] = 'discard';
$options[APPEND_TO_BODY] = t('Append to body');
$options[REPLACE_BODY] = t('Replace body');
// Insert destination type fields into $options that are of the same type as the source.
foreach ($dest_fields as $dest_field_name => $dest_field) {
if ($fields_info[$source_field_name]['type'] == $fields_info[$dest_field_name]['type']) {
$options[$dest_field['field_name']] = $dest_field['field_name'];
}
}
// Remember the source fields to be converted
$form['source_field_' . $i] = array(
'#type' => 'value',
'#value' => $source_field['field_name'],
);
// The select populated with possible destination fields for each source field
$form['dest_field_' . $i] = array(
'#type' => 'select',
'#options' => $options,
'#title' => $source_field['field_name'] . " " . t("should be inserted into"),
);
}
$form['number_of_fields'] = array(
'#type' => 'value',
'#value' => $i,
);
}
$form['no_fields'] = array(
'#type' => 'value',
'#value' => $no_fields,
);
// All node specific form options needed for types like book, forum, etc. are done here
$hook_options = module_invoke_all('node_convert_change', array('dest_node_type' => $form_state['storage']['dest_type']), 'options');
if (!empty($hook_options)) {
$form['hook_options'] = $hook_options;
array_unshift($form['hook_options'], array('#value' => '' . t("Also the following parameters are available:") . ''));
$form['hook_options']['#tree'] = TRUE;
}
}
if ($op == 'choose_destination_type' && $to_types != FALSE && $from_types != FALSE) {
$form['submit'] = array(
'#type' => 'submit',
'#value' => t("Next"),
);
}
elseif ($op == "choose_destination_fields") {
$form['submit'] = array(
'#type' => 'submit',
'#value' => t("Create"),
'#weight' => 100,
);
}
return $form;
}
/**
* @todo Please document this function.
* @see http://drupal.org/node/1354
*/
function node_convert_add_template_validate($form, &$form_state) {
if ($form_state['values']['step'] == 'choose_destination_type') {
if ($form_state['values']['source_type'] == $form_state['values']['dest_type']) {
form_set_error('source_type', t('Please select different node types.'));
form_set_error('dest_type', t('Please select different node types.'));
}
}
// All node specific form validations needed for types like book, forum, etc. are done here
elseif ($form_state['values']['step'] == 'choose_destination_fields') {
module_invoke_all('node_convert_change', array('dest_node_type' => $form_state['storage']['dest_type'], 'form_state' => $form_state), 'options validate');
}
}
/**
* @todo Please document this function.
* @see http://drupal.org/node/1354
*/
function node_convert_add_template_submit($form, &$form_state) {
if ($form_state['values']['step'] == 'choose_destination_type') {
$form_state['rebuild'] = TRUE;
$form_state['storage']['template_name'] = $form_state['values']['template_name'];
$form_state['storage']['source_type'] = $form_state['values']['source_type'];
$form_state['storage']['dest_type'] = $form_state['values']['dest_type'];
$form_state['storage']['create_action'] = $form_state['values']['create_action'];
}
elseif ($form_state['values']['step'] == 'choose_destination_fields') {
$no_fields = $form_state['values']['no_fields'];
// If there are fields that can to be converted
if ($no_fields == FALSE) {
for ($i = 1; $i <= $form_state['values']['number_of_fields']; $i++) {
$source_fields[] = $form_state['values']['source_field_' . $i]; // Source fields
$dest_fields[] = $form_state['values']['dest_field_' . $i]; // Destination fields
}
}
if (!empty($form['hook_options'])) {
$hook_options = $form_state['values']['hook_options'];
}
else {
$hook_options = NULL;
}
$fields = array(
'source' => $source_fields,
'destination' => $dest_fields,
);
$data = array(
'fields' => $fields,
'hook_options' => $hook_options,
'no_fields' => $no_fields,
);
$data = serialize($data);
$id = db_insert('node_convert_templates')
->fields(array(
'name' => $form_state['storage']['template_name'],
'source_type' => $form_state['storage']['source_type'],
'destination_type' => $form_state['storage']['dest_type'],
'data' => $data,
))
->execute();
drupal_set_message(t("Template created successfully."));
if ($form_state['storage']['create_action'] == 1) {
$template_id = $id;
actions_save('node_convert_convert_action', 'node', array('template' => $template_id), 'Convert ' . $form_state['storage']['template_name'], NULL);
}
// We clear the storage so redirect works
$form_state['storage'] = array();
$form_state['redirect'] = 'admin/structure/node_convert_templates';
}
}
/**
* @todo Please document this function.
* @see http://drupal.org/node/1354
*/
function theme_node_convert_add_template($variables) {
$form = $variables['form'];
if ($form['step']['#value'] == "choose_destination_type") {
$output = '' . t("Choose the source type of the nodes that should be shown, and the destination type to which they will be converted.") . '
';
$output .= drupal_render_children($form);
}
elseif ($form['step']['#value'] == "choose_destination_fields") {
$output = '';
if ($form['no_fields']['#value'] == TRUE) {
$output .= '' . t("There are no fields to convert. Please press Create.") . '
';
}
$output .= drupal_render_children($form);
}
return $output;
}
/* ------------- Template adding, viewing, deleting. ----------------- */
/**
* @todo Please document this function.
* @see http://drupal.org/node/1354
*/
function node_convert_templates() {
$output = '';
$rows = array();
$headers = array(t("Name"), t("Source type"), t("Dest type"), t("Delete"));
$result = db_query("SELECT * FROM {node_convert_templates} ORDER BY nctid");
foreach ($result as $row) {
$rows[$row->nctid] = array(l($row->name, 'admin/structure/node_convert_templates/' . $row->nctid), $row->source_type, $row->destination_type, l(t("Delete"), 'admin/structure/node_convert_templates/delete/' . $row->nctid));
}
$output = theme('table', array('header' => $headers, 'rows' => $rows));
return $output;
}
/**
* @todo Please document this function.
* @see http://drupal.org/node/1354
*/
function node_convert_template_info($template_id) {
$output = '';
$rows = array();
$headers = array(t("Property"), t("Value"));
$row = node_convert_load_template($template_id);
$rows[] = array(t("Template id"), $row['nctid']);
$rows[] = array(t("Name"), $row['name']);
$rows[] = array(t("Source type"), $row['source_type']);
$rows[] = array(t("Destination type"), $row['destination_type']);
$data = $row['data'];
if ($data['no_fields'] == FALSE) {
$source_fields_string = implode(', ', $data['fields']['source']);
$dest_fields_string = implode(', ', $data['fields']['destination']);
$rows[] = array(t("Source fields"), $source_fields_string);
$rows[] = array(t("Destination fields"), $dest_fields_string);
}
if (!empty($data['hook_options'])) {
$rows[] = array(t("Hook options"), print_r($data['hook_options'], TRUE));
}
$output .= theme('table', array('header' => $headers, 'rows' => $rows));
return $output;
}
/**
* @todo Please document this function.
* @see http://drupal.org/node/1354
*/
function node_convert_template_delete_confirm($form, &$form_state, $template_id) {
$form['template_id'] = array(
'#type' => 'value',
'#value' => $template_id,
);
$form['delete_action'] = array(
'#type' => 'checkbox',
'#title' => t("Delete action?"),
'#default_value' => 1,
'#description' => t("If the option is checked, all actions that contain this template will be erased. Otheriwise, the actions' template will be set to none."),
);
return confirm_form($form,
t('Are you sure you want to delete this template?'),
isset($_GET['destination']) ? $_GET['destination'] : 'admin/structure/node_convert_templates',
t('This action cannot be undone.'),
t('Delete'),
t('Cancel')
);
}
/**
* @todo Please document this function.
* @see http://drupal.org/node/1354
*/
function node_convert_template_delete_confirm_submit($form, &$form_state) {
if ($form_state['values']['confirm']) {
db_delete('node_convert_templates')
->condition('nctid', $form_state['values']['template_id'])
->execute();
if ($form_state['values']['delete_action'] == 1) {
db_delete('actions')
->condition('callback', 'node_convert_convert_action')
->condition('parameters', '%template";s:%:"' . $form_state['values']['template_id'] . '%', 'LIKE')
->execute();
}
else {
$none = serialize(array('template' => '0'));
db_update('actions')
->fields(array(
'parameters' => $none,
))
->condition('callback', 'node_convert_convert_action')
->condition('parameters', '%template";s:%:"' . $form_state['values']['template_id'] . '%', 'LIKE')
->execute();
}
}
$form_state['redirect'] = 'admin/structure/node_convert_templates';
}
/* The node_convert action */
/**
* @todo Please document this function.
* @see http://drupal.org/node/1354
*/
function node_convert_convert_action(&$node, &$context = array()) {
if ($context['template'] === "0") {
drupal_set_message(t("Dummy template. No action is being performed."), 'warning', FALSE);
return FALSE;
}
$template = node_convert_load_template($context['template']);
$access = node_convert_check_template_permission_user(array('template' => $template));
if ($access == FALSE) {
drupal_set_message(t("You don't have permission to use this conversion template"), 'warning', FALSE);
return;
}
if ($node->type != $template['source_type']) {
drupal_set_message(t("Node %nid doesn't match the template source type. Discarded.", array('nid' => $node->nid)), 'warning');
}
else {
$result = node_convert_node_convert($node->nid, $template['destination_type'], $template['data']['fields']['source'], $template['data']['fields']['destination'], $template['data']['no_fields'], $template['data']['hook_options']);
// We display errors if any, or the default success message
node_convert_messages($result, array('nid' => $node->nid));
// This is node_load is necessary. It loads the new data from the DB, which gets passed down the action chain by reference, where it is saved.
$node = node_load($node->nid, NULL, TRUE);
}
}
/**
* @todo Please document this function.
* @see http://drupal.org/node/1354
*/
function node_convert_convert_action_form($context) {
$result = db_query("SELECT * FROM {node_convert_templates}");
$templates = array(0 => 'none');
foreach ($result as $row) {
$access = node_convert_check_template_permission_user(array('template_id' => $row->nctid));
if ($access == TRUE) {
$templates[$row->nctid] = $row->name;
}
}
if (isset($context['template'])) {
$default_template = $context['template'];
}
$form['template'] = array(
'#type' => 'select',
'#title' => t("Template"),
'#description' => t("Convesion template to use when the action is fired."),
'#options' => $templates,
'#default_value' => isset($default_template) ? $default_template : '',
);
return $form;
}
/**
* @todo Please document this function.
* @see http://drupal.org/node/1354
*/
function node_convert_convert_action_submit($form, &$form_state) {
return array('template' => $form_state['values']['template']);
}
/* ----------------------- API --------------------- */
/**
* Converts a node to another content type.
*
* @param $nid
* The nid of the node to be converted.
* @param $dest_node_type
* A string containing the destination node type of the node.
* @param $source_fields
* An array containing the source field names.
* @param $dest_fields
* An array containing the destination field names.
* @param $no_fields_flag
* A boolean containing if there are source fields that have to be converted.
* @param $hook_options
* An array containing values used by the hook_node_convert_change functions.
* @return bool Returns the $nid.
*/
function node_convert_node_convert($nid, $dest_node_type, $source_fields, $dest_fields, $no_fields_flag, $hook_options = NULL) {
$node = node_load($nid);
if ($node == FALSE) {
return FALSE;
}
// Change the node type in the DB
db_update('node')->fields(array('type' => $dest_node_type,))->condition('nid', $nid)->execute();
// If there are fields that can be converted
if ($no_fields_flag == FALSE) {
// Conversion process for each field
$resave_body_field = FALSE;
foreach ($source_fields as $key => $field) {
$replaced_body = node_convert_field_convert($node, $field, $dest_fields[$key]);
if ($replaced_body == REPLACE_BODY) {
$resave_body_field = TRUE;
}
}
// If something was appended to the body, or replaced the body, we update body field.
if ($resave_body_field == TRUE) {
$field_body = field_info_fields();
$field_body = $field_body['body'];
$field_ids = array($field_body['id'] => $field_body['id']);
module_invoke($field_body['storage']['module'], 'field_storage_write', 'node', $node, FIELD_STORAGE_UPDATE, $field_ids);
}
}
// We collate data to send to the hook implementations
$data = array(
'node' => $node,
'dest_node_type' => $dest_node_type,
);
if (!empty($hook_options)) {
$data['hook_options'] = $hook_options;
}
// We make sure that all custom node modules do their changes at the appropriate steps
module_invoke_all('node_convert_change', $data, 'insert');
module_invoke_all('node_convert_change', $data, 'delete');
// TODO Check what to do about token cache clearing.
/*if (module_exists('token')) {
token_get_values('global', NULL, TRUE);
}*/
cache_clear_all('field:node:' . $node->nid, 'cache_field');
$converted_node = node_load($nid, NULL, TRUE);
$converted_node->type = $dest_node_type;
try {
node_save($converted_node);
}
catch (Exception $e) {
drupal_set_message(t('Caught exception: @exception', array('@exception' => $e->getMessage())), 'error');
}
if (module_exists('rules')) {
rules_invoke_event('node_convert_converted_node', $converted_node);
}
return $nid;
}
/**
* Transfers information from source_field to dest_field.
*
* @param $node
* The node object to be converted.
* @param $source_field
* A string containing the name of the source field which contains to be transferred information.
* @param $dest_field
* A string containing the name of the destination field where the information should be stored.
* @return
* Returns REPLACE_BODY if something replaced the body field, or was appended to it.
*/
function node_convert_field_convert(&$node, $source_field, $dest_field) {
$field_info_source = field_info_fields(); // Get source field information
$field_info_source = $field_info_source[$source_field];
$db_info_source = $field_info_source['storage']; // Get DB specific source field information
if ($dest_field == 'discard') {
// Delete node info in the separate field table
$field_id = $field_info_source['id'];
module_invoke($db_info_source['module'], 'field_storage_delete', 'node', $node, array($field_id => $field_id));
return;
}
if (!in_array($dest_field, array('discard', APPEND_TO_BODY, REPLACE_BODY))) {
$field_info_dest = field_info_fields($dest_field); // Get destination field information
$field_info_dest = $field_info_dest[$dest_field]; // Get destination field information
$db_info_dest = $field_info_dest['storage']; // Get DB specific destination field information
}
$source_values = array();
// We save each field value from the DB for transfer.
$source_values = field_get_items('node', $node, $source_field);
// After getting the source field values, we delete them in the DB
$field_id = $field_info_source['id'];
module_invoke($db_info_source['module'], 'field_storage_delete', 'node', $node, array($field_id => $field_id));
// The source field value should be appended to the body or replaced.
if ($dest_field == APPEND_TO_BODY || $dest_field == REPLACE_BODY) {
static $node_body = '';
//static $node_teaser = '';
if (empty($node_body)) {
$node_body_field = field_get_items('node', $node, 'body');
$node_body = $node_body_field[0]['value'];
//$node_teaser = $node_body_field[0]['summary'];
}
if (is_array($source_values)) {
foreach ($source_values as $field_value) {
if ($dest_field == APPEND_TO_BODY) {
$node_body = $node_body . "\n" . $field_value['value'];
//$node_teaser = $node_teaser . "\n" . $field_value['value'];
}
elseif ($dest_field == REPLACE_BODY) {
$node_body = $field_value['value'];
//$node_teaser = $field_value['value'];
}
}
$langcode = field_language('node', $node, $source_field);
$node->body[$langcode][0]['value'] = $node_body;
//$node->body[$langcode][0]['summary'] = $node_teaser;
}
return REPLACE_BODY;
}
// We put each field value back into the DB
// To do it we first get the id of the field, then we find its language code from the source value
// We add $source_values into the node object, and invoke field_storage write
$field_ids = array($field_info_dest['id'] => $field_info_dest['id']);
$langcode = field_language('node', $node, $source_field);
// Make sure that we actually have values in the source field
if($source_values !== FALSE) {
$node->{$dest_field}[$langcode] = $source_values;
} else {
$node->{$dest_field} = array();
}
// Give possibility to fields to pre-process their data
// (e.g. Link module transforms attribute array into a serialized array before insertion)
field_attach_presave('node', $node);
try {
module_invoke($db_info_dest['module'], 'field_storage_write', 'node', $node, FIELD_STORAGE_INSERT, $field_ids);
} catch (Exception $e) {
drupal_set_message(t('Caught exception: @exception', array('@exception' => $e->getMessage())));
}
}
/**
* Displays error messages if any occurred, otherwise the success message.
*
* @param $result
* The result value of the node conversion. Possible values
* - FALSE Displays an error message.
* - Any other Displays success message.
* @param $params
* An array containing message parameters. Possible values
* - display_success If TRUE, the success message will be displayed, otherwise no message is displayed.
* Default is TRUE.
*/
function node_convert_messages($result, $params = array()) {
$params += array('display_success' => TRUE);
if ($result == FALSE) {
drupal_set_message(t("Conversion failed. Node nid @nid doesn't exist.", array('@nid' => $params['nid'])), 'error');
}
elseif ($params['display_success'] == TRUE) {
drupal_set_message(t("Node @nid has been converted successfully.", array('@nid' => $params['nid'])));
}
}
/**
* This is an example implementation for the hook. Preforms actions when converting a node based on it's type.
*
* @param $data
* An array containing information about the conversion process. The keys are
* - dest_node_type The destination type of the node
* - node The node object
* - Any other information passed by $op = 'options' or $op = 'options validate'
* @param $op
* A string containg the operation which should be executed. These are the possible values
* - insert Operations which should be run when the node is transferred to the new node type.
* Usually for transferring and adding new node information into the database.
* - delete Operations which should be run after the node is transferred to the new node type.
* Usually for deleting unneeded information from the database after the transfer.
* - options Configuration elements shown on the conversion form. Should return a FAPI array.
* - options validate Validation check on the options elements.
* @return
* Should return a FAPI array only when using the options operation.
*/
function hook_node_convert_change($data, $op) {
// All of this is just an example, there real data is being called from hook_init
if ($op == 'insert') {
if ($data['dest_node_type'] == 'book') {
$book = array();
$node = $data['node'];
$book['link_path'] = 'node/' . $node->nid;
$book['link_title'] = $node->title;
$book['plid'] = 0;
$book['menu_name'] = book_menu_name($node->nid);
$mlid = menu_link_save($book);
$book['bid'] = $data['hook_options']['bid'];
if ($book['bid'] == 'self') {
$book['bid'] = $node->nid;
}
// TODO Please review the conversion of this statement to the D7 database API syntax.
/* db_query("INSERT INTO {book} (nid, mlid, bid) VALUES (%d, %d, %d)", $node->nid, $book['mlid'], $book['bid']) */
$id = db_insert('book')
->fields(array(
'nid' => $node->nid,
'mlid' => $book['mlid'],
'bid' => $book['bid'],
))
->execute();
}
if ($data['dest_node_type'] == 'forum') {
// TODO Please review the conversion of this statement to the D7 database API syntax.
/* db_query('INSERT INTO {forum} (tid, vid, nid) VALUES (%d, %d, %d)', $data['hook_options']['forum'], $data['node']->vid, $data['node']->nid) */
$id = db_insert('forum')
->fields(array(
'tid' => $data['hook_options']['forum'],
'vid' => $data['node']->vid,
'nid' => $data['node']->nid,
))
->execute();
// TODO Please review the conversion of this statement to the D7 database API syntax.
/* db_query('INSERT INTO {taxonomy_term_node} (tid, vid, nid) VALUES (%d, %d, %d)', $data['hook_options']['forum'], $data['node']->vid, $data['node']->nid) */
$id = db_insert('taxonomy_term_node')
->fields(array(
'tid' => $data['hook_options']['forum'],
'vid' => $data['node']->vid,
'nid' => $data['node']->nid,
))
->execute();
}
}
elseif ($op == 'delete') {
if ($data['node']->type == 'book') {
menu_link_delete($data['node']->book['mlid']);
// TODO Please review the conversion of this statement to the D7 database API syntax.
/* db_query('DELETE FROM {book} WHERE mlid = %d', $data['node']->book['mlid']) */
db_delete('book')
->condition('mlid', $data['node']->book['mlid'])
->execute();
}
if ($data['node']->type == 'forum') {
// TODO Please review the conversion of this statement to the D7 database API syntax.
/* db_query('DELETE FROM {forum} WHERE nid = %d', $data['node']->nid) */
db_delete('forum')
->condition('nid', $data['node']->nid)
->execute();
// TODO Please review the conversion of this statement to the D7 database API syntax.
/* db_query('DELETE FROM {taxonomy_term_node} WHERE nid = %d', $data['node']->nid) */
db_delete('taxonomy_term_node')
->condition('nid', $data['node']->nid)
->execute();
}
}
elseif ($op == 'options') {
$form = array();
if ($data['dest_node_type'] == 'book') {
foreach (book_get_books() as $book) {
$options[$book['nid']] = $book['title'];
}
$options = array('self' => '<' . t('create a new book') . '>') + $options;
$form['bid'] = array(
'#type' => 'select',
'#title' => t('Book'),
'#options' => $options,
'#description' => t('Your page will be a part of the selected book.'),
'#attributes' => array('class' => 'book-title-select'),
);
}
if ($data['dest_node_type'] == 'forum') {
$vid = variable_get('forum_nav_vocabulary', '');
$form['forum'] = taxonomy_form($vid);
$form['forum']['#weight'] = 7;
$form['forum']['#required'] = TRUE;
$form['forum']['#options'][''] = t('- Please choose -');
}
return $form;
}
elseif ($op == 'options validate') {
$form_state = $data['form_state'];
if ($data['dest_node_type'] == 'forum') {
$containers = variable_get('forum_containers', array());
$term = $form_state['values']['hook_options']['forum'];
if (in_array($term, $containers)) {
$term = taxonomy_term_load($term);
form_set_error('hook_options][forum', t('The item %forum is only a container for forums. Please select one of the forums below it.', array('%forum' => $term->name)));
}
}
}
}
/**
* Checks if user can do conversions from this node's type.
*
* @param $node
* A node object to be checked.
* @return
* TRUE if user can make conversions using this type, FALSE otherwise.
*/
function node_convert_check_access($node) {
$access = (user_access('administer conversion') || user_access('convert from ' . $node->type)) ? TRUE : FALSE;
return $access;
}
/**
* Returns a list of node types that the user has access to, depending on the direction of conversion.
*
* @param $direction
* A string containing either 'to' or 'from'.
* @return
* An array of node types or FALSE if the user doesn't have access to any node type.
*/
function node_convert_return_access_node_types($direction) {
$list = array();
$types = node_type_get_types();
foreach ($types as $type => $parameters) {
if ((user_access('administer conversion')) || (user_access('convert ' . $direction . ' ' . $type))) {
$list[$type] = $parameters->name;
}
}
if (!empty($list)) {
return $list;
}
else {
return FALSE;
}
}
/**
* Returns a string containing the value of the $field from the $node object.
*
* @param $node
* A $node object
* @param $field
* The field who's value to get.
* @return
* A string containing the value of the $field from the $node object.
*/
function node_convert_get_field_value($node, $field) {
$value = '';
$field_values = field_get_items('node', $node, $field['field_name']);
if ($field['type'] == "image") {
$value = $field_values[0]['title'] . " ; " . $field_values[0]['uri'];
}
elseif ($field['type'] == "link_field") {
$value = $field_values[0]['url'] . " ; " . $field_values[0]['title'];
}
/*elseif ($field['type'] == "email") {
$value = $field_values[0]['email'];
}
elseif ($field['type'] == "file_audio" || $field['type'] == "file_video") {
$value = $field_values[0]['filename'] . " " . $field_values[0]['filemime'] . " " . $field_values[0]['filesize'] . " " . t("bytes");
}*/
elseif ($field['type'] == "file") {
$value = $field_values[0]['fid'] . " ; " . $field_values[0]['uri'];
}
elseif ($field['type'] == "taxonomy_term_reference") {
$value = $field_values[0]['tid'];
}
/*elseif ($field['type'] == "nodereference") {
$value = "nid: " . $field_values[0]['nid'];
}
elseif ($field['type'] == "userreference") {
$value = "uid: " . $field_values[0]['uid'];
}*/
else {
if (!isset($field_values[0]['value'])) {
$value = t("Unknown field format, can't display field value.");
} else {
$value = $field_values[0]['value'];
}
}
if (empty($value)) {
$value = 'NULL';
}
return $value;
}
/**
* Loads a conversion template array using template_id.
*
* @param $template_id
* The template id to use
* @return
* An array containing the template information or FALSE if there is no such template
*/
function node_convert_load_template($template_id) {
$template = db_query("SELECT * FROM {node_convert_templates} WHERE nctid = :nctid", array(':nctid' => $template_id))->fetchAssoc();
if (is_array($template)) {
$template['data'] = unserialize($template['data']);
}
return $template;
}
/**
* Checks if the logged in user has access to use the conversion template.
*
* @param $data
* An array containing either of the following keys
* - template_id The template id used for conversion
* - template The template array containing data
* @return
* TRUE if user can use the template, FALSE otherwise.
*/
function node_convert_check_template_permission_user($data) {
if (!empty($data['template'])) {
$template = $data['template'];
}
elseif (!empty($data['template_id'])) {
$template = node_convert_load_template($data['template_id']);
}
else {
return FALSE;
}
// User with this permission can convert from/to any content type.
if (user_access('administer conversion')) {
return TRUE;
}
$access = user_access('convert from ' . $template['source_type']) && user_access('convert to ' . $template['destination_type']);
return $access;
}
/**
* Converts a list of nodes using a given template
*
* @param $nodes
* An array containing a list of node nid's
* @param $template_id
* The template to use for conversion
* @return
* FALSE if the user doesn't have permission to use the template.
*/
function node_convert_convert_nodes_using_template($nodes, $template_id) {
$template = node_convert_load_template($template_id);
$access = node_convert_check_template_permission_user(array('template' => $template));
if ($access == FALSE) {
drupal_set_message(t("You don't have permission to use this conversion template."), 'warning', FALSE);
return FALSE;
}
foreach ($nodes as $nid) {
$node = node_load($nid);
// The source type of the given node doesn't match the template one, so we discard it with a message
if ($node->type != $template['source_type']) {
drupal_set_message(t("Node %nid doesn't match the template source type. Discarded.", array('%nid' => $node->nid)), 'warning');
}
else {
$result = node_convert_node_convert($node->nid, $template['destination_type'], $template['data']['fields']['source'], $template['data']['fields']['destination'], $template['data']['no_fields'], $template['data']['hook_options']);
// We display errors if there are any, or the default success message
node_convert_messages($result, array('nid' => $nid));
}
}
}