devel_wizard-2.x-dev/templates/spell/entity_type/content/controller.php.twig

templates/spell/entity_type/content/controller.php.twig
{%
  include '@devel_wizard/php/devel_wizard.php.file.header.php.twig'
  with {
    'namespace': content.namespace,
  }
%}

use Drupal\Core\Config\Entity\ConfigEntityInterface;
use {{ content.interface_fqn }};
{% if goal == 'bundleable' %}
use {{ config.interface_fqn }};
use {{ config.namespace }}\Comparer;
{% endif %}
use {{ content.namespace }}\StorageInterface as {{ content.idUpperCamel }}StorageInterface;
use Drupal\Component\Utility\Xss;
use Drupal\Core\Config\Entity\ConfigEntityType;
use Drupal\Core\Controller\ControllerBase;
use Drupal\Core\Datetime\DateFormatterInterface;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityRepositoryInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Link;
use Drupal\Core\Render\RendererInterface;
use Drupal\Core\StringTranslation\TranslatableMarkup;
use Drupal\Core\Url;
use Symfony\Component\DependencyInjection\ContainerInterface;

class Controller extends ControllerBase {

  protected string $entityTypeId = '{{ content.id }}';

  protected int $revisionHistoryItemPerPage = 50;

  protected function getRevisionHistoryItemPerPage(): int {
    return $this->revisionHistoryItemPerPage;
  }

  /**
   * {@inheritdoc}
   *
   * @return static
   */
  public static function create(ContainerInterface $container) {
    // @phpstan-ignore-next-line
    return new static(
      $container->get('entity_type.manager'),
      $container->get('date.formatter'),
      $container->get('renderer'),
      $container->get('entity.{{ content.id }}.permission_provider'),
      $container->get('entity.repository'),
    );
  }

  public function __construct(
    EntityTypeManagerInterface $entityTypeManager,
    protected DateFormatterInterface $dateFormatter,
    protected RendererInterface $renderer,
    protected PermissionProviderInterface $permissionProvider,
    protected EntityRepositoryInterface $entityRepository,
  ) {
    $this->entityTypeManager = $entityTypeManager;
  }

  /**
   * Page callback.
   *
   * @return array{{ '<' }}string, mixed>|\Symfony\Component\HttpFoundation\Response
   *   Render array or Response object.
   */
  public function addPageContent() {
    $etm = $this->entityTypeManager();
    /* @noinspection PhpUnhandledExceptionInspection */
    $contentEntityType = $etm->getDefinition($this->entityTypeId);
    $bundleEntityTypeId = $contentEntityType->getBundleEntityType();

    if (!$bundleEntityTypeId) {
      return $this->redirect("entity.{{ '{' }}$this->entityTypeId{{ '}' }}.add_form");
    }

    /* @noinspection PhpUnhandledExceptionInspection */
    /** @var \Drupal\Core\Config\Entity\ConfigEntityType $bundleEntityType */
    $bundleEntityType = $etm->getDefinition($bundleEntityTypeId);

    $build = [
      '#theme' => 'entity_add_list',
      '#bundles' => [],
      '#cache' => [
        'tags' => $bundleEntityType->getListCacheTags(),
      ],
      '#add_bundle_message' => $this->addPageBundleMessage($bundleEntityType),
    ];

    $contentAch = $etm->getAccessControlHandler($this->entityTypeId);
    /* @noinspection PhpUnhandledExceptionInspection */
    /** @var \Drupal\Core\Config\Entity\ConfigEntityInterface[] $bundles */
    $bundles = $etm
      ->getStorage($bundleEntityTypeId)
      ->loadMultiple();
    foreach ($bundles as $bundle) {
      $access = $contentAch->createAccess((string) $bundle->id(), NULL, [], TRUE);
      if ($access->isAllowed()) {
        $build['#bundles'][$bundle->id()] = $this->addPageBundleBuild($bundle);
      }

      $this->renderer->addCacheableDependency($build, $access);
    }

    if (count($build['#bundles']) === 1) {
      $bundleMeta = reset($build['#bundles']);
      $bundle = $bundleMeta['entity'];
      $contentEntityTypeId = $bundle->getEntityType()->getBundleOf();

      return $this->redirect(
        "entity.$contentEntityTypeId.add_form",
        [
          $bundle->getEntityTypeId() => $bundle->id(),
        ],
      );
    }

    return $build;
  }

  protected function addPageBundleMessage(ConfigEntityType $config): TranslatableMarkup {
    $configAch = $this
      ->entityTypeManager()
      ->getAccessControlHandler($config->id());

    /* @noinspection HtmlUnknownTarget */
    return $configAch->createAccess() ?
      $this->t(
        'There are no @entityType.labelPlural. <a href=":entityType.url.add">Add new.</a>',
        [
          '@entityType.labelPlural' => $config->getPluralLabel(),
          ':entityType.url.add' => Url::fromRoute("entity.{$config->id()}.add_form")->toString(),
        ],
      )
      : $this->t(
      'There are no @entityType.labelPlural.',
        [
          '@entityType.labelPlural' => $config->getPluralLabel(),
        ],
      );
  }

  /**
   * @return array{{ '<' }}string, mixed>
   */
  protected function addPageBundleBuild(ConfigEntityInterface $bundle): array {
    $contentEntityTypeId = $bundle->getEntityType()->getBundleOf();

    return [
      'entity' => $bundle,
      'weight' => $bundle->get('weight'),
      'label' => $bundle->label(),
      'add_link' => Link::createFromRoute(
        $bundle->label(),
        "entity.$contentEntityTypeId.add_form",
        [
          $bundle->getEntityTypeId() => $bundle->id(),
        ],
      ),
      'description' => $bundle->get('description'),
    ];
  }

  /**
   * @return array{{ '<' }}string, mixed>
   *
   * @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
   * @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
   */
  public function addForm(?EntityInterface $bundle): array {
    $etm = $this->entityTypeManager();
    /* @noinspection PhpUnhandledExceptionInspection */
    $definition = $etm->getDefinition($this->entityTypeId);
    $bundleEntityTypeId = $definition->getBundleEntityType();

    assert(
      ($bundleEntityTypeId && $bundle) || (!$bundleEntityTypeId && !$bundle),
      sprintf(
        'Inconsistent entity type definition: %s; $bundleEntityTypeId=%s; $bundle->id()=%s',
        $this->entityTypeId,
        (string) ($bundle?->getEntityTypeId() ?: '-missing-'),
        (string) ($bundle?->id() ?: '-missing-'),
      ),
    );

    assert(
      !$bundleEntityTypeId && !$bundle,
      sprintf(
        'entity type "%s" is bundleable, but no "%s" instance is given as bundle',
        $this->entityTypeId,
        $bundleEntityTypeId,
      ),
    );

    // @phpstan-ignore-next-line
    return $bundle && $bundleEntityTypeId
      ? $this->addFormBundleable($bundle)
      : $this->addFormFlat();
  }

  /**
   * @return array{{ '<' }}string, mixed>
   *
   * @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
   * @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
   */
  public function addFormFlat(): array {
    $etm = $this->entityTypeManager();
    /* @noinspection PhpUnhandledExceptionInspection */
    $definition = $etm->getDefinition($this->entityTypeId);
    $definition->getKey('bundle');
    /* @noinspection PhpUnhandledExceptionInspection */
    $entity = $etm
      ->getStorage($this->entityTypeId)
      ->create([
        $definition->getKey('bundle') => $this->entityTypeId,
      ]);

    return $this
      ->entityFormBuilder()
      ->getForm($entity);
  }

  /**
   * Returns a form to add a new {{ content.id }}.
   *
   * @param \Drupal\Core\Entity\EntityInterface ${{ config.id }}
   *   The object which represent the bundle this content will be added to.
   *
   * @return array{{ '<' }}string, mixed>
   */
  public function addFormBundleable(EntityInterface ${{ config.id }}): array {
    $etm = $this->entityTypeManager();
    $bundleEntityType = ${{ config.id }}->getEntityType();
    /* @noinspection PhpUnhandledExceptionInspection */
    $bundleOf = $etm->getDefinition($bundleEntityType->getBundleOf());
    /* @noinspection PhpUnhandledExceptionInspection */
    $contentEntity = $etm
      ->getStorage($bundleOf->id())
      ->create([
        $bundleOf->getKey('bundle') => ${{ config.id }}->id(),
      ]);

    return $this
      ->entityFormBuilder()
      ->getForm($contentEntity);
  }

  /**
   * Route title callback.
   *
   * @return string|\Stringable|array{{ '<' }}string, mixed>
   */
  public function entityLabel(EntityInterface ${{ content.id }}): array {
    return [
      '#markup' => ${{ content.id }}->label(),
      '#allowed_tags' => Xss::getHtmlTagList(),
    ];
  }

  /**
   * Route title callback.
   *
   * @return string|\Stringable|array{{ '<' }}string, mixed>
   */
  public function revisionHistoryTitle({{ content.interface }} ${{ content.id }}) {
    return [
      '#markup' => $this->t(
        'Revisions of %entity.label @entityType.label',
        [
          '%entity.label' => ${{ content.id }}->label(),
          '@entityType.label' => ${{ content.id }}->getEntityType()->getLabel(),
        ]
      ),
      '#allowed_tags' => Xss::getHtmlTagList(),
    ];
  }

  /**
   * Route content callback.
   *
   * Generates an overview table of older revisions of a {{ content.label }}.
   *
   * @return string|int|array{{ '<' }}string, mixed>|\Psr\Http\Message\ResponseInterface
   *   An array as expected by \Drupal\Core\Render\RendererInterface::render().
   */
  public function revisionHistoryContent({{ content.interface }} ${{ content.id }}) {
    $account = $this->currentUser();
    $langCode = ${{ content.id }}->language()->getId();
    $languages = ${{ content.id }}->getTranslationLanguages();
    $hasTranslations = (count($languages) > 1);

    /* @noinspection PhpUnhandledExceptionInspection */
    /** @var \{{ content.namespace }}\StorageInterface $storage */
    $storage = $this->entityTypeManager()->getStorage(${{ content.id }}->getEntityTypeId());
    $entityTypeId = ${{ content.id }}->getEntityTypeId();

    $build = [
      'revisions' => [
        '#theme' => 'table',
        '#attached' => [],
        '#attributes' => [
          'class' => 'revision-table',
        ],
        '#header' => [
          'label' => $this->t('label'),
          'details' => $this->t('Details'),
          'operations' => $this->t('Operations'),
        ],
        '#rows' => [],
      ],
      'pager' => [
        '#type' => 'pager',
      ],
    ];

    $access = [
      'hasRevertPermission' => ${{ content.id }}->access('revision_revert', $account) && ${{ content.id }}->access('update'),
      'hasDeletePermission' => ${{ content.id }}->access('revision_delete', $account) && ${{ content.id }}->access('delete'),
    ];

    $defaultRevision = ${{ content.id }}->getRevisionId();
    $isCurrentRevisionDisplayed = FALSE;

    foreach ($this->getRevisionIds(${{ content.id }}, $storage) as $revisionId) {
      $revision = $storage->loadRevision($revisionId);

      if (!$revision->hasTranslation($langCode) || !$revision->getTranslation($langCode)->isRevisionTranslationAffected()) {
        continue;
      }

      // We treat also the latest translation-affecting revision as current
      // revision, if it was the default revision, as its values for the
      // current language will be the same of the current default revision in
      // this case.
      $isCurrentRevision = $revisionId == $defaultRevision || (!$isCurrentRevisionDisplayed && $revision->wasDefaultRevision());

      $linkText = $this->dateFormatter->format($revision->getRevisionCreationTime(), 'short');
      $linkRouteName = "entity.$entityTypeId.canonical";
      $linkParams = [
        $entityTypeId => ${{ content.id }}->id(),
      ];

      if ($isCurrentRevision) {
        $isCurrentRevisionDisplayed = TRUE;
      }
      else {
        $linkRouteName = "entity.$entityTypeId.revision_view";
        $linkParams["{$entityTypeId}_revision"] = $revisionId;
      }

      $link = Link::fromTextAndUrl($linkText, new Url($linkRouteName, $linkParams));

      $build['revisions']['#rows'][$revisionId] = [
        'data' => [
          'label' => [
            'data' => $this->getRevisionLabelElement($revision),
          ],
          'details' => [
            'data' => $this->getRevisionSubmissionElement($revision, $link),
          ],
          'operations' => [
            'data' => $this->getRevisionOperationElement($isCurrentRevision, ${{ content.id }}, $revision, $access, $hasTranslations),
          ],
        ],
      ];

      if ($isCurrentRevision) {
        $build['revisions']['#rows'][$revisionId]['class'][] = 'revision-current';
      }
    }

    return $build;
  }

  /**
   * Route title callback.
   *
   * @param int|string ${{ content.id }}_revision
   *
   * @return string|\Stringable|array{{ '<' }}string, mixed>
   */
  public function revisionViewTitle(${{ content.id }}_revision) {
    $etm = $this->entityTypeManager();

    /* @noinspection PhpUnhandledExceptionInspection */
    /** @var \{{ content.namespace }}\StorageInterface $storage */
    $storage = $etm->getStorage($this->entityTypeId);
    $revision = $storage->loadRevision(${{ content.id }}_revision);

    return [
      '#markup' => $revision->label(),
      '#allowed_tags' => Xss::getHtmlTagList(),
    ];
  }

  /**
   * Route content callback.
   *
   * @param string|int ${{ content.id }}_revision
   *
   * @return string|int|array|\Psr\Http\Message\ResponseInterface
   */
  public function revisionViewContent(${{ content.id }}_revision) {
    $etm = $this->entityTypeManager();

    /* @noinspection PhpUnhandledExceptionInspection */
    /** @var \{{ content.namespace }}\StorageInterface $storage */
    $storage = $etm->getStorage($this->entityTypeId);
    $revision = $storage->loadRevision(${{ content.id }}_revision);

    $this->entityRepository->getTranslationFromContext($revision);

    /* @noinspection PhpUnhandledExceptionInspection */
    /** @var \Drupal\Core\Entity\EntityViewBuilderInterface $viewController */
    $viewController = $etm->getHandler($revision->getEntityTypeId(), 'view_controller');

    // @todo Unset #cache.
    $page = $viewController->view($revision);

    return $page;
  }

  /**
   * @return int[]
   *   Revision IDs (in descending order).
   */
  protected function getRevisionIds({{ content.interface }} $entity, {{ content.class }}StorageInterface $storage): array {
    $result = $storage
      ->getQuery()
      ->accessCheck()
      ->allRevisions()
      ->condition(
        (string) $entity->getEntityType()->getKey('id'),
        $entity->id(),
      )
      ->sort(
        (string) $entity->getEntityType()->getKey('revision'),
        'DESC',
      )
      ->pager($this->getRevisionHistoryItemPerPage())
      ->execute();

    return array_keys($result);
  }

  /**
   * @return array{{ '<' }}string, mixed>
   */
  protected function getRevisionLabelElement({{ content.interface }} $revision): array {
    {# @todo URL is wrong. -#}
    return [
      '#type' => 'link',
      '#title' => $revision->label(),
      '#url' => $revision->toUrl(),
      '#attributes' => [
        'target' => '_blank',
      ],
    ];
  }

  /**
   * @return array{{ '<' }}string, mixed>
   */
  protected function getRevisionSubmissionElement({{ content.interface }} $revision, Link $link): array {
    $username = [
      '#theme' => 'username',
      '#account' => $revision->getRevisionUser(),
    ];

    $element = [
      '#type' => 'inline_template',
      '#template' => $this->getRevisionSubmissionInlineTemplate(),
      '#context' => [
        'date' => $link->toString(),
        'username' => $this->renderer->renderInIsolation($username),
        'message' => [
          '#markup' => $revision->getRevisionLogMessage(),
          '#allowed_tags' => Xss::getHtmlTagList(),
        ],
      ],
    ];

    // @todo Simplify once https://www.drupal.org/node/2334319 lands.
    $this->renderer->addCacheableDependency($element, $username);

    return $element;
  }

  protected function getRevisionSubmissionInlineTemplate(): string {
    return '{% trans %}{{ date }} by {{ username }}{% endtrans %}{% if message %}<p class="revision-log">{{ message }}</p>{% endif %}';
  }

  /**
   * @phpstan-param array{{ '<' }}string, bool> $access
   *
   * @return array{{ '<' }}string, mixed>
   */
  protected function getRevisionOperationElement(
    bool $isCurrentRevision,
    {{ content.interface }} $current,
    {{ content.interface }} $revision,
    array $access,
    bool $hasTranslations,
  ): array {
    if ($isCurrentRevision) {
      return [
        '#prefix' => '<em>',
        '#markup' => $this->t('Current revision'),
        '#suffix' => '</em>',
      ];
    }

    return [
      '#type' => 'operations',
      '#links' => $this->getRevisionOperationLinks($current, $revision, $access, $hasTranslations),
    ];
  }

  /**
   * @phpstan-param array{{ '<' }}string, bool> $access
   *
   * @return array{{ '<' }}string, array{{ '<' }}string, mixed>>
   */
  protected function getRevisionOperationLinks(
    {{ content.interface }} $current,
    {{ content.interface }} $revision,
    array $access,
    bool $hasTranslations,
  ): array {
    $entityTypeId = $current->getEntityTypeId();
    $langCode = $current->language()->getId();

    $links = [];

    if ($access['hasRevertPermission']) {
      $routeName = "entity.$entityTypeId.revision_revert" . ($hasTranslations ? '_translation' : '');
      $routeParams = [
        $entityTypeId => $current->id(),
        "{$entityTypeId}_revision" => $revision->getRevisionId(),
      ];
      if ($hasTranslations) {
        $routeParams['langcode'] = $langCode;
      }

      $links['revert'] = [
        'title' => $revision->getRevisionId() < $current->getRevisionId() ? $this->t('Revert') : $this->t('Set as current revision'),
        'url' => Url::fromRoute($routeName, $routeParams),
      ];
    }

    if ($access['hasDeletePermission']) {
      $links['delete'] = [
        'title' => $this->t('Delete'),
        'url' => Url::fromRoute(
          "entity.$entityTypeId.revision_delete",
          [
            $entityTypeId => $current->id(),
            "{$entityTypeId}_revision" => $revision->getRevisionId(),
          ]
        ),
      ];
    }

    return $links;
  }

}

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

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