You are here

clone.admin.inc in Content Type Clone 7

Content Type Clone cloning functions.

File

includes/clone.admin.inc
View source
<?php

/**
 * @file
 * Content Type Clone cloning functions.
 */

/**
 * Form constructor for the clone creation.
 *
 * @param array $form
 *   The form array.
 * @param array $form_state
 *   Reference to the form state array.
 * @param string $machine_name
 *   The machine name of the source content type.
 */
function content_type_clone_create_form(array $form, array &$form_state, $machine_name) {

  // Load the node type from machine name.
  $node_type = node_type_load($machine_name);

  // Prepare the new node type machine name prefix.
  $node_type_prefix = $node_type->type . '_clone';

  // Get the next occurrence count.
  $num = content_type_clone_get_next_type_number($node_type_prefix);

  // Source fieldset.
  $form['fieldset_1'] = array(
    '#type' => 'fieldset',
    '#title' => t('Content type source'),
    '#collapsible' => TRUE,
    '#collapsed' => TRUE,
  );

  // Source node type name field.
  $form['fieldset_1']['source_name'] = array(
    '#type' => 'textfield',
    '#title' => t('Name'),
    '#default_value' => $node_type->name,
    '#required' => TRUE,
    '#attributes' => array(
      'readonly' => 'readonly',
      'class' => array(
        'ctc-readonly',
      ),
    ),
  );

  // Source node type machine name field.
  $form['fieldset_1']['source_machine_name'] = array(
    '#type' => 'textfield',
    '#title' => t('Machine name'),
    '#default_value' => $node_type->type,
    '#required' => TRUE,
    '#attributes' => array(
      'readonly' => 'readonly',
      'class' => array(
        'ctc-readonly',
      ),
    ),
  );

  // Source node type description field.
  $form['fieldset_1']['source_description'] = array(
    '#type' => 'textarea',
    '#title' => t('Description'),
    '#default_value' => $node_type->description,
    '#attributes' => array(
      'readonly' => 'readonly',
      'class' => array(
        'ctc-readonly',
      ),
    ),
  );

  // Target fieldset.
  $form['fieldset_2'] = array(
    '#type' => 'fieldset',
    '#title' => t('Content type target'),
    '#collapsible' => FALSE,
    '#collapsed' => FALSE,
  );

  // Target node type name field.
  $default_value = 'Clone of ' . $node_type->name . ' (' . $num . ')';
  $form['fieldset_2']['target_name'] = array(
    '#type' => 'textfield',
    '#title' => t('Name'),
    '#default_value' => $default_value,
    '#token' => FALSE,
    '#required' => TRUE,
  );

  // Target type machine name field.
  $form['fieldset_2']['target_machine_name'] = array(
    '#type' => 'textfield',
    '#title' => t('Machine name'),
    '#default_value' => $node_type_prefix . '_' . $num,
    '#required' => TRUE,
  );

  // Source node type description field.
  $form['fieldset_2']['target_description'] = array(
    '#type' => 'textarea',
    '#title' => t('Description'),
    '#default_value' => t('Description of @name (@num)', array(
      '@name' => $node_type->name,
      '@num' => $num,
    )),
  );

  // Clone parameters fieldset.
  $form['fieldset_2']['clone_source_nodes'] = array(
    '#type' => 'checkbox',
    '#title' => t('Copy all nodes from the source content type to the target content type'),
  );

  // Delete parameters fieldset.
  $form['fieldset_2']['delete_source_nodes'] = array(
    '#type' => 'checkbox',
    '#title' => t('Delete all nodes from the source content type after they have been copied to the target content type'),
  );

  // Token fieldset.
  $form['fieldset_3'] = array(
    '#type' => 'fieldset',
    '#title' => t('Replacement patterns'),
    '#collapsible' => TRUE,
    '#collapsed' => TRUE,
  );

  // Token fields.
  if (module_exists('token')) {

    // Display the node title pattern field.
    $default_value = t('Clone of @title', array(
      '@title' => '[node:title]',
    ));
    $form['fieldset_3']['title_pattern'] = array(
      '#type' => 'textfield',
      '#title' => t('Node title pattern'),
      '#default_value' => $default_value,
      '#token' => FALSE,
      '#required' => TRUE,
    );

    // List available tokens.
    $form['fieldset_3']['token_tree_link'] = array(
      '#theme' => 'token_tree',
      '#token_types' => array(
        'node',
      ),
      '#global_types' => TRUE,
      '#click_insert' => TRUE,
    );
  }
  else {
    $form['fieldset_3']['token_tree'] = array(
      '#markup' => '<p>' . t('Enable the <a href="@drupal-token">Token module</a> to view the available token browser.', array(
        '@drupal-token' => 'http://drupal.org/project/token',
      )) . '</p>',
    );
  }

  // Submit button.
  $form['submit'] = array(
    '#type' => 'submit',
    '#value' => t('Generate'),
  );

  // Attach the admin css file.
  $form['#attached']['css'] = array(
    drupal_get_path('module', 'content_type_clone') . '/css/admin.css',
  );

  // Return the form.
  return $form;
}

/**
 * Generate a default item number for a new clone.
 *
 * This function will add the clone number to the machine name, the title
 * and the description fields in the content type creation form.
 *
 * The clone number is based on the number of existing clones
 * for the content type being cloned.
 *
 * @param string $node_type_prefix
 *   The default prefix defined for the new content type.
 */
function content_type_clone_get_next_type_number($node_type_prefix) {

  // Get node types.
  $node_types = array_keys(node_type_get_types());

  // Prepare the search pattern.
  $pattern = '/^' . $node_type_prefix . '/';

  // Count existing prefix occurrences.
  $occurrences = count(preg_grep($pattern, $node_types));

  // Return an incremented value.
  return $occurrences + 1;
}

/**
 * Form validation handler for the clone creation form.
 *
 * @param array $form
 *   The form array.
 * @param array $form_state
 *   Reference to the form state array.
 */
function content_type_clone_create_form_validate(array $form, array &$form_state) {
  if ($form['#form_id'] == 'content_type_clone_create_form') {

    // Get node types.
    $node_types = array_keys(node_type_get_types());

    // If the machine name already exists.
    if (in_array($form_state['values']['target_machine_name'], $node_types)) {
      form_set_error('target_machine_name', t('The content type name provided already exists'));
    }

    // Add the custom form submit handler.
    $form['#submit'][] = 'content_type_clone_create_form_submit';
  }
}

/**
 * Form submit handler for the clone creation form.
 *
 * @param array $form
 *   The form array.
 * @param array $form_state
 *   Reference to the form state array.
 */
function content_type_clone_create_form_submit(array $form, array &$form_state) {
  if ($form['#form_id'] == 'content_type_clone_create_form') {

    // Reset counter for debug information.
    $_SESSION['http_request_count'] = 0;

    // Execute the function batch function.
    $batch = content_type_clone_create($form_state['values']);
    batch_set($batch);
  }
}

/**
 * Create a content type clone.
 *
 * This function creates the new content type and adds the cloned fields.
 *
 * @param array $values
 *   Form values coming from the content type creation form.
 */
function content_type_clone_create(array $values) {

  // Create the type definition array.
  $type = array(
    'type' => $values['target_machine_name'],
    'name' => $values['target_name'],
    'base' => 'node_content',
    'description' => $values['target_description'],
    'custom' => 1,
    'modified' => 1,
    'locked' => 0,
  );

  // Set the type defaults.
  $type = node_type_set_defaults($type);

  // Save the ndoe type.
  node_type_save($type);

  // Add body field.
  node_add_body_field($type);

  // Prepare the operations array.
  $operations = array();

  // Get node type base fields.
  $base_fields = field_info_instances('node', $values['source_machine_name']);

  // Get node type extra fields.
  $extra_fields = field_info_extra_fields('node', $values['source_machine_name'], 'form');

  // Merge all fields.
  $all_fields = array_merge($base_fields, $extra_fields);

  // Create new fields.
  foreach ($all_fields as $name => $instance) {
    if (isset($name) && !empty($name)) {
      $operations[] = array(
        'content_type_clone_set_target_field',
        array(
          $instance,
          $name,
          $values,
        ),
      );
    }
  }

  // Also clone the nodes if requested.
  if ((int) $values['clone_source_nodes'] == 1) {

    // Load all nodes for the source content type.
    $nids = content_type_clone_get_type_nids($values['source_machine_name']);

    // Loop through each node.
    foreach ($nids as $nid) {

      // Add an operation.
      $operations[] = array(
        'content_type_clone_copy_node',
        array(
          $nid,
          $values,
        ),
      );
    }
  }

  // Define batch operation.
  $batch = array(
    'title' => t('Clone content type'),
    'operations' => $operations,
    'file' => drupal_get_path('module', 'content_type_clone') . '/includes/clone.admin.inc',
    'finished' => 'content_type_clone_finished',
    'init_message' => t('Operation in progress...'),
    'progress_message' => t('Task @current/@total'),
    'error_message' => t('An error has occurred please try again.'),
  );
  return $batch;
}

/**
 * Create a field.
 *
 * This function creates a field for a cloned content type.
 *
 * @param object $instance
 *   The newly created content type.
 * @param string $field_name
 *   The name of the field in the source content type.
 * @param array $values
 *   Form values coming from the content type creation form.
 */
function content_type_clone_set_target_field($instance, $field_name, array $values, array &$context) {

  // Progress context results.
  $context['results'][] = $field_name;

  // Pass the cloned content type name to context.
  $context['results']['source_name'] = $values['source_name'];

  // Update the field count.
  if (!isset($context['results']['field_count'])) {
    $context['results']['field_count'] = 0;
  }
  $context['results']['field_count']++;

  // Progress context message.
  $context['message'] = t('Cloning field @field', array(
    '@field' => $field_name,
  ));

  // Verify the field does not already exist.
  if (!_content_type_clone_exclude_field($field_name)) {

    // If field doesn't exist, create it.
    if (!field_info_field($field_name)) {
      if ($field = field_info_field($field_name)) {
        $field = field_create_field($field);
      }
    }

    // Set the new bundle name.
    $instance['bundle'] = $values['target_machine_name'];
    $instance['field_name'] = $field_name;

    // Create an instance of the field and bind it to the bundle.
    field_create_instance($instance);
  }

  // Update progress.
  _content_type_clone_update_http_requests();
}

/**
 * Checks if a field should be excluded.
 *
 * @param string $field_name
 *   The field name.
 * @return bool
 *   Number of requests. 
 */
function _content_type_clone_exclude_field($field_name) {
  $exclude = array(
    'title',
    'body',
    'metatags',
    'locations',
  );
  return in_array($field_name, $exclude);
}

/**
 * Utility function to increment HTTP requests in a session variable.
 */
function _content_type_clone_update_http_requests() {
  $_SESSION['http_request_count']++;
}

/**
 * Get node ids by content type.
 *
 * This function returns all node ids for a given content type.
 *
 * @param string $type
 *   The content type.
 */
function content_type_clone_get_type_nids($type) {

  // Get all nids for the content type.
  $nids = db_query('SELECT nid FROM {node} WHERE type =  :type', array(
    ':type' => $type,
  ))
    ->fetchCol();

  // Return the results.
  return $nids;
}

/**
 * Clone a node.
 *
 * This function copies a node from the
 * source content type to the target content type.
 *
 * @param object $node_id
 *   The id of the node to clone.
 * @param array $values
 *   Values from the content type clone form.
 * @param array $context
 *   The batch operations context.
 */
function content_type_clone_copy_node($node_id, array $values, array &$context) {

  // Load the node by id.
  $source_node = node_load($node_id);

  // Progress context results.
  $context['results'][] = 'nid' . $source_node->nid;

  // Update the node count.
  $context['results']['node_count']++;

  // Progress context message.
  $context['message'] = t('Cloning node @title', array(
    '@title' => $source_node->title,
  ));

  // Copy the source node.
  $target_node = clone $source_node;

  // Unset version and node id's.
  unset($target_node->nid);
  unset($target_node->vid);
  unset($target_node->path);

  // Set target node type.
  $target_node->type = $values['target_machine_name'];

  // Set the target node title.
  if (module_exists('token')) {
    $target_node->title = token_replace($values['title_pattern'], array(
      'node' => $target_node,
    ));
  }
  else {
    $target_node->title = t('Clone of @title', array(
      '@title' => $source_node->title,
    ));
  }

  // Save the copy.
  node_save($target_node);

  // Delete the source node if required.
  if ((int) $values['delete_source_nodes'] == 1) {
    node_delete($source_node->nid);
  }

  // Update progress.
  _content_type_clone_update_http_requests();
}

/**
 * Utility function to count the HTTP requests in a session variable.
 *
 * @return int
 *   Number of requests.
 */
function _content_type_clone_get_http_requests() {
  return !empty($_SESSION['http_request_count']) ? $_SESSION['http_request_count'] : 0;
}

/**
 * Batch operation finished.
 *
 * This function displays a message after the batch operation ends.
 *
 * @param bool $success
 *   The batch status.
 * @param array $results
 *   The batch results array.
 * @param array $operations
 *   The batch operations array.
 */
function content_type_clone_finished($success, array $results, array $operations) {

  // Test the results.
  if ($success) {
    drupal_set_message(t('The "@source_name" content type and @field_count fields have been cloned successfully.', array(
      '@source_name' => $results['source_name'],
      '@field_count' => $results['field_count'],
    )));
  }
  else {
    $error_operation = reset($operations);
    drupal_set_message(t('An error occurred while processing @operation with arguments : @args', array(
      '@operation' => $error_operation[0],
      '@args' => print_r($error_operation[0], TRUE),
    )));
  }

  // Redirect to the content types list.
  drupal_goto('/admin/structure/types');
}

Functions

Namesort descending Description
content_type_clone_copy_node Clone a node.
content_type_clone_create Create a content type clone.
content_type_clone_create_form Form constructor for the clone creation.
content_type_clone_create_form_submit Form submit handler for the clone creation form.
content_type_clone_create_form_validate Form validation handler for the clone creation form.
content_type_clone_finished Batch operation finished.
content_type_clone_get_next_type_number Generate a default item number for a new clone.
content_type_clone_get_type_nids Get node ids by content type.
content_type_clone_set_target_field Create a field.
_content_type_clone_exclude_field Checks if a field should be excluded.
_content_type_clone_get_http_requests Utility function to count the HTTP requests in a session variable.
_content_type_clone_update_http_requests Utility function to increment HTTP requests in a session variable.