Skip to content
Snippets Groups Projects
entity_clone.module 6.11 KiB
Newer Older
Brian Canini's avatar
Brian Canini committed
<?php

/**
 * @file
 * Contains entity_clone.module.
 */

use Drupal\Core\Access\AccessResult;
use Drupal\Core\Cache\CacheableMetadata;
Brian Canini's avatar
Brian Canini committed
use Drupal\Core\Entity\ContentEntityTypeInterface;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\Core\Config\Entity\ConfigEntityTypeInterface;
use Drupal\Core\Session\AccountInterface;
Brian Canini's avatar
Brian Canini committed
use Drupal\entity_clone\EntityClone\Config\ConfigEntityCloneBase;
use Drupal\entity_clone\EntityClone\Config\ConfigEntityCloneFormBase;
use Drupal\entity_clone\EntityClone\Config\ConfigWithFieldEntityClone;
use Drupal\entity_clone\EntityClone\Config\FieldConfigEntityClone;
use Drupal\entity_clone\EntityClone\Config\MenuEntityClone;
use Drupal\entity_clone\EntityClone\Config\MenuEntityCloneForm;
Brian Canini's avatar
Brian Canini committed
use Drupal\entity_clone\EntityClone\Content\ContentEntityCloneBase;
use Drupal\entity_clone\EntityClone\Content\ContentEntityCloneFormBase;
use Drupal\entity_clone\EntityClone\Content\FileEntityClone;
use Drupal\entity_clone\EntityClone\Content\TaxonomyTermEntityClone;
use Drupal\entity_clone\EntityClone\Content\UserEntityClone;
use Drupal\entity_clone\EntityClone\Config\LayoutBuilderEntityClone;
Brian Canini's avatar
Brian Canini committed

/**
 * Implements hook_help().
 */
function entity_clone_help($route_name, RouteMatchInterface $route_match) {
  switch ($route_name) {
    // Main module help for the entity_clone module.
    case 'help.page.entity_clone':
      $output = '';
      $output .= '<h3>' . t('About') . '</h3>';
      $output .= '<p>' . t('Provides a new operation to clone all Entities.') . '</p>';
      return $output;

    default:
      return;

  }
}

/**
 * Implements hook_entity_type_build().
 */
function entity_clone_entity_type_build(array &$entity_types) {
  $specific_handler = [
    'file' => [
      'entity_clone' => FileEntityClone::class,
    ],
    'user' => [
      'entity_clone' => UserEntityClone::class,
    ],
    'field_config' => [
      'entity_clone' => FieldConfigEntityClone::class,
    ],
    'node_type' => [
      'entity_clone' => ConfigWithFieldEntityClone::class,
    ],
    'comment_type' => [
      'entity_clone' => ConfigWithFieldEntityClone::class,
    ],
    'block_content_type' => [
      'entity_clone' => ConfigWithFieldEntityClone::class,
    ],
    'contact_form' => [
      'entity_clone' => ConfigWithFieldEntityClone::class,
    ],
    'taxonomy_term' => [
      'entity_clone' => TaxonomyTermEntityClone::class,
    ],
    'menu' => [
      'entity_clone_form' => MenuEntityCloneForm::class,
      'entity_clone' => MenuEntityClone::class,
    ],
    'taxonomy_vocabulary' => [
      'entity_clone' => ConfigWithFieldEntityClone::class,
    ],
    'entity_view_display' => [
      'entity_clone' => \Drupal::moduleHandler()->moduleExists('layout_builder') ? LayoutBuilderEntityClone::class : NULL,
    ],
Brian Canini's avatar
Brian Canini committed
  ];

  /** @var \Drupal\Core\Entity\EntityTypeInterface[] $entity_types */
  foreach ($entity_types as $entity_type_id => $entity_type) {
    $has_entity_clone_handler = $entity_type->getHandlerClass('entity_clone');
    if (!$has_entity_clone_handler) {
      if ($entity_type instanceof ContentEntityTypeInterface) {
        $entity_type->setHandlerClass('entity_clone', ContentEntityCloneBase::class);
        $entity_type->setHandlerClass('entity_clone_form', ContentEntityCloneFormBase::class);
      }
      elseif ($entity_type instanceof ConfigEntityTypeInterface) {
        $entity_type->setHandlerClass('entity_clone', ConfigEntityCloneBase::class);
        $entity_type->setHandlerClass('entity_clone_form', ConfigEntityCloneFormBase::class);

    if (isset($specific_handler[$entity_type->id()]['entity_clone'])) {
      $entity_type->setHandlerClass('entity_clone', $specific_handler[$entity_type->id()]['entity_clone']);
    if (isset($specific_handler[$entity_type->id()]['entity_clone_form'])) {
      $entity_type->setHandlerClass('entity_clone_form', $specific_handler[$entity_type->id()]['entity_clone_form']);

    $entity_type->setLinkTemplate('clone-form', "/entity_clone/$entity_type_id/{{$entity_type_id}}");
Brian Canini's avatar
Brian Canini committed
  }
}

/**
 * Declares entity operations.
 *
 * @param \Drupal\Core\Entity\EntityInterface $entity
 *   The entity on which the linked operations will be performed.
 *
 * @return array
 *   An operations array as returned by
 *   EntityListBuilderInterface::getOperations().
 *
 * @see \Drupal\Core\Entity\EntityListBuilderInterface::getOperations()
 */
function entity_clone_entity_operation(EntityInterface $entity) {
  if ($entity->hasLinkTemplate('clone-form') && $entity->access('clone') && !$entity->isNew()) {
Brian Canini's avatar
Brian Canini committed
    return [
      'clone' => [
        'title' => t('Clone'),
        'weight' => 50,
        'url' => $entity->toUrl('clone-form')
          ->mergeOptions(['query' => \Drupal::destination()->getAsArray()]),
 * Implements hook_entity_access().
function entity_clone_entity_access(EntityInterface $entity, $operation, AccountInterface $account) {
  if ($operation !== 'clone') {
    return AccessResult::neutral();

  $cache = new CacheableMetadata();
  $cache->addCacheContexts(['user.permissions']);

  // Deny access if the user cannot clone the entity.
  $access = AccessResult::forbiddenIf(!$account->hasPermission('clone ' . $entity->getEntityTypeId() . ' entity'));
  if ($access->isForbidden()) {
    return $access->addCacheableDependency($cache);
  }

  // Deny access if the user can clone but cannot create new entities of this
  // type. However, we have some exceptions in which the access control handler
  // doesn't have a say in things. In these cases, we go based on the clone
  // permission only.
  $exceptions = [
    'file',
    'paragraph',
  ];

  if (in_array($entity->getEntityTypeId(), $exceptions)) {
    return AccessResult::allowed()->addCacheableDependency($cache);
  }

  $handler = \Drupal::entityTypeManager()->getAccessControlHandler($entity->getEntityTypeId());
  $access = $handler->createAccess($entity->bundle(), $account, [], TRUE);
  if (!$access->isAllowed()) {
    $cache->addCacheableDependency($access);
    $forbidden = AccessResult::forbidden();
    return $forbidden->addCacheableDependency($cache);
  }

  return AccessResult::allowed()->addCacheableDependency($cache);