reassign_user_content-1.0.1/reassign_user_content.module

reassign_user_content.module
<?php

/**
 * @file
 * Reassign deleted user content to another user.
 */

use Drupal\Core\Batch\BatchBuilder;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Link;
use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\Core\Session\AnonymousUserSession;
use Drupal\group\Entity\GroupInterface;
use Drupal\reassign_user_content\MediaBatchService;
use Drupal\user\UserInterface;

/**
 * Implements hook_help().
 */
function reassign_user_content_help($route_name, RouteMatchInterface $route_match) {
  if ($route_name == 'help.page.reassign_user_content') {
    return '<p>' . t('The Reassign User Content module allows you to reassign content of user you are about to delete to another user.') . '</p>';
  }
}

/**
 * Implements hook_form_alter().
 */
function reassign_user_content_form_alter(&$form, FormStateInterface $form_state, $form_id): void {
  $media_module_enabled = \Drupal::moduleHandler()->moduleExists('media');
  $group_module_enabled = \Drupal::moduleHandler()->moduleExists('group');
  if (in_array($form_id, [
    'user_multiple_cancel_confirm',
    'user_cancel_form',
  ])) {
    $form['user_to_assign'] = [
      '#type'        => 'entity_autocomplete',
      '#title'       => t('Choose user to assign content@media@group.', [
        '@media' => $media_module_enabled ? ', media' : '',
        '@group' => $group_module_enabled ? ', and groups' : '',
      ]),
      '#target_type' => 'user',
      '#states'      => [
        'visible'  => [
          [':input[name="user_cancel_method"]' => ['value' => 'user_cancel_reassign_content']],
        ],
        'required' => [
          [':input[name="user_cancel_method"]' => ['value' => 'user_cancel_reassign_content']],
        ],
      ],
    ];

    $form['#validate'][] = 'reassign_user_content__cancel_user_form_validate';
  }
}

/**
 * User cancel Validate.
 *
 * @param array $form
 *   Form array.
 * @param \Drupal\Core\Form\FormStateInterface $form_state
 *   Form state.
 */
function reassign_user_content__cancel_user_form_validate(array $form, FormStateInterface $form_state) : void {
  $to_assign_uid = $form_state->getValue('user_to_assign');
  // In case the user to assign is in the list of users to delete.
  if (
    $form['#form_id'] == 'user_multiple_cancel_confirm' &&
    in_array($to_assign_uid, $form_state->getValue('accounts'))
  ) {
    $form_state->setErrorByName('user_to_assign', t('Choose different user than the ones you want to delete.'));
  }
  else {
    if (
      $form_state->getValue('user_cancel_method') == 'user_cancel_reassign_content' &&
      $form_state->getValue('user_to_assign') == $form_state->getValue('uid')
    ) {
      $form_state->setErrorByName('user_to_assign', t('Choose different user than the deleted one to assign content.'));
    }
  }
}

/**
 * Implements hook_user_cancel_methods_alter().
 */
function reassign_user_content_user_cancel_methods_alter(&$methods): void {
  $media_module_enabled = \Drupal::moduleHandler()->moduleExists('media');
  $group_module_enabled = \Drupal::moduleHandler()->moduleExists('group');
  $methods['user_cancel_reassign_content'] = [
    'title'       => t('Delete the account and make its content@media@group belong to another user. This action cannot be undone.', [
      '@media' => $media_module_enabled ? ', media' : '',
      '@group' => $group_module_enabled ? ', and groups' : '',
    ]),
    'description' => t('Your account will be removed and all account information deleted. All of your content will be assigned to the another user.'),
  ];
}

/**
 * Implements hook_user_cancel().
 */
function reassign_user_content_user_cancel($edit, UserInterface $account, $method): void {
  $user_to_assign_content = $edit['user_to_assign'] ?? NULL;
  if ($method == 'user_cancel_reassign_content' && $user_to_assign_content) {
    // Reassign nodes (current revisions).
    \Drupal::moduleHandler()->loadInclude('node', 'inc', 'node.admin');
    $nodes = \Drupal::entityQuery('node')
      ->accessCheck(FALSE)
      ->condition('uid', $account->id())
      ->execute();
    node_mass_update($nodes, ['uid' => $user_to_assign_content], NULL, TRUE);
    // Reassign old revisions.
    \Drupal::database()->update('node_field_revision')
      ->fields(['uid' => $user_to_assign_content])
      ->condition('uid', $account->id())
      ->execute();

    // Reassign old revisions.
    \Drupal::database()->update('node_revision')
      ->fields(['revision_uid' => $user_to_assign_content])
      ->condition('revision_uid', $account->id())
      ->execute();

    // Reassign revisions with moderated state (draft mode).
    if (\Drupal::moduleHandler()->moduleExists('content_moderation')) {
      \Drupal::database()->update('content_moderation_state_field_revision')
        ->fields(['uid' => $user_to_assign_content])
        ->condition('uid', $account->id())
        ->execute();
    }

    // Anonymize user comments.
    if (\Drupal::moduleHandler()->moduleExists('comment')) {
      $comments = \Drupal::entityTypeManager()
        ->getStorage('comment')
        ->loadByProperties(['uid' => $account->id()]);
      _reassign_user_content_anonymize_comments($comments ?? []);
    }

    // Reassign user media if media module enabled.
    if (\Drupal::moduleHandler()->moduleExists('media')) {
      // Get all media of the deleted user.
      $medias = \Drupal::entityTypeManager()
        ->getStorage('media')
        ->loadByProperties(['uid' => $account->id()]);
      // Reassign all user medias using media batch.
      \Drupal::service('class_resolver')
        ->getInstanceFromDefinition(MediaBatchService::class)
        ->reassignUserMedia($medias, $user_to_assign_content);
    }

    // Reassign user groups if group module enabled.
    if (\Drupal::moduleHandler()->moduleExists('group')) {
      $storage = \Drupal::entityTypeManager()->getStorage('group');
      $gids = $storage->getQuery()
        ->accessCheck(FALSE)
        ->condition('uid', $account->id())
        ->execute();

      // Run this as a batch if there are more than 10 groups.
      if (count($gids) > 10) {
        batch_set([
          'operations' => [
            [
              '_reassign_user_content_group',
              [$gids, $user_to_assign_content],
            ],
          ],
        ]);
      }
      else {
        foreach ($storage->loadMultiple($gids) as $group) {
          assert($group instanceof GroupInterface);
          $group->set('uid', $user_to_assign_content);
          $storage->save($group);
        }
      }
    }
  }
}

/**
 * Implements hook_batch_alter().
 *
 * Alter _user_cancel batch operation
 * because _user_cancel supports only the methods added by UserCancelForm.
 *
 * @see _user_cancel()
 */
function reassign_user_content_batch_alter(&$batch): void {
  $values = isset($batch['form_state']) ? $batch['form_state']->getValues() : [];
  // Only go over the batch sets if we come from the form-submit
  // of the user cancel form, and our method was selected.
  if (
    $values &&
    isset($values['user_cancel_method']) &&
    $values['user_cancel_method'] === 'user_cancel_reassign_content'
  ) {
    // For every account that gets deleted, the batch was created.
    // Change all of them.
    foreach ($batch['sets'] as &$set) {
      if (
        isset($set['operations'][0][0]) &&
        $set['operations'][0][0] == '_user_cancel' &&
        isset($set['operations'][0][1][2]) &&
        $set['operations'][0][1][2] == 'user_cancel_reassign_content'
      ) {
        // Change the batch operation callback.
        $set['operations'][0][0] = 'reassign_user_content__reassign_user_content';
      }
    }
  }
}

/**
 * Implements callback_batch_operation().
 *
 * Similar to _user_cancel().
 *
 * @param array $edit
 *   An array of submitted form values.
 * @param \Drupal\user\UserInterface $account
 *   The user ID of the user account to cancel.
 * @param string $method
 *   The account cancellation method to use.
 *
 * @throws \Drupal\Core\Entity\EntityStorageException
 *
 * @see _user_cancel()
 */
function reassign_user_content__reassign_user_content(array $edit, UserInterface $account, string $method): void {
  $logger = \Drupal::logger('user');

  if ($method == 'user_cancel_reassign_content') {
    // Send account canceled notification if option was checked.
    if (!empty($edit['user_cancel_notify'])) {
      _user_mail_notify('status_canceled', $account);
    }
    $account->delete();
    \Drupal::messenger()
      ->addStatus(t('Account %name has been deleted.', ['%name' => $account->getDisplayName()]));
    $logger->notice('Deleted user: %name %email.', [
      '%name'  => $account->getAccountName(),
      '%email' => '<' . $account->getEmail() . '>',
    ]);
  }

  // After cancelling an account, ensure that user is logged out.
  // We can't destroy their session though, as we might have information on it,
  // and we can't regenerate it because batch API uses the session ID,
  // we will regenerate it in _user_cancel_session_regenerate().
  if ($account->id() == \Drupal::currentUser()->id()) {
    \Drupal::currentUser()->setAccount(new AnonymousUserSession());
  }
}

/**
 * Helper function to an anonymize array of comments.
 *
 * @param mixed $comments
 *   Comments array.
 */
function _reassign_user_content_anonymize_comments(array $comments): void {
  if (empty($comments)) {
    return;
  }

  if (count($comments) > 10) {
    $batch_builder = (new BatchBuilder())
      ->addOperation('_reassign_user_content__anonymize_comments_batch_process', [$comments])
      ->setFinishCallback('_reassign_user_content__anonymize_comments_batch_finished')
      ->setTitle(t('Processing'))
      ->setErrorMessage(t('The update has encountered an error.'))
      // We use a single multi-pass operation, so the default
      // 'Remaining x of y operations' message will be confusing here.
      ->setProgressMessage('');
    batch_set($batch_builder->toArray());
  }
  else {
    _reassign_user_content_anonymize_chunk_comments($comments);
  }
}

/**
 * Helper function set anonymous as comment's owner.
 *
 * @param array $comments
 *   Array of comments to anonymize.
 */
function _reassign_user_content_anonymize_chunk_comments(array $comments): void {
  foreach ($comments as $comment) {
    $lang_codes = array_keys($comment->getTranslationLanguages());
    // For efficiency, manually save the original comment before applying any
    // changes.
    $comment->original = clone $comment;
    foreach ($lang_codes as $lang_code) {
      $comment_translated = $comment->getTranslation($lang_code);
      $comment_translated->setOwnerId(0);
      $comment_translated->setAuthorName(
        \Drupal::config('user.settings')->get('anonymous')
      );
    }
    $comment->save();
  }
}

/**
 * Anonymize array comments batch process callback.
 *
 * @param array $comments
 *   Array of comments.
 * @param mixed $context
 *   Batch context.
 */
function _reassign_user_content__anonymize_comments_batch_process(array $comments, &$context): void {
  if (!isset($context['sandbox']['progress'])) {
    $context['sandbox']['progress'] = 0;
    $context['sandbox']['max'] = count($comments);
    $context['sandbox']['comments'] = $comments;
  }

  // Process comments by groups of 5.
  $count = min(5, count($context['sandbox']['comments']));
  for ($i = 1; $i <= $count; $i++) {
    $comment = array_shift($context['sandbox']['comments']);
    // For each comment, set the uid to anonymous, and save it.
    _reassign_user_content_anonymize_chunk_comments([$comment]);
    // Store result for post-processing in the finished callback.
    $context['results'][] = Link::fromTextAndUrl($comment->label(), $comment->toUrl())->toString();
    // Update our progress information.
    $context['sandbox']['progress']++;
  }

  // Inform the batch engine that we are not finished,
  // and provide an estimation of the completion level we reached.
  if ($context['sandbox']['progress'] != $context['sandbox']['max']) {
    $context['finished'] = $context['sandbox']['progress'] / $context['sandbox']['max'];
  }
}

/**
 * Anonymize comments batch finished callback.
 *
 * @param mixed $success
 *   Success.
 * @param mixed $results
 *   Results.
 * @param mixed $operations
 *   Operations.
 */
function _reassign_user_content__anonymize_comments_batch_finished($success, $results, $operations): void {
  if ($success) {
    \Drupal::messenger()
      ->addStatus(t('The comments update has been performed.'));
  }
  else {
    \Drupal::messenger()
      ->addError(t('An error occurred and processing did not complete.'));
    $message = \Drupal::translation()
      ->formatPlural(count($results), '1 item successfully processed:', '@count items successfully processed:');
    $item_list = [
      '#theme' => 'item_list',
      '#items' => $results,
    ];
    $message .= \Drupal::service('renderer')->render($item_list);
    \Drupal::messenger()->addStatus($message);
  }
}

/**
 * Mass reassigns ownership of groups to given user.
 *
 * @param array $ids
 *   An array of group IDs.
 * @param mixed $uid
 *   User to assign groups to.
 * @param mixed $context
 *   Batch context.
 *
 * @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
 * @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
 * @throws \Drupal\Core\Entity\EntityStorageException
 */
function _reassign_user_content_group(array $ids, $uid, &$context): void {
  if (!isset($context['sandbox']['progress'])) {
    $context['sandbox']['progress'] = 0;
    $context['sandbox']['max'] = count($ids);
    $context['sandbox']['ids'] = $ids;
  }

  // Try to update 10 groups at a time.
  $ids = array_slice($context['sandbox']['ids'], $context['sandbox']['progress'], 10);

  $storage = \Drupal::entityTypeManager()->getStorage('group');
  foreach ($storage->loadMultiple($ids) as $group) {
    assert($group instanceof GroupInterface);
    $group->set('uid', $uid);
    $storage->save($group);
    $context['sandbox']['progress']++;
  }

  if ($context['sandbox']['progress'] != $context['sandbox']['max']) {
    $context['finished'] = $context['sandbox']['progress'] / $context['sandbox']['max'];
  }
}

Главная | Обратная связь

drupal hosting | друпал хостинг | it patrol .inc