You are here

node_clone.module in Node clone 8

Allow users to make a copy of an item of content (a node) and then edit that copy.

File

node_clone.module
View source
<?php

/**
 * @file
 * Allow users to make a copy of an item of content (a node) and then edit that copy.
 */

/**
 * Implementation of hook_help().
 */
function node_clone_help($path, $arg) {
  switch ($path) {
    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".') . '</p>';
      return $output;
    case 'node/%/clone':

      // This looks like another module's variable. You'll need to rewrite this call
      // to ensure that it uses the correct configuration object.
      // $method = variable_get('clone_method', 'prepopulate');
      // if ($method == 'prepopulate') {
      return t('This clone will not be saved to the database until you submit.');
  }
}
function node_clone_is_permitted($type) {

  // @FIXME
  // Could not extract the default value because it is either indeterminate, or
  // not scalar. You'll need to provide a default value in
  // config/install/node_clone.settings.yml and config/schema/node_clone.schema.yml.
  $omitted = \Drupal::config('node_clone.settings')
    ->get('node_clone_omitted');
  return empty($omitted[$type]);
}

/**
 * Menu title callback.
 */
function node_clone_action_link_title($node) {

  // A hack to present a shorter title in contextual links.
  if (\Drupal\Core\Url::fromRoute("<current>")
    ->toString() != 'node/' . $node->nid) {
    return t('Clone');
  }
  if (\Drupal::config('node_clone.settings')
    ->get('node_clone_use_node_type_name')) {
    return t('Clone this !type', array(
      '!type' => \Drupal\Component\Utility\Unicode::strtolower(node_type_get_name($node)),
    ));
  }
  return t('Clone content');
}

/**
 * Implementation of hook_node_type_delete().
 */
function node_clone_node_type_delete($info) {

  // @FIXME
}

/**
 * Implementation of hook_node_type_update().
 */
function node_clone_node_type_update($info) {
  if (!empty($info->old_type) && $info->old_type != $info->type) {

    // @FIXME
    // // // The correct configuration object could not be determined. You'll need to
    // // // rewrite this call manually.
    // // variable_del('node_clone_reset_' . $info->old_type);
    //
    //       variable_set('node_clone_reset_' . $info->type, TRUE);
    //     }
  }
}

/**
 * Implements hook_form_BASE_FORM_ID_alter().
 */
function node_clone_form_node_form_alter(&$form, $form_state, $form_id) {

  // Add the clone_from_original_nid value for node forms triggered by cloning.
  // This will make sure the clone_from_original_nid property is still
  // attached to the node when passing through hook_node_insert().
  if (!empty($form['#node']->clone_from_original_nid)) {
    $form['clone_from_original_nid'] = array(
      '#type' => 'value',
      '#value' => $form['#node']->clone_from_original_nid,
    );
  }
}

/**
 * Implements hook_form_FORM_ID_alter().
 */
function node_clone_form_node_admin_content_alter(&$form, $form_state, $form_id) {

  // @FIXME
  // // @FIXME
  // // This looks like another module's variable. You'll need to rewrite this call
  // // to ensure that it uses the correct configuration object.
  // if (variable_get('clone_method', 'prepopulate') == 'prepopulate') {
  //     $destination = drupal_get_destination();
  //   }
  //   else {
  //     $destination = array();
  //   }
  // The property attribute changes in the $form array depending on the user role.
  $property = isset($form['admin']['nodes']['#options']) ? '#options' : '#rows';
  if (empty($form['admin']['nodes'][$property])) {
    return;
  }

  // Expose a Clone operation on each node.
  foreach ($form['admin']['nodes'][$property] as $nid => &$row) {
    $node = \Drupal::entityManager()
      ->getStorage('node')
      ->load($nid);
    if (node_clone_access_cloning($node)) {

      // The structure of this form is different if there is just 1 or more
      // than one operation.
      if (!isset($row['operations']['data']['#links'])) {
        $row['operations']['data']['#links'] = array();
        $row['operations']['data']['#attributes']['class'] = array(
          'links',
          'inline',
        );
        $row['operations']['data']['#theme'] = 'links__node_operations';
        if (isset($row['operations']['data']['#title'])) {
          $title = $row['operations']['data']['#title'];
          $row['operations']['data']['#links'][$title] = array(
            'title' => $title,
            'href' => $row['operations']['data']['#href'],
            'query' => $row['operations']['data']['#options']['query'],
          );
          unset($row['operations']['data']['#type']);
        }
      }
      $row['operations']['data']['#links']['clone'] = array(
        'title' => t('clone'),
        'href' => 'node/' . $nid . '/clone/' . node_clone_get_token($nid),
        'query' => $destination,
      );
    }
  }
}

/**
 * Implements hook_action_info().
 */
function node_clone_action_info() {
  return array(
    'node_clone_action_clone' => array(
      'type' => 'node',
      'label' => t('Clone item'),
      'configurable' => TRUE,
      'hooks' => array(
        'any' => TRUE,
      ),
      'triggers' => array(
        'any',
      ),
    ),
  );
}

/**
 * Action callback.
 */
function node_clone_action_clone($original_node, $context) {
  module_load_include('inc', 'clone', 'clone.pages');
  if (node_clone_is_permitted($original_node->type)) {
    $val = !empty($context['clone_context']) ? $context['clone_context'] : array();
    $node = _node_clone_node_prepare($original_node, !empty($val['prefix_title']));
    if (isset($val['substitute_from']) && strlen($val['substitute_from']) && isset($val['substitute_to'])) {
      $i = !empty($val['substitute_case_insensitive']) ? 'i' : '';
      $pattern = '#' . strtr($val['substitute_from'], array(
        '#' => '\\#',
      )) . '#' . $i;
      foreach (array(
        'title',
      ) as $property) {
        $new = preg_replace($pattern, $val['substitute_to'], $node->{$property});
        if ($new) {
          $node->{$property} = $new;
        }
      }
      foreach (array(
        'body',
      ) as $property) {
        foreach ($node->{$property} as $lang => $row) {
          foreach ($row as $delta => $data) {
            foreach (array(
              'value',
              'summary',
            ) as $key) {
              if (isset($node->{$property}[$lang][$delta][$key])) {
                $new = preg_replace($pattern, $val['substitute_to'], $node->{$property}[$lang][$delta][$key]);
                if ($new) {
                  $node->{$property}[$lang][$delta][$key] = $new;
                }
              }
            }
          }
        }
      }
    }

    // Let other modules do special fixing up.
    $context = array(
      'method' => 'action',
      'original_node' => $original_node,
      'clone_context' => $val,
    );
    \Drupal::moduleHandler()
      ->alter('node_clone_node', $node, $context);
    $node
      ->save();
    if (\Drupal::moduleHandler()
      ->moduleExists('rules')) {
      rules_invoke_event('node_clone_node', $node, $original_node);
    }
  }
  else {
    drupal_set_message(t('Clone failed for %title : not permitted for nodes of type %type', array(
      '%title' => $original_node->title,
      '%type' => $original_node->type,
    )), 'warning');
  }
}

/**
 * Action form.
 */
function node_clone_action_clone_form($context) {
  $form['clone_context'] = array(
    '#tree' => TRUE,
  );
  $form['clone_context']['prefix_title'] = array(
    '#title' => t('Prefix title'),
    '#type' => 'checkbox',
    '#description' => t('Should cloned node tiles be prefixed?'),
    '#default_value' => isset($context['clone_context']['prefix_title']) ? $context['clone_context']['prefix_title'] : 1,
  );
  $form['clone_context']['substitute_from'] = array(
    '#title' => t('Substitute from string'),
    '#type' => 'textfield',
    '#description' => t('String (or regex) to substitute from in title and body.'),
    '#default_value' => isset($context['clone_context']['substitue_from']) ? $context['clone_context']['substitue_from'] : '',
  );
  $form['clone_context']['substitute_to'] = array(
    '#title' => t('Substitute to string'),
    '#type' => 'textfield',
    '#description' => t('String (or regex) to substitute to in title and body.'),
    '#default_value' => isset($context['clone_context']['substitue_to']) ? $context['clone_context']['substitue_to'] : '',
  );
  $form['clone_context']['substitute_case_insensitive'] = array(
    '#title' => t('Case insensitive substitution'),
    '#type' => 'checkbox',
    '#description' => t('Should the substituion match be case insensitive?'),
    '#default_value' => isset($context['clone_context']['substitute_case_insensitive']) ? $context['clone_context']['substitute_case_insensitive'] : NULL,
  );
  return $form;
}

/**
 * Action form submit.
 */
function node_clone_action_clone_submit($form, $form_state) {
  return array(
    'clone_context' => $form_state['values']['clone_context'],
  );
}

Functions

Namesort descending Description
node_clone_action_clone Action callback.
node_clone_action_clone_form Action form.
node_clone_action_clone_submit Action form submit.
node_clone_action_info Implements hook_action_info().
node_clone_action_link_title Menu title callback.
node_clone_form_node_admin_content_alter Implements hook_form_FORM_ID_alter().
node_clone_form_node_form_alter Implements hook_form_BASE_FORM_ID_alter().
node_clone_help Implementation of hook_help().
node_clone_is_permitted
node_clone_node_type_delete Implementation of hook_node_type_delete().
node_clone_node_type_update Implementation of hook_node_type_update().