Updated to Drupal 8.6.4, which is PHP 7.3 friendly. Also updated HTMLaw library....
[yaffs-website] / web / core / modules / content_translation / src / ContentTranslationHandler.php
index c1688b9d64775e32f02b700953f44b854de5f035..0fef1bed47faabed957c4da3a6a409cbe902266b 100644 (file)
@@ -5,6 +5,7 @@ namespace Drupal\content_translation;
 use Drupal\Core\Access\AccessResult;
 use Drupal\Core\DependencyInjection\DependencySerializationTrait;
 use Drupal\Core\Entity\EntityChangedInterface;
+use Drupal\Core\Entity\EntityChangesDetectionTrait;
 use Drupal\Core\Entity\EntityHandlerInterface;
 use Drupal\Core\Entity\EntityInterface;
 use Drupal\Core\Entity\EntityManagerInterface;
@@ -13,8 +14,10 @@ use Drupal\Core\Field\BaseFieldDefinition;
 use Drupal\Core\Form\FormStateInterface;
 use Drupal\Core\Language\LanguageInterface;
 use Drupal\Core\Language\LanguageManagerInterface;
+use Drupal\Core\Messenger\MessengerInterface;
 use Drupal\Core\Render\Element;
 use Drupal\Core\Session\AccountInterface;
+use Drupal\Core\StringTranslation\StringTranslationTrait;
 use Drupal\user\Entity\User;
 use Drupal\user\EntityOwnerInterface;
 use Symfony\Component\DependencyInjection\ContainerInterface;
@@ -25,7 +28,10 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
  * @ingroup entity_api
  */
 class ContentTranslationHandler implements ContentTranslationHandlerInterface, EntityHandlerInterface {
+
+  use EntityChangesDetectionTrait;
   use DependencySerializationTrait;
+  use StringTranslationTrait;
 
   /**
    * The type of the entity being translated.
@@ -55,6 +61,13 @@ class ContentTranslationHandler implements ContentTranslationHandlerInterface, E
    */
   protected $manager;
 
+  /**
+   * The entity type manager.
+   *
+   * @var \Drupal\Core\Entity\EntityTypeManagerInterface
+   */
+  protected $entityTypeManager;
+
   /**
    * The current user.
    *
@@ -70,6 +83,13 @@ class ContentTranslationHandler implements ContentTranslationHandlerInterface, E
    */
   protected $fieldStorageDefinitions;
 
+  /**
+   * The messenger service.
+   *
+   * @var \Drupal\Core\Messenger\MessengerInterface
+   */
+  protected $messenger;
+
   /**
    * Initializes an instance of the content translation controller.
    *
@@ -83,14 +103,18 @@ class ContentTranslationHandler implements ContentTranslationHandlerInterface, E
    *   The entity manager.
    * @param \Drupal\Core\Session\AccountInterface $current_user
    *   The current user.
+   * @param \Drupal\Core\Messenger\MessengerInterface $messenger
+   *   The messenger service.
    */
-  public function __construct(EntityTypeInterface $entity_type, LanguageManagerInterface $language_manager, ContentTranslationManagerInterface $manager, EntityManagerInterface $entity_manager, AccountInterface $current_user) {
+  public function __construct(EntityTypeInterface $entity_type, LanguageManagerInterface $language_manager, ContentTranslationManagerInterface $manager, EntityManagerInterface $entity_manager, AccountInterface $current_user, MessengerInterface $messenger) {
     $this->entityTypeId = $entity_type->id();
     $this->entityType = $entity_type;
     $this->languageManager = $language_manager;
     $this->manager = $manager;
+    $this->entityTypeManager = $entity_manager;
     $this->currentUser = $current_user;
     $this->fieldStorageDefinitions = $entity_manager->getLastInstalledFieldStorageDefinitions($this->entityTypeId);
+    $this->messenger = $messenger;
   }
 
   /**
@@ -102,7 +126,8 @@ class ContentTranslationHandler implements ContentTranslationHandlerInterface, E
       $container->get('language_manager'),
       $container->get('content_translation.manager'),
       $container->get('entity.manager'),
-      $container->get('current_user')
+      $container->get('current_user'),
+      $container->get('messenger')
     );
   }
 
@@ -116,6 +141,7 @@ class ContentTranslationHandler implements ContentTranslationHandlerInterface, E
       ->setLabel(t('Translation source'))
       ->setDescription(t('The source language from which this translation was created.'))
       ->setDefaultValue(LanguageInterface::LANGCODE_NOT_SPECIFIED)
+      ->setInitialValue(LanguageInterface::LANGCODE_NOT_SPECIFIED)
       ->setRevisionable(TRUE)
       ->setTranslatable(TRUE);
 
@@ -123,6 +149,7 @@ class ContentTranslationHandler implements ContentTranslationHandlerInterface, E
       ->setLabel(t('Translation outdated'))
       ->setDescription(t('A boolean indicating whether this translation needs to be updated.'))
       ->setDefaultValue(FALSE)
+      ->setInitialValue(FALSE)
       ->setRevisionable(TRUE)
       ->setTranslatable(TRUE);
 
@@ -142,6 +169,7 @@ class ContentTranslationHandler implements ContentTranslationHandlerInterface, E
         ->setLabel(t('Translation status'))
         ->setDescription(t('A boolean indicating whether the translation is visible to non-translators.'))
         ->setDefaultValue(TRUE)
+        ->setInitialValue(TRUE)
         ->setRevisionable(TRUE)
         ->setTranslatable(TRUE);
     }
@@ -266,6 +294,8 @@ class ContentTranslationHandler implements ContentTranslationHandlerInterface, E
    * {@inheritdoc}
    */
   public function entityFormAlter(array &$form, FormStateInterface $form_state, EntityInterface $entity) {
+    /** @var \Drupal\Core\Entity\ContentEntityInterface $entity */
+
     $form_object = $form_state->getFormObject();
     $form_langcode = $form_object->getFormLangcode($form_state);
     $entity_langcode = $entity->getUntranslated()->language()->getId();
@@ -360,7 +390,12 @@ class ContentTranslationHandler implements ContentTranslationHandlerInterface, E
             break;
           }
         }
-        $access = $this->getTranslationAccess($entity, 'delete')->isAllowed() || ($entity->access('delete') && $this->entityType->hasLinkTemplate('delete-form'));
+        /** @var \Drupal\Core\Access\AccessResultInterface $delete_access */
+        $delete_access = \Drupal::service('content_translation.delete_access')->checkAccess($entity);
+        $access = $delete_access->isAllowed() && (
+          $this->getTranslationAccess($entity, 'delete')->isAllowed() ||
+          ($entity->access('delete') && $this->entityType->hasLinkTemplate('delete-form'))
+        );
         $form['actions']['delete_translation'] = [
           '#type' => 'submit',
           '#value' => t('Delete translation'),
@@ -423,12 +458,19 @@ class ContentTranslationHandler implements ContentTranslationHandlerInterface, E
       ];
 
       $translate = !$new_translation && $metadata->isOutdated();
-      if (!$translate) {
+      $outdated_access = !ContentTranslationManager::isPendingRevisionSupportEnabled($entity->getEntityTypeId(), $entity->bundle());
+      if (!$outdated_access) {
+        $form['content_translation']['outdated'] = [
+          '#markup' => $this->t('Translations cannot be flagged as outdated when content is moderated.'),
+        ];
+      }
+      elseif (!$translate) {
         $form['content_translation']['retranslate'] = [
           '#type' => 'checkbox',
           '#title' => t('Flag other translations as outdated'),
           '#default_value' => FALSE,
           '#description' => t('If you made a significant change, which means the other translations should be updated, you can flag all translations of this content as outdated. This will not change any other property of them, like whether they are published or not.'),
+          '#access' => $outdated_access,
         ];
       }
       else {
@@ -437,6 +479,7 @@ class ContentTranslationHandler implements ContentTranslationHandlerInterface, E
           '#title' => t('This translation needs to be updated'),
           '#default_value' => $translate,
           '#description' => t('When this option is checked, this translation needs to be updated. Uncheck when the translation is up to date again.'),
+          '#access' => $outdated_access,
         ];
         $form['content_translation']['#open'] = TRUE;
       }
@@ -469,10 +512,6 @@ class ContentTranslationHandler implements ContentTranslationHandlerInterface, E
         '#default_value' => $new_translation || !$date ? '' : format_date($date, 'custom', 'Y-m-d H:i:s O'),
       ];
 
-      if (isset($language_widget)) {
-        $language_widget['#multilingual'] = TRUE;
-      }
-
       $form['#process'][] = [$this, 'entityFormSharedElements'];
     }
 
@@ -509,6 +548,20 @@ class ContentTranslationHandler implements ContentTranslationHandlerInterface, E
       $ignored_types = array_flip(['actions', 'value', 'hidden', 'vertical_tabs', 'token', 'details']);
     }
 
+    /** @var \Drupal\Core\Entity\ContentEntityForm $form_object */
+    $form_object = $form_state->getFormObject();
+    /** @var \Drupal\Core\Entity\ContentEntityInterface $entity */
+    $entity = $form_object->getEntity();
+    $display_translatability_clue = !$entity->isDefaultTranslationAffectedOnly();
+    $hide_untranslatable_fields = $entity->isDefaultTranslationAffectedOnly() && !$entity->isDefaultTranslation();
+    $translation_form = $form_state->get(['content_translation', 'translation_form']);
+    $display_warning = FALSE;
+
+    // We use field definitions to identify untranslatable field widgets to be
+    // hidden. Fields that are not involved in translation changes checks should
+    // not be affected by this logic (the "revision_log" field, for instance).
+    $field_definitions = array_diff_key($entity->getFieldDefinitions(), array_flip($this->getFieldsToSkipFromTranslationChangesCheck($entity)));
+
     foreach (Element::children($element) as $key) {
       if (!isset($element[$key]['#type'])) {
         $this->entityFormSharedElements($element[$key], $form_state, $form);
@@ -521,10 +574,17 @@ class ContentTranslationHandler implements ContentTranslationHandlerInterface, E
         // Elements are considered to be non multilingual by default.
         if (empty($element[$key]['#multilingual'])) {
           // If we are displaying a multilingual entity form we need to provide
-          // translatability clues, otherwise the shared form elements should be
-          // hidden.
-          if (!$form_state->get(['content_translation', 'translation_form'])) {
-            $this->addTranslatabilityClue($element[$key]);
+          // translatability clues, otherwise the non-multilingual form elements
+          // should be hidden.
+          if (!$translation_form) {
+            if ($display_translatability_clue) {
+              $this->addTranslatabilityClue($element[$key]);
+            }
+            // Hide widgets for untranslatable fields.
+            if ($hide_untranslatable_fields && isset($field_definitions[$key])) {
+              $element[$key]['#access'] = FALSE;
+              $display_warning = TRUE;
+            }
           }
           else {
             $element[$key]['#access'] = FALSE;
@@ -533,6 +593,11 @@ class ContentTranslationHandler implements ContentTranslationHandlerInterface, E
       }
     }
 
+    if ($display_warning && !$form_state->isSubmitted() && !$form_state->isRebuilding()) {
+      $url = $entity->getUntranslated()->toUrl('edit-form')->toString();
+      $this->messenger->addWarning($this->t('Fields that apply to all languages are hidden to avoid conflicting changes. <a href=":url">Edit them on the original language form</a>.', [':url' => $url]));
+    }
+
     return $element;
   }
 
@@ -640,7 +705,7 @@ class ContentTranslationHandler implements ContentTranslationHandlerInterface, E
     // after the entity has been validated, so that it does not break the
     // EntityChanged constraint validator. The content translation metadata
     // field for the changed timestamp  does not have such a constraint defined
-    // at the moment, but it is correct to update it's value in a submission
+    // at the moment, but it is correct to update its value in a submission
     // handler as well and have the same logic like in the Form API.
     if ($entity->hasField('content_translation_changed')) {
       $metadata = $this->manager->getTranslationMetadata($entity);
@@ -665,7 +730,7 @@ class ContentTranslationHandler implements ContentTranslationHandlerInterface, E
       'target' => $form_object->getFormLangcode($form_state),
     ]);
     $languages = $this->languageManager->getLanguages();
-    drupal_set_message(t('Source language set to: %language', ['%language' => $languages[$source]->getName()]));
+    $this->messenger->addStatus(t('Source language set to: %language', ['%language' => $languages[$source]->getName()]));
   }
 
   /**
@@ -674,10 +739,10 @@ class ContentTranslationHandler implements ContentTranslationHandlerInterface, E
    * Takes care of entity deletion.
    */
   public function entityFormDelete($form, FormStateInterface $form_state) {
-    $form_object = $form_state->getFormObject()->getEntity();
+    $form_object = $form_state->getFormObject();
     $entity = $form_object->getEntity();
     if (count($entity->getTranslationLanguages()) > 1) {
-      drupal_set_message(t('This will delete all the translations of %label.', ['%label' => $entity->label()]), 'warning');
+      $this->messenger->addWarning(t('This will delete all the translations of %label.', ['%label' => $entity->label()]));
     }
   }