You are here

public function ContentEntityConflictHandler::entityFormEntityBuilder in Conflict 8.2

Entity builder method.

Parameters

string $entity_type_id: The entity type ID.

\Drupal\Core\Entity\EntityInterface $entity: The entity.

$form: The entity form.

\Drupal\Core\Form\FormStateInterface $form_state: The current state of the form.

See also

\Drupal\conflict\Entity\ContentEntityConflictHandler::entityFormAlter()

File

src/Entity/ContentEntityConflictHandler.php, line 172

Class

ContentEntityConflictHandler

Namespace

Drupal\conflict\Entity

Code

public function entityFormEntityBuilder($entity_type_id, EntityInterface $entity, &$form, FormStateInterface $form_state) {

  // Run only as part of the final form level submission.
  if (!$this
    ->isFormLevelSubmission($form_state)) {
    return;
  }
  if ($entity instanceof ContentEntityInterface && !$entity
    ->isNew()) {
    if ($entity
      ->isDefaultRevision()) {

      /** @var \Drupal\Core\Entity\ContentEntityInterface $entity_server */
      $id = $entity
        ->id();
      $entity_server = $this->storage
        ->loadUnchanged($id);
    }
    else {

      // TODO - how to deal with forward revisions?
      return;
    }
    $input = $form_state
      ->getUserInput();
    $hash_path = $form['#parents'];
    $hash_path[] = 'conflict_entity_original_hash';
    $hash = NestedArray::getValue($input, $hash_path);

    /** @var \Drupal\Core\Entity\ContentEntityInterface $entity_original */
    $entity_original = unserialize($this->keyValueOriginalEntity
      ->get($hash));

    // Set the original entity as it might have changed. The original entity
    // in our context is the one that was used to build the form initially
    // and not the unchanged entity.
    $entity->{static::CONFLICT_ENTITY_ORIGINAL} = $entity_original;

    // Currently we do not support concurrent editing in the following cases:
    //  - editing a translation that is removed on the newest version.
    //  - while creating a new translation.
    $edited_langcode = $entity
      ->language()
      ->getId();
    if (!$entity_server
      ->hasTranslation($edited_langcode)) {
      if ($entity_original
        ->hasTranslation($edited_langcode)) {

        // Currently being on a translation that has been removed in the
        // newest version.
        $form_state
          ->setError($form, t('You are editing a translation, that has been removed meanwhile. As a result, your changes cannot be saved.'));
        return;
      }
      else {

        // @todo A new translation is being added. Currently we do not have
        // any support for concurrent editing during translating content. If
        // the entity has been modified meanwhile then the
        // EntityChangedConstraintValidator will fail.
        return;
      }
    }
    else {
      if (!$entity_original
        ->hasTranslation($edited_langcode)) {
        $form_state
          ->setError($form, t('You are creating a translation, that has been created meanwhile. As a result, your changes cannot be saved.'));
        return;
      }
    }

    // Work directly with the entity translations.
    $entity_server = $entity_server
      ->getTranslation($edited_langcode);
    $entity_original = $entity_original
      ->getTranslation($edited_langcode);

    // Check if the entity requires a merge.
    $needs_merge = $this
      ->needsMerge($entity, $entity_original, $entity_server, FALSE);
    if ($needs_merge) {

      /** @var \Drupal\Core\Entity\Display\EntityFormDisplayInterface $form_display */
      $form_display = $form_state
        ->getFormObject()
        ->getFormDisplay($form_state);

      // Append each field's widget to the field so that further processing
      // can access it.
      foreach (array_keys($form_display
        ->getComponents()) as $field_name) {
        if ($entity
          ->getFieldDefinition($field_name)) {
          $entity
            ->get($field_name)->conflictWidget = $form_display
            ->getRenderer($field_name);
        }
      }

      // Auto merge changes in other translations.
      $this
        ->autoMergeNonEditedTranslations($entity, $entity_server);

      // Auto merge changes in non-editable fields.
      $this
        ->autoMergeNonEditableFields($entity, $entity_server, $form_display);

      // Auto merge non-changed fields.
      $this
        ->autoMergeNotTouchedFields($entity, $entity_original, $entity_server, $form, $form_state);

      // Auto merge entity metadata.
      $this
        ->autoMergeEntityMetadata($entity, $entity_server, $form, $form_state);

      // Run the entities through the event system for conflict discovery
      // and resolution.
      $context = new ParameterBag([
        'form' => $form,
        'form_state' => $form_state,
        'form_display' => $form_display,
      ]);

      // Disable the merge remote only changes strategy, which is enabled by
      // default.
      // TODO reconsider the decision for having all strategies enabled by
      // default! They should be instead enabled through configuration and/or
      // hooks/events.
      $context
        ->set('merge_strategy.disabled', [
        'conflict.merge_remote_only_changes',
      ]);
      $conflicts = $this->conflictManager
        ->resolveConflicts($entity, $entity_server, $entity_original, $entity, $context);

      // In case the entity still has conflicts then a user interaction is
      // needed.
      $needs_merge = !empty($conflicts) || $this
        ->needsMerge($entity, $entity_original, $entity_server, TRUE);
      if ($needs_merge) {

        // Prepare the entity for conflict resolution.
        $this
          ->prepareConflictResolution($entity, $entity_server);

        // If the entity supports conflict UI merge then add the path to it to
        // the form state storage, otherwise flag the form with an error as it
        // would've been flagged by the entity constraint "EntityChanged".
        // @see \Drupal\Core\Entity\Plugin\Validation\Constraint\EntityChangedConstraint::$message
        // @see \Drupal\Core\Entity\Plugin\Validation\Constraint\EntityChangedConstraintValidator::validate()
        if ($this->entityType
          ->get('conflict_ui_merge_supported') && !(bool) $form_state
          ->get('manual-merge-not-possible')) {
          $path = implode('.', $form['#parents']);
          $conflict_paths = $form_state
            ->get('conflict.paths') ?: [];
          $conflict_paths[$path] = [
            'entity_type' => $entity_type_id,
            'entity_id' => $entity
              ->id(),
          ];
          $form_state
            ->set('conflict.paths', $conflict_paths);
        }
        else {
          $message = t('The content has either been modified by another user, or you have already submitted modifications. As a result, your changes cannot be saved.');
          $message .= ' ' . t('Unfortunately no conflict resolution could be provided for the set of changes.');
          $form_state
            ->setError($form, $message);
        }
      }
    }
  }
}