diff -u b/src/Access/LatestRevisionCheck.php b/src/Access/LatestRevisionCheck.php --- b/src/Access/LatestRevisionCheck.php +++ b/src/Access/LatestRevisionCheck.php @@ -2,24 +2,83 @@ namespace Drupal\access_unpublished\Access; +use Drupal\Core\Access\AccessException; +use Drupal\Core\Entity\EntityInterface; +use Drupal\Core\Routing\Access\AccessInterface; use Drupal\Core\Routing\RouteMatchInterface; use Drupal\Core\Session\AccountInterface; use Symfony\Component\Routing\Route; -use Drupal\content_moderation\Access\LatestRevisionCheck as LatestRevisionCheckBase; /** - * Access check for the entity moderation tab. + * Access check for the entity moderation tab which supports access_unpublished. */ -class LatestRevisionCheck extends LatestRevisionCheckBase { +class LatestRevisionCheck implements AccessInterface { /** - * {@inheritdoc} + * The decorated access check. + * + * @var \Drupal\Core\Routing\Access\AccessInterface */ - public function access(Route $route, RouteMatchInterface $route_match, AccountInterface $account) { + protected $accessCheck; - $access_result = parent::access($route, $route_match, $account); + /** + * LatestRevisionCheck constructor. + * + * @param \Drupal\Core\Routing\Access\AccessInterface $access_check + * Latest revision access check to decorate. + */ + public function __construct(AccessInterface $access_check) { + $this->accessCheck = $access_check; + } + + /** + * Checks that there is a pending revision available. + * + * This checker assumes the presence of an '_entity_access' requirement key + * in the same form as used by EntityAccessCheck. + * + * @param \Symfony\Component\Routing\Route $route + * The route to check against. + * @param \Drupal\Core\Routing\RouteMatchInterface $route_match + * The parametrized route. + * @param \Drupal\Core\Session\AccountInterface $account + * The current user account. + * + * @return \Drupal\Core\Access\AccessResultInterface + * The access result. + * + * @see \Drupal\Core\Entity\EntityAccessCheck + */ + public function access(Route $route, RouteMatchInterface $route_match, AccountInterface $account) { + /* @var \Drupal\Core\Access\AccessResultInterface $access */ + $access = $this->accessCheck->access($route, $route_match, $account); $entity = $this->loadEntity($route, $route_match); - return $access_result->orIf(access_unpublished_entity_access($entity, 'view', $account)); + return $access->orIf(access_unpublished_entity_access($entity, 'view', $account)); + } + + /** + * Returns the default revision of the entity this route is for. + * + * @param \Symfony\Component\Routing\Route $route + * The route to check against. + * @param \Drupal\Core\Routing\RouteMatchInterface $route_match + * The parametrized route. + * + * @return \Drupal\Core\Entity\ContentEntityInterface + * returns the Entity in question. + * + * @throws \Drupal\Core\Access\AccessException + * An AccessException is thrown if the entity couldn't be loaded. + */ + protected function loadEntity(Route $route, RouteMatchInterface $route_match) { + $entity_type = $route->getOption('_content_moderation_entity_type'); + + if ($entity = $route_match->getParameter($entity_type)) { + if ($entity instanceof EntityInterface) { + return $entity; + } + } + throw new AccessException(sprintf('%s is not a valid entity route. The LatestRevisionCheck access checker may only be used with a route that has a single entity parameter.', $route_match->getRouteName())); } } diff -u b/src/AccessTokenManager.php b/src/AccessTokenManager.php --- b/src/AccessTokenManager.php +++ b/src/AccessTokenManager.php @@ -108,6 +108,22 @@ public function getAccessTokenUrl(EntityInterface $entity, AccessTokenInterface $token) { $tokenKey = $this->configFactory->get('access_unpublished.settings')->get('hash_key'); + $rel = 'canonical'; + + // Link to a forward revision if available. + if ($this->moderationInfo && $this->moderationInfo->hasPendingRevision($entity) && $entity->getEntityType()->hasLinkTemplate('latest-version')) { + $rel = 'latest-version'; + } + return $entity->toUrl($rel, [ + 'query' => [$tokenKey => $token->get('value')->value], + 'absolute' => TRUE, + ])->toString(); + } + +} + public function getAccessTokenUrl(EntityInterface $entity, AccessTokenInterface $token) { + $tokenKey = $this->configFactory->get('access_unpublished.settings')->get('hash_key'); + $url = Url::fromRoute('entity.' . $entity->getEntityType()->id() . '.canonical', [ $entity->getEntityType()->id() => $entity->id(), @@ -125,14 +140,0 @@ - public function getAccessTokenUrl(EntityInterface $entity, AccessTokenInterface $token) { - $tokenKey = $this->configFactory->get('access_unpublished.settings')->get('hash_key'); - - $rel = 'canonical'; - if ($this->moderationInfo && $this->moderationInfo->hasPendingRevision($entity) && $entity->getEntityType()->hasLinkTemplate('latest-version')) { - $rel = 'latest-version'; - } - return $entity->toUrl($rel, [ - 'query' => [$tokenKey => $token->get('value')->value], - 'absolute' => TRUE, - ])->toString(); - } - -} diff -u b/src/AccessUnpublishedServiceProvider.php b/src/AccessUnpublishedServiceProvider.php --- b/src/AccessUnpublishedServiceProvider.php +++ b/src/AccessUnpublishedServiceProvider.php @@ -1,16 +1,14 @@ hasDefinition('access_check.latest_revision')) { - $definition = $container->getDefinition('access_check.latest_revision'); - $definition->setClass('Drupal\access_unpublished\Access\LatestRevisionCheck'); + $container->register('access_unpublished.access_check.latest_revision', LatestRevisionCheck::class) + ->setDecoratedService('access_check.latest_revision') + ->addArgument(new Reference('access_unpublished.access_check.latest_revision.inner')) + ->addTag('access_check', ['applies_to' => '_content_moderation_latest_version']); } - } + } diff -u b/tests/src/Functional/ContentModerationAccessTest.php b/tests/src/Functional/ContentModerationAccessTest.php --- b/tests/src/Functional/ContentModerationAccessTest.php +++ b/tests/src/Functional/ContentModerationAccessTest.php @@ -60,19 +60,12 @@ public function testAccessWithValidToken() { $assert_session = $this->assertSession(); - // Create tokens for the entity. - $expiredToken = AccessToken::create([ - 'entity_type' => 'node', - 'entity_id' => $this->entity->id(), - 'value' => 'iAmExpired', - 'expire' => REQUEST_TIME - 100, - ]); - $expiredToken->save(); + // Create a token for the entity. $validToken = AccessToken::create([ 'entity_type' => 'node', 'entity_id' => $this->entity->id(), 'value' => 'iAmValid', - 'expire' => REQUEST_TIME + 100, + 'expire' => REQUEST_TIME + 10000, ]); $validToken->save(); only in patch2: unchanged: --- a/access_unpublished.module +++ b/access_unpublished.module @@ -25,8 +25,7 @@ function access_unpublished_entity_access(EntityInterface $entity, $operation, A $operation == 'view' && \Drupal::request()->query->has($tokenKey) && $entity instanceof EntityPublishedInterface && - $account->hasPermission($permission) && - !$entity->isPublished() + $account->hasPermission($permission) ) { $query = \Drupal::entityQuery('access_token'); @@ -55,7 +54,6 @@ function access_unpublished_entity_access(EntityInterface $entity, $operation, A * Implements hook_form_alter(). */ function access_unpublished_form_alter(&$form, FormStateInterface $form_state, $form_id) { - \Drupal::service('class_resolver') ->getInstanceFromDefinition(AccessUnpublishedForm::class) ->formAlter($form, $form_state, $form_id);