You are here

clone.module in Node clone 5

Same filename and directory in other branches
  1. 5.2 clone.module
  2. 6 clone.module
  3. 7 clone.module

File

clone.module
View source
<?php

/**
 * Implementation of hook_help().
 */
function clone_help($section) {
  switch ($section) {
    case 'admin/help#clone':
      $output = '<p>' . t('The clone module allows users to make a copy of an existing node and then edit that copy. The authorship is set to the current user, the menu and url aliases are reset, and the words "Clone of" are inserted into the title to remind you that you are not editing the original node.') . '</p>';
      $output .= '<p>' . t('Users with the "clone node" permission can utilize this functionality. A new tab will appear on node pages with the word "clone". Once you click this tab you have <em>already</em> created a new node that is a copy of the node you were viewing, and you will be redirected to an edit screen for that new node.') . '</p>';
      return $output;
  }
}

/**
* Implementation of hook_perm().
*/
function clone_perm() {
  return array(
    'clone node',
    'clone own nodes',
  );
}

/**
 * Implementation of hook_menu().
 */
function clone_menu($may_cache) {
  $items = array();
  if ($may_cache) {
    $items[] = array(
      'path' => 'admin/settings/clone',
      'title' => t('Clone module'),
      'description' => t('Allows users to clone (copy then edit) an existing node.'),
      'callback' => 'drupal_get_form',
      'callback arguments' => 'clone_settings',
      'access' => user_access('administer site configuration'),
    );
  }
  else {
    if (arg(0) == 'node' && is_numeric(arg(1))) {
      $node = node_load(arg(1));
      if ($node->nid) {
        $items[] = array(
          'path' => 'node/' . $node->nid . '/clone',
          'title' => t('Clone'),
          'callback' => 'clone_node_check',
          'access' => clone_access_cloning($node),
          'type' => MENU_LOCAL_TASK,
          'weight' => 5,
        );
      }
    }
  }
  return $items;
}
function clone_access_cloning($node) {
  global $user;

  // Check basic permissions first.
  $access = user_access('clone node') || $user->uid && $node->uid == $user->uid && user_access('clone own nodes');

  // Make sure the user can view the original node content.
  $access = $access && node_access('view', $node);

  // Check additional conditions
  $access = $access && (clone_is_permitted($node->type) && filter_access($node->format) && node_access('create', $node->type));

  // Let other modules alter this - for exmple to only allow some users
  // to clone specific nodes or types.
  foreach (module_implements('clone_access_alter') as $module) {
    $function = $module . '_clone_access_alter';
    $function($access, $node);
  }
  return $access;
}
function clone_is_permitted($type) {
  $omitted = variable_get('clone_omitted', array());
  return empty($omitted[$type]);
}

/**
* menu callback to configure module settings.
*/
function clone_settings() {
  $form['heading'] = array(
    '#value' => '<b>' . t('Configuration options for the clone module:') . '</b>',
  );
  $form['clone_nodes_without_confirm'] = array(
    '#type' => 'checkbox',
    '#title' => t('Clone nodes without requiring confirmation'),
    '#default_value' => variable_get('clone_nodes_without_confirm', FALSE),
    '#description' => t('If this is set, a new node will be created immediately upon clicking the "clone" tab when viewing a node.'),
  );
  $form['publishing'] = array(
    '#type' => 'fieldset',
    '#title' => t('Should the publishing options ( e.g. published, promoted, etc) be reset to the defaults?'),
  );
  $types = node_get_types('names');
  foreach ($types as $type => $name) {
    $form['publishing']['clone_reset_' . $type] = array(
      '#type' => 'checkbox',
      '#title' => t('@s: reset publishing options when cloned', array(
        '@s' => $name,
      )),
      '#default_value' => variable_get('clone_reset_' . $type, FALSE),
    );
  }

  // Need the variable default key to be something that's never a valid node type.
  $types = array_merge(array(
    '!' => "<" . t("none") . ">",
  ), $types);
  $form['clone_omitted'] = array(
    '#type' => 'select',
    '#title' => t('Omitted content types'),
    '#default_value' => variable_get('clone_omitted', array(
      '!',
    )),
    '#options' => $types,
    '#description' => t('Select any node types which should <em>never</em> be cloned. Typically you should will want to select here all node types for which cloning fails (e.g. image nodes).'),
    '#multiple' => TRUE,
  );
  $form['clone_method'] = array(
    '#type' => 'value',
    '#value' => 'save-edit',
  );
  return system_settings_form($form);
}

/**
 * Implementation of hook_mode_type().
 */
function clone_node_type($op, $type_obj) {
  switch ($op) {
    case 'delete':
      variable_del('clone_reset_' . $type_obj->type);
      break;
    case 'update':
      if (!empty($type_obj->old_type) && $type_obj->old_type != $type_obj->type) {
        if (variable_get('clone_reset_' . $type_obj->old_type, FALSE)) {
          variable_del('clone_reset_' . $type_obj->old_type);
          variable_set('clone_reset_' . $type_obj->type, TRUE);
        }
      }
      break;
  }
}

/**
 *  Menu callback: prompt the user to confirm the operation
 */
function clone_node_check() {
  if (variable_get('clone_nodes_without_confirm', FALSE)) {
    clone_node(arg(1));
    return;
  }
  return drupal_get_form('clone_node_confirm');
}

/**
 *  form builder: prompt the user to confirm the operation
 */
function clone_node_confirm() {
  $node = node_load(arg(1));
  $form['nid'] = array(
    '#type' => 'value',
    '#value' => $node->nid,
  );
  return confirm_form($form, t('Are you sure you want to clone %title?', array(
    '%title' => $node->title,
  )), 'node/' . $node->nid, '<p>' . t('This action will create a new node. You will then be redirected to the edit page for the new node.') . '</p>', t('Clone'), t('Cancel'));
}

/**
 *  Handle confirm form submission
 */
function clone_node_confirm_submit($form_id, $form_values) {
  if ($form_values['confirm']) {
    clone_node($form_values['nid']);
  }
  return '';
}

/**
 *  Clones a node
 */
function clone_node($nid) {
  if (is_numeric($nid)) {
    global $user;
    $node = node_load($nid);
    if (isset($node->nid) && clone_is_permitted($node->type)) {
      $original_node = drupal_clone($node);
      $node->nid = NULL;
      $node->vid = NULL;
      $node->name = $user->name;
      $node->uid = $user->uid;
      $node->created = 0;
      $node->menu = NULL;
      $node->path = NULL;
      $node->files = array();

      // Remove CCK file and image attachements
      if (module_exists('imagefield') || module_exists('filefield')) {
        $content_type = module_invoke('content', 'types', $node->type);

        // Find all the fields that are files or images.
        foreach ($content_type['fields'] as $data) {
          if ($data['type'] == 'file' || $data['type'] == 'image') {
            $key = $data['field_name'];

            // Remove any attached files as with $node->files
            if (isset($node->{$key})) {
              $node->{$key} = array();
            }
          }
        }
      }

      // Add indicator text to the title
      $node->title = t('Clone of !title', array(
        '!title' => $node->title,
      ));

      // Add an extra property as a flag.
      $node->clone_from_original_nid = $original_node->nid;
      if (variable_get('clone_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);
        }
      }

      // Let other modules do special fixing up.
      // The function signature is: hook_clone_node_alter(&$node, $original_node, $method)
      foreach (module_implements('clone_node_alter') as $module) {
        $function = $module . '_clone_node_alter';
        $function($node, $original_node, "save-edit");
      }
      node_save($node);
      drupal_goto('node/' . $node->nid . '/edit');
    }
  }
}

Functions

Namesort descending Description
clone_access_cloning
clone_help Implementation of hook_help().
clone_is_permitted
clone_menu Implementation of hook_menu().
clone_node Clones a node
clone_node_check Menu callback: prompt the user to confirm the operation
clone_node_confirm form builder: prompt the user to confirm the operation
clone_node_confirm_submit Handle confirm form submission
clone_node_type Implementation of hook_mode_type().
clone_perm Implementation of hook_perm().
clone_settings menu callback to configure module settings.