You are here

public function ContentTranslationController::overview in Drupal 9

Same name and namespace in other branches
  1. 8 core/modules/content_translation/src/Controller/ContentTranslationController.php \Drupal\content_translation\Controller\ContentTranslationController::overview()

Builds the translations overview page.

Parameters

\Drupal\Core\Routing\RouteMatchInterface $route_match: The route match.

string $entity_type_id: (optional) The entity type ID.

Return value

array Array of page elements to render.

File

core/modules/content_translation/src/Controller/ContentTranslationController.php, line 102

Class

ContentTranslationController
Base class for entity translation controllers.

Namespace

Drupal\content_translation\Controller

Code

public function overview(RouteMatchInterface $route_match, $entity_type_id = NULL) {

  /** @var \Drupal\Core\Entity\ContentEntityInterface $entity */
  $entity = $route_match
    ->getParameter($entity_type_id);
  $account = $this
    ->currentUser();
  $handler = $this
    ->entityTypeManager()
    ->getHandler($entity_type_id, 'translation');
  $manager = $this->manager;
  $entity_type = $entity
    ->getEntityType();
  $use_latest_revisions = $entity_type
    ->isRevisionable() && ContentTranslationManager::isPendingRevisionSupportEnabled($entity_type_id, $entity
    ->bundle());

  // Start collecting the cacheability metadata, starting with the entity and
  // later merge in the access result cacheability metadata.
  $cacheability = CacheableMetadata::createFromObject($entity);
  $languages = $this
    ->languageManager()
    ->getLanguages();
  $original = $entity
    ->getUntranslated()
    ->language()
    ->getId();
  $translations = $entity
    ->getTranslationLanguages();
  $field_ui = $this
    ->moduleHandler()
    ->moduleExists('field_ui') && $account
    ->hasPermission('administer ' . $entity_type_id . ' fields');
  $rows = [];
  $show_source_column = FALSE;

  /** @var \Drupal\Core\Entity\ContentEntityStorageInterface $storage */
  $storage = $this
    ->entityTypeManager()
    ->getStorage($entity_type_id);
  $default_revision = $storage
    ->load($entity
    ->id());
  if ($this
    ->languageManager()
    ->isMultilingual()) {

    // Determine whether the current entity is translatable.
    $translatable = FALSE;
    foreach ($this->entityFieldManager
      ->getFieldDefinitions($entity_type_id, $entity
      ->bundle()) as $instance) {
      if ($instance
        ->isTranslatable()) {
        $translatable = TRUE;
        break;
      }
    }

    // Show source-language column if there are non-original source langcodes.
    $additional_source_langcodes = array_filter(array_keys($translations), function ($langcode) use ($entity, $original, $manager) {
      $source = $manager
        ->getTranslationMetadata($entity
        ->getTranslation($langcode))
        ->getSource();
      return $source != $original && $source != LanguageInterface::LANGCODE_NOT_SPECIFIED;
    });
    $show_source_column = !empty($additional_source_langcodes);
    foreach ($languages as $language) {
      $language_name = $language
        ->getName();
      $langcode = $language
        ->getId();

      // If the entity type is revisionable, we may have pending revisions
      // with translations not available yet in the default revision. Thus we
      // need to load the latest translation-affecting revision for each
      // language to be sure we are listing all available translations.
      if ($use_latest_revisions) {
        $entity = $default_revision;
        $latest_revision_id = $storage
          ->getLatestTranslationAffectedRevisionId($entity
          ->id(), $langcode);
        if ($latest_revision_id) {

          /** @var \Drupal\Core\Entity\ContentEntityInterface $latest_revision */
          $latest_revision = $storage
            ->loadRevision($latest_revision_id);

          // Make sure we do not list removed translations, i.e. translations
          // that have been part of a default revision but no longer are.
          if (!$latest_revision
            ->wasDefaultRevision() || $default_revision
            ->hasTranslation($langcode)) {
            $entity = $latest_revision;
          }
        }
        $translations = $entity
          ->getTranslationLanguages();
      }
      $options = [
        'language' => $language,
      ];
      $add_url = $entity
        ->toUrl('drupal:content-translation-add', $options)
        ->setRouteParameter('source', $original)
        ->setRouteParameter('target', $language
        ->getId());
      $edit_url = $entity
        ->toUrl('drupal:content-translation-edit', $options)
        ->setRouteParameter('language', $language
        ->getId());
      $delete_url = $entity
        ->toUrl('drupal:content-translation-delete', $options)
        ->setRouteParameter('language', $language
        ->getId());
      $operations = [
        'data' => [
          '#type' => 'operations',
          '#links' => [],
        ],
      ];
      $links =& $operations['data']['#links'];
      if (array_key_exists($langcode, $translations)) {

        // Existing translation in the translation set: display status.
        $translation = $entity
          ->getTranslation($langcode);
        $metadata = $manager
          ->getTranslationMetadata($translation);
        $source = $metadata
          ->getSource() ?: LanguageInterface::LANGCODE_NOT_SPECIFIED;
        $is_original = $langcode == $original;
        $label = $entity
          ->getTranslation($langcode)
          ->label();
        $link = isset($links->links[$langcode]['url']) ? $links->links[$langcode] : [
          'url' => $entity
            ->toUrl(),
        ];
        if (!empty($link['url'])) {
          $link['url']
            ->setOption('language', $language);
          $row_title = Link::fromTextAndUrl($label, $link['url'])
            ->toString();
        }
        if (empty($link['url'])) {
          $row_title = $is_original ? $label : $this
            ->t('n/a');
        }

        // If the user is allowed to edit the entity we point the edit link to
        // the entity form, otherwise if we are not dealing with the original
        // language we point the link to the translation form.
        $update_access = $entity
          ->access('update', NULL, TRUE);
        $translation_access = $handler
          ->getTranslationAccess($entity, 'update');
        $cacheability = $cacheability
          ->merge(CacheableMetadata::createFromObject($update_access))
          ->merge(CacheableMetadata::createFromObject($translation_access));
        if ($update_access
          ->isAllowed() && $entity_type
          ->hasLinkTemplate('edit-form')) {
          $links['edit']['url'] = $entity
            ->toUrl('edit-form');
          $links['edit']['language'] = $language;
        }
        elseif (!$is_original && $translation_access
          ->isAllowed()) {
          $links['edit']['url'] = $edit_url;
        }
        if (isset($links['edit'])) {
          $links['edit']['title'] = $this
            ->t('Edit');
        }
        $status = [
          'data' => [
            '#type' => 'inline_template',
            '#template' => '<span class="status">{% if status %}{{ "Published"|t }}{% else %}{{ "Not published"|t }}{% endif %}</span>{% if outdated %} <span class="marker">{{ "outdated"|t }}</span>{% endif %}',
            '#context' => [
              'status' => $metadata
                ->isPublished(),
              'outdated' => $metadata
                ->isOutdated(),
            ],
          ],
        ];
        if ($is_original) {
          $language_name = $this
            ->t('<strong>@language_name (Original language)</strong>', [
            '@language_name' => $language_name,
          ]);
          $source_name = $this
            ->t('n/a');
        }
        else {

          /** @var \Drupal\Core\Access\AccessResultInterface $delete_route_access */
          $delete_route_access = \Drupal::service('content_translation.delete_access')
            ->checkAccess($translation);
          $cacheability
            ->addCacheableDependency($delete_route_access);
          if ($delete_route_access
            ->isAllowed()) {
            $source_name = isset($languages[$source]) ? $languages[$source]
              ->getName() : $this
              ->t('n/a');
            $delete_access = $entity
              ->access('delete', NULL, TRUE);
            $translation_access = $handler
              ->getTranslationAccess($entity, 'delete');
            $cacheability
              ->addCacheableDependency($delete_access)
              ->addCacheableDependency($translation_access);
            if ($delete_access
              ->isAllowed() && $entity_type
              ->hasLinkTemplate('delete-form')) {
              $links['delete'] = [
                'title' => $this
                  ->t('Delete'),
                'url' => $entity
                  ->toUrl('delete-form'),
                'language' => $language,
              ];
            }
            elseif ($translation_access
              ->isAllowed()) {
              $links['delete'] = [
                'title' => $this
                  ->t('Delete'),
                'url' => $delete_url,
              ];
            }
          }
          else {
            $this
              ->messenger()
              ->addWarning($this
              ->t('The "Delete translation" action is only available for published translations.'), FALSE);
          }
        }
      }
      else {

        // No such translation in the set yet: help user to create it.
        $row_title = $source_name = $this
          ->t('n/a');
        $source = $entity
          ->language()
          ->getId();
        $create_translation_access = $handler
          ->getTranslationAccess($entity, 'create');
        $cacheability = $cacheability
          ->merge(CacheableMetadata::createFromObject($create_translation_access));
        if ($source != $langcode && $create_translation_access
          ->isAllowed()) {
          if ($translatable) {
            $links['add'] = [
              'title' => $this
                ->t('Add'),
              'url' => $add_url,
            ];
          }
          elseif ($field_ui) {
            $url = new Url('language.content_settings_page');

            // Link directly to the fields tab to make it easier to find the
            // setting to enable translation on fields.
            $links['nofields'] = [
              'title' => $this
                ->t('No translatable fields'),
              'url' => $url,
            ];
          }
        }
        $status = $this
          ->t('Not translated');
      }
      if ($show_source_column) {
        $rows[] = [
          $language_name,
          $row_title,
          $source_name,
          $status,
          $operations,
        ];
      }
      else {
        $rows[] = [
          $language_name,
          $row_title,
          $status,
          $operations,
        ];
      }
    }
  }
  if ($show_source_column) {
    $header = [
      $this
        ->t('Language'),
      $this
        ->t('Translation'),
      $this
        ->t('Source language'),
      $this
        ->t('Status'),
      $this
        ->t('Operations'),
    ];
  }
  else {
    $header = [
      $this
        ->t('Language'),
      $this
        ->t('Translation'),
      $this
        ->t('Status'),
      $this
        ->t('Operations'),
    ];
  }
  $build['#title'] = $this
    ->t('Translations of %label', [
    '%label' => $entity
      ->label(),
  ]);

  // Add metadata to the build render array to let other modules know about
  // which entity this is.
  $build['#entity'] = $entity;
  $cacheability
    ->addCacheTags($entity
    ->getCacheTags())
    ->applyTo($build);
  $build['content_translation_overview'] = [
    '#theme' => 'table',
    '#header' => $header,
    '#rows' => $rows,
  ];
  return $build;
}