/** * {@inheritdoc} */ public function getCancelUrl() { if ($this->revision->getEntityType()->hasLinkTemplate('version-history')) { return $this->revision->toUrl('version-history'); } return $this->revision->toUrl(); }
/** * {@inheritdoc} */ public function getFormId() { $form_id = $this->entity->getEntityTypeId(); if ($this->entity->getEntityType()->hasKey('bundle')) { $form_id = $this->entity->bundle() . '_' . $form_id; } if ($this->operation != 'default') { $form_id = $form_id . '_' . $this->operation; } return $form_id . '_form'; }
/** * {@inheritdoc} */ public function isModeratedEntity(EntityInterface $entity) { if (!$entity instanceof ContentEntityInterface) { return FALSE; } return $this->shouldModerateEntitiesOfBundle($entity->getEntityType(), $entity->bundle()); }
/** * {@inheritdoc} */ public function buildForm(array $form, FormStateInterface $form_state) { if ($this->entity && $this->entityTypeDefinition->hasHandlerClass('entity_clone')) { $form['information'] = [ '#markup' => $this->stringTranslationManager->translate('<p>Do you want clone the <em>@entity_type</em> entity named <em>@title</em>?</p>', [ '@entity_type' => $this->entity->getEntityType()->getLabel(), '@title' => $this->entity->label(), ]), ]; /** @var \Drupal\entity_clone\EntityClone\EntityCloneFormInterface $entity_clone_handler */ if ($this->entityTypeManager->hasHandler($this->entityTypeDefinition->id(), 'entity_clone_form')) { $entity_clone_form_handler = $this->entityTypeManager->getHandler($this->entityTypeDefinition->id(), 'entity_clone_form'); $form = array_merge($form, $entity_clone_form_handler->formElement($this->entity)); } $form['clone'] = [ '#type' => 'submit', '#value' => 'Clone', ]; $form['abort'] = [ '#type' => 'submit', '#value' => 'Abort', '#submit' => '::cancelForm', ]; } return $form; }
/** * {@inheritdoc} */ public function getTranslationMetadata(EntityInterface $translation) { // We need a new instance of the metadata handler wrapping each translation. $entity_type = $translation->getEntityType(); $class = $entity_type->get('content_translation_metadata'); return new $class($translation, $this->getTranslationHandler($entity_type->id())); }
/** * {@inheritdoc} */ public function save(array $form, FormStateInterface $form_state) { // Save as a new revision if requested to do so. if (!$form_state->isValueEmpty('revision')) { $this->entity->setNewRevision(); } $insert = $this->entity->isNew(); $this->entity->save(); $context = ['@type' => $this->entity->bundle(), '%info' => $this->entity->label()]; $logger = $this->logger($this->entity->id()); $bundle_entity = $this->getBundleEntity(); $t_args = ['@type' => $bundle_entity ? $bundle_entity->label() : 'None', '%info' => $this->entity->label()]; if ($insert) { $logger->notice('@type: added %info.', $context); drupal_set_message($this->t('@type %info has been created.', $t_args)); } else { $logger->notice('@type: updated %info.', $context); drupal_set_message($this->t('@type %info has been updated.', $t_args)); } if ($this->entity->id()) { $form_state->setValue('id', $this->entity->id()); $form_state->set('id', $this->entity->id()); if ($this->entity->getEntityType()->hasLinkTemplate('collection')) { $form_state->setRedirectUrl($this->entity->toUrl('collection')); } else { $form_state->setRedirectUrl($this->entity->toUrl('canonical')); } } else { // In the unlikely case something went wrong on save, the entity will be // rebuilt and entity form redisplayed. drupal_set_message($this->t('The entity could not be saved.'), 'error'); $form_state->setRebuild(); } }
/** * Implements hook_entity_view_alter(). */ public function entityViewAlter(&$build, EntityInterface $entity, EntityViewDisplayInterface $display) { if ($entity->getEntityType()->isRevisionable() && !$this->moderationInfo->isLatestRevision($entity)) { // Hide quickedit, because its super confusing for the user to not edit the // live revision. unset($build['#attributes']['data-quickedit-entity-id']); } }
/** * {@inheritdoc} */ protected function alterBuild(array &$build, EntityInterface $entity, EntityViewDisplayInterface $display, $view_mode) { $entity_type_id = $entity->getEntityTypeId(); if ($entity instanceof ContentEntityInterface && $entity->isDefaultRevision() || !$entity->getEntityType()->isRevisionable()) { $build['#contextual_links'][$entity_type_id] = ['route_parameters' => [$entity_type_id => $entity->id()]]; } else { $build['#contextual_links'][$entity_type_id . '_revision'] = ['route_parameters' => [$entity_type_id => $entity->id(), $entity_type_id . '_revision' => $entity->getRevisionId()]]; } }
/** * {@inheritdoc} */ protected function checkAccess(EntityInterface $entity) { // Only check access if the current file access control handler explicitly // opts in by implementing FileAccessFormatterControlHandlerInterface. $access_handler_class = $entity->getEntityType()->getHandlerClass('access'); if (is_subclass_of($access_handler_class, '\\Drupal\\file\\FileAccessFormatterControlHandlerInterface')) { return $entity->access('view', NULL, TRUE); } else { return AccessResult::allowed(); } }
/** * {@inheritdoc} */ public function getTranslationAccess(EntityInterface $entity, $op) { // @todo Move this logic into a translation access controller checking also // the translation language and the given account. $entity_type = $entity->getEntityType(); $translate_permission = TRUE; // If no permission granularity is defined this entity type does not need an // explicit translate permission. $current_user = \Drupal::currentUser(); if (!$current_user->hasPermission('translate any entity') && ($permission_granularity = $entity_type->getPermissionGranularity())) { $translate_permission = $current_user->hasPermission($permission_granularity == 'bundle' ? "translate {$entity->bundle()} {$entity->getEntityTypeId()}" : "translate {$entity->getEntityTypeId()}"); } return $translate_permission && $current_user->hasPermission("{$op} content translations"); }
/** * Populate the fields on a given entity with sample values. * * @param $entity * The entity to be enriched with sample field values. */ public function populateFields(EntityInterface $entity) { $instances = entity_load_multiple_by_properties('field_instance_config', array('entity_type' => $entity->getEntityType()->id(), 'bundle' => $entity->bundle())); if ($skips = function_exists('drush_get_option') ? drush_get_option('skip-fields', '') : @$_REQUEST['skip-fields']) { foreach (explode(',', $skips) as $skip) { unset($instances[$skip]); } } foreach ($instances as $instance) { /** @var \Drupal\field\FieldStorageConfigInterface $field_storage */ $field_storage = $instance->getFieldStorageDefinition(); $max = $cardinality = $field_storage->getCardinality(); if ($cardinality == FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED) { // Just an arbitrary number for 'unlimited' $max = rand(1, 3); } $field_name = $field_storage->getName(); $entity->{$field_name}->generateSampleItems($max); } }
/** * Tests cache tags presence and invalidation of the entity when referenced. * * Tests the following cache tags: * - entity type view cache tag: "<entity type>_view" * - entity cache tag: "<entity type>:<entity ID>" * - entity type list cache tag: "<entity type>_list" * - referencing entity type view cache tag: "<referencing entity type>_view" * - referencing entity type cache tag: "<referencing entity type>:<referencing entity ID>" */ public function testReferencedEntity() { $entity_type = $this->entity->getEntityTypeId(); $referencing_entity_url = $this->referencing_entity->urlInfo('canonical'); $non_referencing_entity_url = $this->non_referencing_entity->urlInfo('canonical'); $listing_url = Url::fromRoute('entity.entity_test.collection_referencing_entities', ['entity_reference_field_name' => $entity_type . '_reference', 'referenced_entity_type' => $entity_type, 'referenced_entity_id' => $this->entity->id()]); $empty_entity_listing_url = Url::fromRoute('entity.entity_test.collection_empty', ['entity_type_id' => $entity_type]); $nonempty_entity_listing_url = Url::fromRoute('entity.entity_test.collection_labels_alphabetically', ['entity_type_id' => $entity_type]); // The default cache contexts for rendered entities. $entity_cache_contexts = ['theme', 'user.roles']; // Cache tags present on every rendered page. $page_cache_tags = Cache::mergeTags(['rendered'], \Drupal::moduleHandler()->moduleExists('block') ? ['config:block_list'] : []); $view_cache_tag = array(); if ($this->entity->getEntityType()->hasHandlerClass('view_builder')) { $view_cache_tag = \Drupal::entityManager()->getViewBuilder($entity_type)->getCacheTags(); } // Generate the cache tags for the (non) referencing entities. $referencing_entity_cache_tags = Cache::mergeTags($this->referencing_entity->getCacheTags(), \Drupal::entityManager()->getViewBuilder('entity_test')->getCacheTags(), $this->entity->getCacheTags(), $this->getAdditionalCacheTagsForEntity($this->entity), $view_cache_tag, ['rendered']); $non_referencing_entity_cache_tags = Cache::mergeTags($this->non_referencing_entity->getCacheTags(), \Drupal::entityManager()->getViewBuilder('entity_test')->getCacheTags(), ['rendered']); // Generate the cache tags for all two possible entity listing paths. // 1. list cache tag only (listing query has no match) // 2. list cache tag plus entity cache tag (listing query has a match) $empty_entity_listing_cache_tags = Cache::mergeTags($this->entity->getEntityType()->getListCacheTags(), $page_cache_tags); $nonempty_entity_listing_cache_tags = Cache::mergeTags($this->entity->getEntityType()->getListCacheTags(), $this->entity->getCacheTags(), $this->getAdditionalCacheTagsForEntityListing($this->entity), $page_cache_tags); $this->pass("Test referencing entity.", 'Debug'); $this->verifyPageCache($referencing_entity_url, 'MISS'); // Verify a cache hit, but also the presence of the correct cache tags. $this->verifyPageCache($referencing_entity_url, 'HIT', Cache::mergeTags($referencing_entity_cache_tags, $page_cache_tags)); // Also verify the existence of an entity render cache entry. $cache_keys = ['entity_view', 'entity_test', $this->referencing_entity->id(), 'full']; $cid = $this->createCacheId($cache_keys, $entity_cache_contexts); $redirected_cid = $this->createRedirectedCacheId($cache_keys, $entity_cache_contexts); $this->verifyRenderCache($cid, $referencing_entity_cache_tags, $redirected_cid); $this->pass("Test non-referencing entity.", 'Debug'); $this->verifyPageCache($non_referencing_entity_url, 'MISS'); // Verify a cache hit, but also the presence of the correct cache tags. $this->verifyPageCache($non_referencing_entity_url, 'HIT', Cache::mergeTags($non_referencing_entity_cache_tags, $page_cache_tags)); // Also verify the existence of an entity render cache entry. $cache_keys = ['entity_view', 'entity_test', $this->non_referencing_entity->id(), 'full']; $cid = $this->createCacheId($cache_keys, $entity_cache_contexts); $this->verifyRenderCache($cid, $non_referencing_entity_cache_tags); $this->pass("Test listing of referencing entities.", 'Debug'); // Prime the page cache for the listing of referencing entities. $this->verifyPageCache($listing_url, 'MISS'); // Verify a cache hit, but also the presence of the correct cache tags. $this->verifyPageCache($listing_url, 'HIT', Cache::mergeTags($referencing_entity_cache_tags, $page_cache_tags)); $this->pass("Test empty listing.", 'Debug'); // Prime the page cache for the empty listing. $this->verifyPageCache($empty_entity_listing_url, 'MISS'); // Verify a cache hit, but also the presence of the correct cache tags. $this->verifyPageCache($empty_entity_listing_url, 'HIT', $empty_entity_listing_cache_tags); // Verify the entity type's list cache contexts are present. $contexts_in_header = $this->drupalGetHeader('X-Drupal-Cache-Contexts'); $this->assertEqual($this->getAdditionalCacheContextsForEntityListing(), empty($contexts_in_header) ? [] : explode(' ', $contexts_in_header)); $this->pass("Test listing containing referenced entity.", 'Debug'); // Prime the page cache for the listing containing the referenced entity. $this->verifyPageCache($nonempty_entity_listing_url, 'MISS', $nonempty_entity_listing_cache_tags); // Verify a cache hit, but also the presence of the correct cache tags. $this->verifyPageCache($nonempty_entity_listing_url, 'HIT', $nonempty_entity_listing_cache_tags); // Verify the entity type's list cache contexts are present. $contexts_in_header = $this->drupalGetHeader('X-Drupal-Cache-Contexts'); $this->assertEqual($this->getAdditionalCacheContextsForEntityListing(), empty($contexts_in_header) ? [] : explode(' ', $contexts_in_header)); // Verify that after modifying the referenced entity, there is a cache miss // for every route except the one for the non-referencing entity. $this->pass("Test modification of referenced entity.", 'Debug'); $this->entity->save(); $this->verifyPageCache($referencing_entity_url, 'MISS'); $this->verifyPageCache($listing_url, 'MISS'); $this->verifyPageCache($empty_entity_listing_url, 'MISS'); $this->verifyPageCache($nonempty_entity_listing_url, 'MISS'); $this->verifyPageCache($non_referencing_entity_url, 'HIT'); // Verify cache hits. $this->verifyPageCache($referencing_entity_url, 'HIT'); $this->verifyPageCache($listing_url, 'HIT'); $this->verifyPageCache($empty_entity_listing_url, 'HIT'); $this->verifyPageCache($nonempty_entity_listing_url, 'HIT'); // Verify that after modifying the referencing entity, there is a cache miss // for every route except the ones for the non-referencing entity and the // empty entity listing. $this->pass("Test modification of referencing entity.", 'Debug'); $this->referencing_entity->save(); $this->verifyPageCache($referencing_entity_url, 'MISS'); $this->verifyPageCache($listing_url, 'MISS'); $this->verifyPageCache($nonempty_entity_listing_url, 'HIT'); $this->verifyPageCache($non_referencing_entity_url, 'HIT'); $this->verifyPageCache($empty_entity_listing_url, 'HIT'); // Verify cache hits. $this->verifyPageCache($referencing_entity_url, 'HIT'); $this->verifyPageCache($listing_url, 'HIT'); $this->verifyPageCache($nonempty_entity_listing_url, 'HIT'); // Verify that after modifying the non-referencing entity, there is a cache // miss only for the non-referencing entity route. $this->pass("Test modification of non-referencing entity.", 'Debug'); $this->non_referencing_entity->save(); $this->verifyPageCache($referencing_entity_url, 'HIT'); $this->verifyPageCache($listing_url, 'HIT'); $this->verifyPageCache($empty_entity_listing_url, 'HIT'); $this->verifyPageCache($nonempty_entity_listing_url, 'HIT'); $this->verifyPageCache($non_referencing_entity_url, 'MISS'); // Verify cache hits. $this->verifyPageCache($non_referencing_entity_url, 'HIT'); if ($this->entity->getEntityType()->hasHandlerClass('view_builder')) { // Verify that after modifying the entity's display, there is a cache miss // for both the referencing entity, and the listing of referencing // entities, but not for any other routes. $referenced_entity_view_mode = $this->selectViewMode($this->entity->getEntityTypeId()); $this->pass("Test modification of referenced entity's '{$referenced_entity_view_mode}' display.", 'Debug'); $entity_display = entity_get_display($entity_type, $this->entity->bundle(), $referenced_entity_view_mode); $entity_display->save(); $this->verifyPageCache($referencing_entity_url, 'MISS'); $this->verifyPageCache($listing_url, 'MISS'); $this->verifyPageCache($non_referencing_entity_url, 'HIT'); $this->verifyPageCache($empty_entity_listing_url, 'HIT'); $this->verifyPageCache($nonempty_entity_listing_url, 'HIT'); // Verify cache hits. $this->verifyPageCache($referencing_entity_url, 'HIT'); $this->verifyPageCache($listing_url, 'HIT'); } $bundle_entity_type = $this->entity->getEntityType()->getBundleEntityType(); if ($bundle_entity_type !== 'bundle') { // Verify that after modifying the corresponding bundle entity, there is a // cache miss for both the referencing entity, and the listing of // referencing entities, but not for any other routes. $this->pass("Test modification of referenced entity's bundle entity.", 'Debug'); $bundle_entity = entity_load($bundle_entity_type, $this->entity->bundle()); $bundle_entity->save(); $this->verifyPageCache($referencing_entity_url, 'MISS'); $this->verifyPageCache($listing_url, 'MISS'); $this->verifyPageCache($non_referencing_entity_url, 'HIT'); // Special case: entity types may choose to use their bundle entity type // cache tags, to avoid having excessively granular invalidation. $is_special_case = $bundle_entity->getCacheTags() == $this->entity->getCacheTags() && $bundle_entity->getEntityType()->getListCacheTags() == $this->entity->getEntityType()->getListCacheTags(); if ($is_special_case) { $this->verifyPageCache($empty_entity_listing_url, 'MISS'); $this->verifyPageCache($nonempty_entity_listing_url, 'MISS'); } else { $this->verifyPageCache($empty_entity_listing_url, 'HIT'); $this->verifyPageCache($nonempty_entity_listing_url, 'HIT'); } // Verify cache hits. $this->verifyPageCache($referencing_entity_url, 'HIT'); $this->verifyPageCache($listing_url, 'HIT'); if ($is_special_case) { $this->verifyPageCache($empty_entity_listing_url, 'HIT'); $this->verifyPageCache($nonempty_entity_listing_url, 'HIT'); } } if ($this->entity->getEntityType()->get('field_ui_base_route')) { // Verify that after modifying a configurable field on the entity, there // is a cache miss. $this->pass("Test modification of referenced entity's configurable field.", 'Debug'); $field_storage_name = $this->entity->getEntityTypeId() . '.configurable_field'; $field_storage = FieldStorageConfig::load($field_storage_name); $field_storage->save(); $this->verifyPageCache($referencing_entity_url, 'MISS'); $this->verifyPageCache($listing_url, 'MISS'); $this->verifyPageCache($empty_entity_listing_url, 'HIT'); $this->verifyPageCache($nonempty_entity_listing_url, 'HIT'); $this->verifyPageCache($non_referencing_entity_url, 'HIT'); // Verify cache hits. $this->verifyPageCache($referencing_entity_url, 'HIT'); $this->verifyPageCache($listing_url, 'HIT'); // Verify that after modifying a configurable field on the entity, there // is a cache miss. $this->pass("Test modification of referenced entity's configurable field.", 'Debug'); $field_name = $this->entity->getEntityTypeId() . '.' . $this->entity->bundle() . '.configurable_field'; $field = FieldConfig::load($field_name); $field->save(); $this->verifyPageCache($referencing_entity_url, 'MISS'); $this->verifyPageCache($listing_url, 'MISS'); $this->verifyPageCache($empty_entity_listing_url, 'HIT'); $this->verifyPageCache($nonempty_entity_listing_url, 'HIT'); $this->verifyPageCache($non_referencing_entity_url, 'HIT'); // Verify cache hits. $this->verifyPageCache($referencing_entity_url, 'HIT'); $this->verifyPageCache($listing_url, 'HIT'); } // Verify that after invalidating the entity's cache tag directly, there is // a cache miss for every route except the ones for the non-referencing // entity and the empty entity listing. $this->pass("Test invalidation of referenced entity's cache tag.", 'Debug'); Cache::invalidateTags($this->entity->getCacheTags()); $this->verifyPageCache($referencing_entity_url, 'MISS'); $this->verifyPageCache($listing_url, 'MISS'); $this->verifyPageCache($nonempty_entity_listing_url, 'MISS'); $this->verifyPageCache($non_referencing_entity_url, 'HIT'); $this->verifyPageCache($empty_entity_listing_url, 'HIT'); // Verify cache hits. $this->verifyPageCache($referencing_entity_url, 'HIT'); $this->verifyPageCache($listing_url, 'HIT'); $this->verifyPageCache($nonempty_entity_listing_url, 'HIT'); // Verify that after invalidating the entity's list cache tag directly, // there is a cache miss for both the empty entity listing and the non-empty // entity listing routes, but not for other routes. $this->pass("Test invalidation of referenced entity's list cache tag.", 'Debug'); Cache::invalidateTags($this->entity->getEntityType()->getListCacheTags()); $this->verifyPageCache($empty_entity_listing_url, 'MISS'); $this->verifyPageCache($nonempty_entity_listing_url, 'MISS'); $this->verifyPageCache($referencing_entity_url, 'HIT'); $this->verifyPageCache($non_referencing_entity_url, 'HIT'); $this->verifyPageCache($listing_url, 'HIT'); // Verify cache hits. $this->verifyPageCache($empty_entity_listing_url, 'HIT'); $this->verifyPageCache($nonempty_entity_listing_url, 'HIT'); if (!empty($view_cache_tag)) { // Verify that after invalidating the generic entity type's view cache tag // directly, there is a cache miss for both the referencing entity, and the // listing of referencing entities, but not for other routes. $this->pass("Test invalidation of referenced entity's 'view' cache tag.", 'Debug'); Cache::invalidateTags($view_cache_tag); $this->verifyPageCache($referencing_entity_url, 'MISS'); $this->verifyPageCache($listing_url, 'MISS'); $this->verifyPageCache($non_referencing_entity_url, 'HIT'); $this->verifyPageCache($empty_entity_listing_url, 'HIT'); $this->verifyPageCache($nonempty_entity_listing_url, 'HIT'); // Verify cache hits. $this->verifyPageCache($referencing_entity_url, 'HIT'); $this->verifyPageCache($listing_url, 'HIT'); } // Verify that after deleting the entity, there is a cache miss for every // route except for the non-referencing entity one. $this->pass('Test deletion of referenced entity.', 'Debug'); $this->entity->delete(); $this->verifyPageCache($referencing_entity_url, 'MISS'); $this->verifyPageCache($listing_url, 'MISS'); $this->verifyPageCache($empty_entity_listing_url, 'MISS'); $this->verifyPageCache($nonempty_entity_listing_url, 'MISS'); $this->verifyPageCache($non_referencing_entity_url, 'HIT'); // Verify cache hits. $referencing_entity_cache_tags = Cache::mergeTags($this->referencing_entity->getCacheTags(), \Drupal::entityManager()->getViewBuilder('entity_test')->getCacheTags(), ['rendered']); $this->verifyPageCache($referencing_entity_url, 'HIT', Cache::mergeTags($referencing_entity_cache_tags, $page_cache_tags)); $this->verifyPageCache($listing_url, 'HIT', $page_cache_tags); $this->verifyPageCache($empty_entity_listing_url, 'HIT', $empty_entity_listing_cache_tags); $this->verifyPageCache($nonempty_entity_listing_url, 'HIT', Cache::mergeTags($this->entity->getEntityType()->getListCacheTags(), $this->getAdditionalCacheTagsForEntityListing(), $page_cache_tags)); }
/** * Responds to entity PATCH requests. * * @param \Drupal\Core\Entity\EntityInterface $original_entity * The original entity object. * @param \Drupal\Core\Entity\EntityInterface $entity * The entity. * * @return \Drupal\rest\ResourceResponse * The HTTP response object. * * @throws \Symfony\Component\HttpKernel\Exception\HttpException */ public function patch(EntityInterface $original_entity, EntityInterface $entity = NULL) { if ($entity == NULL) { throw new BadRequestHttpException('No entity content received.'); } $definition = $this->getPluginDefinition(); if ($entity->getEntityTypeId() != $definition['entity_type']) { throw new BadRequestHttpException('Invalid entity type'); } if (!$original_entity->access('update')) { throw new AccessDeniedHttpException(); } // Overwrite the received properties. $langcode_key = $entity->getEntityType()->getKey('langcode'); foreach ($entity->_restSubmittedFields as $field_name) { $field = $entity->get($field_name); // It is not possible to set the language to NULL as it is automatically // re-initialized. As it must not be empty, skip it if it is. if ($field_name == $langcode_key && $field->isEmpty()) { continue; } if (!$original_entity->get($field_name)->access('edit')) { throw new AccessDeniedHttpException(SafeMarkup::format('Access denied on updating field @field.', array('@field' => $field_name))); } $original_entity->set($field_name, $field->getValue()); } // Validate the received data before saving. $this->validate($original_entity); try { $original_entity->save(); $this->logger->notice('Updated entity %type with ID %id.', array('%type' => $entity->getEntityTypeId(), '%id' => $entity->id())); // Update responses have an empty body. return new ResourceResponse(NULL, 204); } catch (EntityStorageException $e) { throw new HttpException(500, 'Internal Server Error', $e); } }
/** * Tests cache tags presence and invalidation of the entity when referenced. * * Tests the following cache tags: * - "<entity type>_view:1" * - "<entity type>:<entity ID>" * - "<referencing entity type>_view:1" * * - "<referencing entity type>:<referencing entity ID>" */ public function testReferencedEntity() { $entity_type = $this->entity->getEntityTypeId(); $referencing_entity_path = $this->referencing_entity->getSystemPath(); $non_referencing_entity_path = $this->non_referencing_entity->getSystemPath(); $listing_path = 'entity_test/list/' . $entity_type . '_reference/' . $entity_type . '/' . $this->entity->id(); $render_cache_tags = array('rendered:1'); $theme_cache_tags = array('theme:stark', 'theme_global_settings:1'); $view_cache_tag = array(); if ($this->entity->getEntityType()->hasControllerClass('view_builder')) { $view_cache_tag = \Drupal::entityManager()->getViewBuilder($entity_type)->getCacheTag(); } // Generate the cache tags for the (non) referencing entities. $referencing_entity_cache_tags = NestedArray::mergeDeep($this->referencing_entity->getCacheTag(), \Drupal::entityManager()->getViewBuilder('entity_test')->getCacheTag(), $this->entity->getCacheTag(), $view_cache_tag); $referencing_entity_cache_tags = explode(' ', HtmlViewSubscriber::convertCacheTagsToHeader($referencing_entity_cache_tags)); $referencing_entity_cache_tags = array_merge($referencing_entity_cache_tags, $this->getAdditionalCacheTagsForEntity($this->entity)); $non_referencing_entity_cache_tags = NestedArray::mergeDeep($this->non_referencing_entity->getCacheTag(), \Drupal::entityManager()->getViewBuilder('entity_test')->getCacheTag()); $non_referencing_entity_cache_tags = explode(' ', HtmlViewSubscriber::convertCacheTagsToHeader($non_referencing_entity_cache_tags)); $this->pass("Test referencing entity.", 'Debug'); $this->verifyPageCache($referencing_entity_path, 'MISS'); // Verify a cache hit, but also the presence of the correct cache tags. $tags = array_merge($render_cache_tags, $theme_cache_tags, $referencing_entity_cache_tags); $this->verifyPageCache($referencing_entity_path, 'HIT', $tags); // Also verify the existence of an entity render cache entry. $cid = 'entity_view:entity_test:' . $this->referencing_entity->id() . ':full:stark:r.anonymous:' . date_default_timezone_get(); $tags = array_merge($render_cache_tags, $referencing_entity_cache_tags); $this->verifyRenderCache($cid, $tags); $this->pass("Test non-referencing entity.", 'Debug'); $this->verifyPageCache($non_referencing_entity_path, 'MISS'); // Verify a cache hit, but also the presence of the correct cache tags. $tags = array_merge($render_cache_tags, $theme_cache_tags, $non_referencing_entity_cache_tags); $this->verifyPageCache($non_referencing_entity_path, 'HIT', $tags); // Also verify the existence of an entity render cache entry. $cid = 'entity_view:entity_test:' . $this->non_referencing_entity->id() . ':full:stark:r.anonymous:' . date_default_timezone_get(); $tags = array_merge($render_cache_tags, $non_referencing_entity_cache_tags); $this->verifyRenderCache($cid, $tags); $this->pass("Test listing of referencing entities.", 'Debug'); // Prime the page cache for the listing of referencing entities. $this->verifyPageCache($listing_path, 'MISS'); // Verify a cache hit, but also the presence of the correct cache tags. $tags = array_merge($render_cache_tags, $theme_cache_tags, $referencing_entity_cache_tags); $this->verifyPageCache($listing_path, 'HIT', $tags); // Verify that after modifying the referenced entity, there is a cache miss // for both the referencing entity, and the listing of referencing entities, // but not for the non-referencing entity. $this->pass("Test modification of referenced entity.", 'Debug'); $this->entity->save(); $this->verifyPageCache($referencing_entity_path, 'MISS'); $this->verifyPageCache($listing_path, 'MISS'); $this->verifyPageCache($non_referencing_entity_path, 'HIT'); // Verify cache hits. $this->verifyPageCache($referencing_entity_path, 'HIT'); $this->verifyPageCache($listing_path, 'HIT'); // Verify that after modifying the referencing entity, there is a cache miss // for both the referencing entity, and the listing of referencing entities, // but not for the non-referencing entity. $this->pass("Test modification of referencing entity.", 'Debug'); $this->referencing_entity->save(); $this->verifyPageCache($referencing_entity_path, 'MISS'); $this->verifyPageCache($listing_path, 'MISS'); $this->verifyPageCache($non_referencing_entity_path, 'HIT'); // Verify cache hits. $this->verifyPageCache($referencing_entity_path, 'HIT'); $this->verifyPageCache($listing_path, 'HIT'); // Verify that after modifying the non-referencing entity, there is a cache // miss for only the non-referencing entity, not for the referencing entity, // nor for the listing of referencing entities. $this->pass("Test modification of non-referencing entity.", 'Debug'); $this->non_referencing_entity->save(); $this->verifyPageCache($referencing_entity_path, 'HIT'); $this->verifyPageCache($listing_path, 'HIT'); $this->verifyPageCache($non_referencing_entity_path, 'MISS'); // Verify cache hits. $this->verifyPageCache($non_referencing_entity_path, 'HIT'); if ($this->entity->getEntityType()->hasControllerClass('view_builder')) { // Verify that after modifying the entity's display, there is a cache miss // for both the referencing entity, and the listing of referencing // entities, but not for the non-referencing entity. $referenced_entity_view_mode = $this->selectViewMode($this->entity->getEntityTypeId()); $this->pass("Test modification of referenced entity's '{$referenced_entity_view_mode}' display.", 'Debug'); $entity_display = entity_get_display($entity_type, $this->entity->bundle(), $referenced_entity_view_mode); $entity_display->save(); $this->verifyPageCache($referencing_entity_path, 'MISS'); $this->verifyPageCache($listing_path, 'MISS'); $this->verifyPageCache($non_referencing_entity_path, 'HIT'); // Verify cache hits. $this->verifyPageCache($referencing_entity_path, 'HIT'); $this->verifyPageCache($listing_path, 'HIT'); } $bundle_entity_type = $this->entity->getEntityType()->getBundleEntityType(); if ($bundle_entity_type !== 'bundle') { // Verify that after modifying the corresponding bundle entity, there is a // cache miss for both the referencing entity, and the listing of // referencing entities, but not for the non-referencing entity. $this->pass("Test modification of referenced entity's bundle entity.", 'Debug'); $bundle_entity = entity_load($bundle_entity_type, $this->entity->bundle()); $bundle_entity->save(); $this->verifyPageCache($referencing_entity_path, 'MISS'); $this->verifyPageCache($listing_path, 'MISS'); $this->verifyPageCache($non_referencing_entity_path, 'HIT'); // Verify cache hits. $this->verifyPageCache($referencing_entity_path, 'HIT'); $this->verifyPageCache($listing_path, 'HIT'); } if ($this->entity->getEntityType()->isFieldable()) { // Verify that after modifying a configurable field on the entity, there // is a cache miss. $this->pass("Test modification of referenced entity's configurable field.", 'Debug'); $field_name = $this->entity->getEntityTypeId() . '.configurable_field'; $field_storage = entity_load('field_storage_config', $field_name); $field_storage->save(); $this->verifyPageCache($referencing_entity_path, 'MISS'); $this->verifyPageCache($listing_path, 'MISS'); $this->verifyPageCache($non_referencing_entity_path, 'HIT'); // Verify cache hits. $this->verifyPageCache($referencing_entity_path, 'HIT'); $this->verifyPageCache($listing_path, 'HIT'); // Verify that after modifying a configurable field instance on the // entity, there is a cache miss. $this->pass("Test modification of referenced entity's configurable field instance.", 'Debug'); $field_instance_name = $this->entity->getEntityTypeId() . '.' . $this->entity->bundle() . '.configurable_field'; $field_instance = entity_load('field_instance_config', $field_instance_name); $field_instance->save(); $this->verifyPageCache($referencing_entity_path, 'MISS'); $this->verifyPageCache($listing_path, 'MISS'); $this->verifyPageCache($non_referencing_entity_path, 'HIT'); // Verify cache hits. $this->verifyPageCache($referencing_entity_path, 'HIT'); $this->verifyPageCache($listing_path, 'HIT'); } // Verify that after invalidating the entity's cache tag directly, there is // a cache miss for both the referencing entity, and the listing of // referencing entities, but not for the non-referencing entity. $this->pass("Test invalidation of referenced entity's cache tag.", 'Debug'); Cache::invalidateTags($this->entity->getCacheTag()); $this->verifyPageCache($referencing_entity_path, 'MISS'); $this->verifyPageCache($listing_path, 'MISS'); $this->verifyPageCache($non_referencing_entity_path, 'HIT'); // Verify cache hits. $this->verifyPageCache($referencing_entity_path, 'HIT'); $this->verifyPageCache($listing_path, 'HIT'); if (!empty($view_cache_tag)) { // Verify that after invalidating the generic entity type's view cache tag // directly, there is a cache miss for both the referencing entity, and the // listing of referencing entities, but not for the non-referencing entity. $this->pass("Test invalidation of referenced entity's 'view' cache tag.", 'Debug'); Cache::invalidateTags($view_cache_tag); $this->verifyPageCache($referencing_entity_path, 'MISS'); $this->verifyPageCache($listing_path, 'MISS'); $this->verifyPageCache($non_referencing_entity_path, 'HIT'); // Verify cache hits. $this->verifyPageCache($referencing_entity_path, 'HIT'); $this->verifyPageCache($listing_path, 'HIT'); } // Verify that after deleting the entity, there is a cache miss for both the // referencing entity, and the listing of referencing entities, but not for // the non-referencing entity. $this->pass('Test deletion of referenced entity.', 'Debug'); $this->entity->delete(); $this->verifyPageCache($referencing_entity_path, 'MISS'); $this->verifyPageCache($listing_path, 'MISS'); $this->verifyPageCache($non_referencing_entity_path, 'HIT'); // Verify cache hits. $referencing_entity_cache_tags = NestedArray::mergeDeep($this->referencing_entity->getCacheTag(), \Drupal::entityManager()->getViewBuilder('entity_test')->getCacheTag()); $referencing_entity_cache_tags = explode(' ', HtmlViewSubscriber::convertCacheTagsToHeader($referencing_entity_cache_tags)); $tags = array_merge($render_cache_tags, $theme_cache_tags, $referencing_entity_cache_tags); $this->verifyPageCache($referencing_entity_path, 'HIT', $tags); $tags = array_merge($render_cache_tags, $theme_cache_tags); $this->verifyPageCache($listing_path, 'HIT', $tags); }
/** * Returns a list of mapped properties for the passed content entity. * * @param \Drupal\Core\Entity\EntityInterface $entity * A content entity. * * @return array * An array of mappings between predicates and field properties. All * fields, and properties of the entity and the fields, that are available * will be returned. */ public function getEntityTypeMappedProperties(EntityInterface $entity) { $bundle = $entity->bundle(); $properties = []; // Collect impacted fields. $definitions = $this->entityManager->getFieldDefinitions($entity->getEntityTypeId(), $bundle); $base_field_definitions = $this->entityManager->getBaseFieldDefinitions($entity->getEntityTypeId()); $rdf_bundle_entity = $this->entityManager->getStorage($entity->getEntityType()->getBundleEntityType())->load($bundle); /** @var \Drupal\Core\Field\BaseFieldDefinition $field_definition */ foreach ($definitions as $field_name => $field_definition) { /** @var \Drupal\field\Entity\FieldStorageConfig $storage_definition */ $storage_definition = $field_definition->getFieldStorageDefinition(); if (!$storage_definition instanceof FieldStorageConfig) { continue; } foreach ($storage_definition->getColumns() as $column => $column_info) { if ($property = $storage_definition->getThirdPartySetting('rdf_entity', 'mapping_' . $column, FALSE)) { $properties['by_field'][$field_name][$column] = $property; $properties['flat'][$property] = $property; } } } foreach ($base_field_definitions as $field_name => $base_field_definition) { $field_data = $rdf_bundle_entity->getThirdPartySetting('rdf_entity', 'mapping_' . $field_name, FALSE); if (!$field_data) { continue; } foreach ($field_data as $column => $predicate) { if (empty($predicate)) { continue; } $properties['by_field'][$field_name][$column] = $predicate; $properties['flat'][$predicate] = $predicate; } } return $properties; }
/** * Adds an operation on bundles that should have a Moderation form. * * @param \Drupal\Core\Entity\EntityInterface $entity * The entity on which to define an operation. * * @return array * An array of operation definitions. * * @see hook_entity_operation() */ public function entityOperation(EntityInterface $entity) { $operations = []; $type = $entity->getEntityType(); $bundle_of = $type->getBundleOf(); if ($this->currentUser->hasPermission('administer moderation states') && $bundle_of && $this->moderationInfo->canModerateEntitiesOfEntityType($this->entityTypeManager->getDefinition($bundle_of))) { $operations['manage-moderation'] = ['title' => t('Manage moderation'), 'weight' => 27, 'url' => Url::fromRoute("entity.{$type->id()}.moderation", [$entity->getEntityTypeId() => $entity->id()])]; } return $operations; }
/** * Builds the group string used in the match array. * * @param \Drupal\Core\Entity\EntityInterface $entity * The matched entity. * * @return string * The match group for this entity. */ protected function buildGroup($entity) { $group = $entity->getEntityType()->getLabel(); // If the entities by this entity should be grouped by bundle, get the // name and append it to the group. if ($this->configuration['group_by_bundle']) { $bundles = $this->entityManager->getBundleInfo($entity->getEntityTypeId()); $bundle_label = $bundles[$entity->bundle()]['label']; $group .= ' - ' . $bundle_label; } return $group; }
/** * Responds to entity PATCH requests. * * @param \Drupal\Core\Entity\EntityInterface $original_entity * The original entity object. * @param \Drupal\Core\Entity\EntityInterface $entity * The entity. * * @return \Drupal\rest\ModifiedResourceResponse * The HTTP response object. * * @throws \Symfony\Component\HttpKernel\Exception\HttpException */ public function patch(EntityInterface $original_entity, EntityInterface $entity = NULL) { if ($entity == NULL) { throw new BadRequestHttpException('No entity content received.'); } $definition = $this->getPluginDefinition(); if ($entity->getEntityTypeId() != $definition['entity_type']) { throw new BadRequestHttpException('Invalid entity type'); } if (!$original_entity->access('update')) { throw new AccessDeniedHttpException(); } // Overwrite the received properties. $entity_keys = $entity->getEntityType()->getKeys(); foreach ($entity->_restSubmittedFields as $field_name) { $field = $entity->get($field_name); // Entity key fields need special treatment: together they uniquely // identify the entity. Therefore it does not make sense to modify any of // them. However, rather than throwing an error, we just ignore them as // long as their specified values match their current values. if (in_array($field_name, $entity_keys, TRUE)) { // Unchanged values for entity keys don't need access checking. if ($original_entity->get($field_name)->getValue() === $entity->get($field_name)->getValue()) { continue; } elseif (isset($entity_keys['langcode']) && $field_name === $entity_keys['langcode'] && $field->isEmpty()) { continue; } } if (!$original_entity->get($field_name)->access('edit')) { throw new AccessDeniedHttpException("Access denied on updating field '{$field_name}'."); } $original_entity->set($field_name, $field->getValue()); } // Validate the received data before saving. $this->validate($original_entity); try { $original_entity->save(); $this->logger->notice('Updated entity %type with ID %id.', array('%type' => $original_entity->getEntityTypeId(), '%id' => $original_entity->id())); // Return the updated entity in the response body. return new ModifiedResourceResponse($original_entity, 200); } catch (EntityStorageException $e) { throw new HttpException(500, 'Internal Server Error', $e); } }
/** * Updates the changed time of the entity. * * Applies only if the entity implements the EntityChangedInterface. * * @param \Drupal\Core\Entity\EntityInterface $entity * The entity updated with the submitted values. */ public function updateChangedTime(EntityInterface $entity) { if ($entity->getEntityType()->isSubclassOf(EntityChangedInterface::class)) { $entity->setChangedTime(REQUEST_TIME); } }
/** * Tests cache tags presence and invalidation of the entity when referenced. * * Tests the following cache tags: * - entity type view cache tag: "<entity type>_view" * - entity cache tag: "<entity type>:<entity ID>" * - entity type list cache tag: "<entity type>_list" * - referencing entity type view cache tag: "<referencing entity type>_view" * - referencing entity type cache tag: "<referencing entity type>:<referencing entity ID>" */ public function testReferencedEntity() { $entity_type = $this->entity->getEntityTypeId(); $referencing_entity_url = $this->referencingEntity->urlInfo('canonical'); $non_referencing_entity_url = $this->nonReferencingEntity->urlInfo('canonical'); $listing_url = Url::fromRoute('entity.entity_test.collection_referencing_entities', ['entity_reference_field_name' => $entity_type . '_reference', 'referenced_entity_type' => $entity_type, 'referenced_entity_id' => $this->entity->id()]); $empty_entity_listing_url = Url::fromRoute('entity.entity_test.collection_empty', ['entity_type_id' => $entity_type]); $nonempty_entity_listing_url = Url::fromRoute('entity.entity_test.collection_labels_alphabetically', ['entity_type_id' => $entity_type]); // The default cache contexts for rendered entities. $default_cache_contexts = ['languages:' . LanguageInterface::TYPE_INTERFACE, 'theme', 'user.permissions']; $entity_cache_contexts = $default_cache_contexts; $page_cache_contexts = Cache::mergeContexts($default_cache_contexts, ['url.query_args:' . MainContentViewSubscriber::WRAPPER_FORMAT]); // Cache tags present on every rendered page. // 'user.permissions' is a required cache context, and responses that vary // by this cache context when requested by anonymous users automatically // also get this cache tag, to ensure correct invalidation. $page_cache_tags = Cache::mergeTags(['rendered'], ['config:user.role.anonymous']); // If the block module is used, the Block page display variant is used, // which adds the block config entity type's list cache tags. $page_cache_tags = Cache::mergeTags($page_cache_tags, \Drupal::moduleHandler()->moduleExists('block') ? ['config:block_list'] : []); $page_cache_tags_referencing_entity = in_array('user.permissions', $this->getAccessCacheContextsForEntity($this->referencingEntity)) ? ['config:user.role.anonymous'] : []; $view_cache_tag = array(); if ($this->entity->getEntityType()->hasHandlerClass('view_builder')) { $view_cache_tag = \Drupal::entityManager()->getViewBuilder($entity_type)->getCacheTags(); } $context_metadata = \Drupal::service('cache_contexts_manager')->convertTokensToKeys($entity_cache_contexts); $cache_context_tags = $context_metadata->getCacheTags(); // Generate the cache tags for the (non) referencing entities. $referencing_entity_cache_tags = Cache::mergeTags($this->referencingEntity->getCacheTags(), \Drupal::entityManager()->getViewBuilder('entity_test')->getCacheTags()); // Includes the main entity's cache tags, since this entity references it. $referencing_entity_cache_tags = Cache::mergeTags($referencing_entity_cache_tags, $this->entity->getCacheTags()); $referencing_entity_cache_tags = Cache::mergeTags($referencing_entity_cache_tags, $this->getAdditionalCacheTagsForEntity($this->entity)); $referencing_entity_cache_tags = Cache::mergeTags($referencing_entity_cache_tags, $view_cache_tag); $referencing_entity_cache_tags = Cache::mergeTags($referencing_entity_cache_tags, $cache_context_tags); $referencing_entity_cache_tags = Cache::mergeTags($referencing_entity_cache_tags, ['rendered']); $non_referencing_entity_cache_tags = Cache::mergeTags($this->nonReferencingEntity->getCacheTags(), \Drupal::entityManager()->getViewBuilder('entity_test')->getCacheTags()); $non_referencing_entity_cache_tags = Cache::mergeTags($non_referencing_entity_cache_tags, ['rendered']); // Generate the cache tags for all two possible entity listing paths. // 1. list cache tag only (listing query has no match) // 2. list cache tag plus entity cache tag (listing query has a match) $empty_entity_listing_cache_tags = Cache::mergeTags($this->entity->getEntityType()->getListCacheTags(), $page_cache_tags); $nonempty_entity_listing_cache_tags = Cache::mergeTags($this->entity->getEntityType()->getListCacheTags(), $this->entity->getCacheTags()); $nonempty_entity_listing_cache_tags = Cache::mergeTags($nonempty_entity_listing_cache_tags, $this->getAdditionalCacheTagsForEntityListing($this->entity)); $nonempty_entity_listing_cache_tags = Cache::mergeTags($nonempty_entity_listing_cache_tags, $page_cache_tags); $this->pass("Test referencing entity.", 'Debug'); $this->verifyPageCache($referencing_entity_url, 'MISS'); // Verify a cache hit, but also the presence of the correct cache tags. $expected_tags = Cache::mergeTags($referencing_entity_cache_tags, $page_cache_tags); $expected_tags = Cache::mergeTags($expected_tags, $page_cache_tags_referencing_entity); $this->verifyPageCache($referencing_entity_url, 'HIT', $expected_tags); // Also verify the existence of an entity render cache entry. $cache_keys = ['entity_view', 'entity_test', $this->referencingEntity->id(), 'full']; $cid = $this->createCacheId($cache_keys, $entity_cache_contexts); $access_cache_contexts = $this->getAccessCacheContextsForEntity($this->entity); $additional_cache_contexts = $this->getAdditionalCacheContextsForEntity($this->referencingEntity); $redirected_cid = NULL; if (count($access_cache_contexts) || count($additional_cache_contexts)) { $cache_contexts = Cache::mergeContexts($entity_cache_contexts, $additional_cache_contexts); $cache_contexts = Cache::mergeContexts($cache_contexts, $access_cache_contexts); $redirected_cid = $this->createCacheId($cache_keys, $cache_contexts); $context_metadata = \Drupal::service('cache_contexts_manager')->convertTokensToKeys($cache_contexts); $referencing_entity_cache_tags = Cache::mergeTags($referencing_entity_cache_tags, $context_metadata->getCacheTags()); } $this->verifyRenderCache($cid, $referencing_entity_cache_tags, $redirected_cid); $this->pass("Test non-referencing entity.", 'Debug'); $this->verifyPageCache($non_referencing_entity_url, 'MISS'); // Verify a cache hit, but also the presence of the correct cache tags. $this->verifyPageCache($non_referencing_entity_url, 'HIT', Cache::mergeTags($non_referencing_entity_cache_tags, $page_cache_tags)); // Also verify the existence of an entity render cache entry. $cache_keys = ['entity_view', 'entity_test', $this->nonReferencingEntity->id(), 'full']; $cid = $this->createCacheId($cache_keys, $entity_cache_contexts); $this->verifyRenderCache($cid, $non_referencing_entity_cache_tags); $this->pass("Test listing of referencing entities.", 'Debug'); // Prime the page cache for the listing of referencing entities. $this->verifyPageCache($listing_url, 'MISS'); // Verify a cache hit, but also the presence of the correct cache tags. $expected_tags = Cache::mergeTags($referencing_entity_cache_tags, $page_cache_tags); $expected_tags = Cache::mergeTags($expected_tags, $page_cache_tags_referencing_entity); $this->verifyPageCache($listing_url, 'HIT', $expected_tags); $this->pass("Test empty listing.", 'Debug'); // Prime the page cache for the empty listing. $this->verifyPageCache($empty_entity_listing_url, 'MISS'); // Verify a cache hit, but also the presence of the correct cache tags. $this->verifyPageCache($empty_entity_listing_url, 'HIT', $empty_entity_listing_cache_tags); // Verify the entity type's list cache contexts are present. $contexts_in_header = $this->drupalGetHeader('X-Drupal-Cache-Contexts'); $this->assertEqual(Cache::mergeContexts($page_cache_contexts, $this->getAdditionalCacheContextsForEntityListing()), empty($contexts_in_header) ? [] : explode(' ', $contexts_in_header)); $this->pass("Test listing containing referenced entity.", 'Debug'); // Prime the page cache for the listing containing the referenced entity. $this->verifyPageCache($nonempty_entity_listing_url, 'MISS', $nonempty_entity_listing_cache_tags); // Verify a cache hit, but also the presence of the correct cache tags. $this->verifyPageCache($nonempty_entity_listing_url, 'HIT', $nonempty_entity_listing_cache_tags); // Verify the entity type's list cache contexts are present. $contexts_in_header = $this->drupalGetHeader('X-Drupal-Cache-Contexts'); $this->assertEqual(Cache::mergeContexts($page_cache_contexts, $this->getAdditionalCacheContextsForEntityListing()), empty($contexts_in_header) ? [] : explode(' ', $contexts_in_header)); // Verify that after modifying the referenced entity, there is a cache miss // for every route except the one for the non-referencing entity. $this->pass("Test modification of referenced entity.", 'Debug'); $this->entity->save(); $this->verifyPageCache($referencing_entity_url, 'MISS'); $this->verifyPageCache($listing_url, 'MISS'); $this->verifyPageCache($empty_entity_listing_url, 'MISS'); $this->verifyPageCache($nonempty_entity_listing_url, 'MISS'); $this->verifyPageCache($non_referencing_entity_url, 'HIT'); // Verify cache hits. $this->verifyPageCache($referencing_entity_url, 'HIT'); $this->verifyPageCache($listing_url, 'HIT'); $this->verifyPageCache($empty_entity_listing_url, 'HIT'); $this->verifyPageCache($nonempty_entity_listing_url, 'HIT'); // Verify that after modifying the referencing entity, there is a cache miss // for every route except the ones for the non-referencing entity and the // empty entity listing. $this->pass("Test modification of referencing entity.", 'Debug'); $this->referencingEntity->save(); $this->verifyPageCache($referencing_entity_url, 'MISS'); $this->verifyPageCache($listing_url, 'MISS'); $this->verifyPageCache($nonempty_entity_listing_url, 'HIT'); $this->verifyPageCache($non_referencing_entity_url, 'HIT'); $this->verifyPageCache($empty_entity_listing_url, 'HIT'); // Verify cache hits. $this->verifyPageCache($referencing_entity_url, 'HIT'); $this->verifyPageCache($listing_url, 'HIT'); $this->verifyPageCache($nonempty_entity_listing_url, 'HIT'); // Verify that after modifying the non-referencing entity, there is a cache // miss only for the non-referencing entity route. $this->pass("Test modification of non-referencing entity.", 'Debug'); $this->nonReferencingEntity->save(); $this->verifyPageCache($referencing_entity_url, 'HIT'); $this->verifyPageCache($listing_url, 'HIT'); $this->verifyPageCache($empty_entity_listing_url, 'HIT'); $this->verifyPageCache($nonempty_entity_listing_url, 'HIT'); $this->verifyPageCache($non_referencing_entity_url, 'MISS'); // Verify cache hits. $this->verifyPageCache($non_referencing_entity_url, 'HIT'); if ($this->entity->getEntityType()->hasHandlerClass('view_builder')) { // Verify that after modifying the entity's display, there is a cache miss // for both the referencing entity, and the listing of referencing // entities, but not for any other routes. $referenced_entity_view_mode = $this->selectViewMode($this->entity->getEntityTypeId()); $this->pass("Test modification of referenced entity's '{$referenced_entity_view_mode}' display.", 'Debug'); $entity_display = entity_get_display($entity_type, $this->entity->bundle(), $referenced_entity_view_mode); $entity_display->save(); $this->verifyPageCache($referencing_entity_url, 'MISS'); $this->verifyPageCache($listing_url, 'MISS'); $this->verifyPageCache($non_referencing_entity_url, 'HIT'); $this->verifyPageCache($empty_entity_listing_url, 'HIT'); $this->verifyPageCache($nonempty_entity_listing_url, 'HIT'); // Verify cache hits. $this->verifyPageCache($referencing_entity_url, 'HIT'); $this->verifyPageCache($listing_url, 'HIT'); } if ($bundle_entity_type_id = $this->entity->getEntityType()->getBundleEntityType()) { // Verify that after modifying the corresponding bundle entity, there is a // cache miss for both the referencing entity, and the listing of // referencing entities, but not for any other routes. $this->pass("Test modification of referenced entity's bundle entity.", 'Debug'); $bundle_entity = entity_load($bundle_entity_type_id, $this->entity->bundle()); $bundle_entity->save(); $this->verifyPageCache($referencing_entity_url, 'MISS'); $this->verifyPageCache($listing_url, 'MISS'); $this->verifyPageCache($non_referencing_entity_url, 'HIT'); // Special case: entity types may choose to use their bundle entity type // cache tags, to avoid having excessively granular invalidation. $is_special_case = $bundle_entity->getCacheTags() == $this->entity->getCacheTags() && $bundle_entity->getEntityType()->getListCacheTags() == $this->entity->getEntityType()->getListCacheTags(); if ($is_special_case) { $this->verifyPageCache($empty_entity_listing_url, 'MISS'); $this->verifyPageCache($nonempty_entity_listing_url, 'MISS'); } else { $this->verifyPageCache($empty_entity_listing_url, 'HIT'); $this->verifyPageCache($nonempty_entity_listing_url, 'HIT'); } // Verify cache hits. $this->verifyPageCache($referencing_entity_url, 'HIT'); $this->verifyPageCache($listing_url, 'HIT'); if ($is_special_case) { $this->verifyPageCache($empty_entity_listing_url, 'HIT'); $this->verifyPageCache($nonempty_entity_listing_url, 'HIT'); } } if ($this->entity->getEntityType()->get('field_ui_base_route')) { // Verify that after modifying a configurable field on the entity, there // is a cache miss. $this->pass("Test modification of referenced entity's configurable field.", 'Debug'); $field_storage_name = $this->entity->getEntityTypeId() . '.configurable_field'; $field_storage = FieldStorageConfig::load($field_storage_name); $field_storage->save(); $this->verifyPageCache($referencing_entity_url, 'MISS'); $this->verifyPageCache($listing_url, 'MISS'); $this->verifyPageCache($empty_entity_listing_url, 'HIT'); $this->verifyPageCache($nonempty_entity_listing_url, 'HIT'); $this->verifyPageCache($non_referencing_entity_url, 'HIT'); // Verify cache hits. $this->verifyPageCache($referencing_entity_url, 'HIT'); $this->verifyPageCache($listing_url, 'HIT'); // Verify that after modifying a configurable field on the entity, there // is a cache miss. $this->pass("Test modification of referenced entity's configurable field.", 'Debug'); $field_name = $this->entity->getEntityTypeId() . '.' . $this->entity->bundle() . '.configurable_field'; $field = FieldConfig::load($field_name); $field->save(); $this->verifyPageCache($referencing_entity_url, 'MISS'); $this->verifyPageCache($listing_url, 'MISS'); $this->verifyPageCache($empty_entity_listing_url, 'HIT'); $this->verifyPageCache($nonempty_entity_listing_url, 'HIT'); $this->verifyPageCache($non_referencing_entity_url, 'HIT'); // Verify cache hits. $this->verifyPageCache($referencing_entity_url, 'HIT'); $this->verifyPageCache($listing_url, 'HIT'); } // Verify that after invalidating the entity's cache tag directly, there is // a cache miss for every route except the ones for the non-referencing // entity and the empty entity listing. $this->pass("Test invalidation of referenced entity's cache tag.", 'Debug'); Cache::invalidateTags($this->entity->getCacheTagsToInvalidate()); $this->verifyPageCache($referencing_entity_url, 'MISS'); $this->verifyPageCache($listing_url, 'MISS'); $this->verifyPageCache($nonempty_entity_listing_url, 'MISS'); $this->verifyPageCache($non_referencing_entity_url, 'HIT'); $this->verifyPageCache($empty_entity_listing_url, 'HIT'); // Verify cache hits. $this->verifyPageCache($referencing_entity_url, 'HIT'); $this->verifyPageCache($listing_url, 'HIT'); $this->verifyPageCache($nonempty_entity_listing_url, 'HIT'); // Verify that after invalidating the entity's list cache tag directly, // there is a cache miss for both the empty entity listing and the non-empty // entity listing routes, but not for other routes. $this->pass("Test invalidation of referenced entity's list cache tag.", 'Debug'); Cache::invalidateTags($this->entity->getEntityType()->getListCacheTags()); $this->verifyPageCache($empty_entity_listing_url, 'MISS'); $this->verifyPageCache($nonempty_entity_listing_url, 'MISS'); $this->verifyPageCache($referencing_entity_url, 'HIT'); $this->verifyPageCache($non_referencing_entity_url, 'HIT'); $this->verifyPageCache($listing_url, 'HIT'); // Verify cache hits. $this->verifyPageCache($empty_entity_listing_url, 'HIT'); $this->verifyPageCache($nonempty_entity_listing_url, 'HIT'); if (!empty($view_cache_tag)) { // Verify that after invalidating the generic entity type's view cache tag // directly, there is a cache miss for both the referencing entity, and the // listing of referencing entities, but not for other routes. $this->pass("Test invalidation of referenced entity's 'view' cache tag.", 'Debug'); Cache::invalidateTags($view_cache_tag); $this->verifyPageCache($referencing_entity_url, 'MISS'); $this->verifyPageCache($listing_url, 'MISS'); $this->verifyPageCache($non_referencing_entity_url, 'HIT'); $this->verifyPageCache($empty_entity_listing_url, 'HIT'); $this->verifyPageCache($nonempty_entity_listing_url, 'HIT'); // Verify cache hits. $this->verifyPageCache($referencing_entity_url, 'HIT'); $this->verifyPageCache($listing_url, 'HIT'); } // Verify that after deleting the entity, there is a cache miss for every // route except for the non-referencing entity one. $this->pass('Test deletion of referenced entity.', 'Debug'); $this->entity->delete(); $this->verifyPageCache($referencing_entity_url, 'MISS'); $this->verifyPageCache($listing_url, 'MISS'); $this->verifyPageCache($empty_entity_listing_url, 'MISS'); $this->verifyPageCache($nonempty_entity_listing_url, 'MISS'); $this->verifyPageCache($non_referencing_entity_url, 'HIT'); // Verify cache hits. $referencing_entity_cache_tags = Cache::mergeTags($this->referencingEntity->getCacheTags(), \Drupal::entityManager()->getViewBuilder('entity_test')->getCacheTags()); $referencing_entity_cache_tags = Cache::mergeTags($referencing_entity_cache_tags, ['rendered']); $nonempty_entity_listing_cache_tags = Cache::mergeTags($this->entity->getEntityType()->getListCacheTags(), $this->getAdditionalCacheTagsForEntityListing()); $nonempty_entity_listing_cache_tags = Cache::mergeTags($nonempty_entity_listing_cache_tags, $page_cache_tags); $this->verifyPageCache($referencing_entity_url, 'HIT', Cache::mergeTags($referencing_entity_cache_tags, $page_cache_tags)); $this->verifyPageCache($listing_url, 'HIT', $page_cache_tags); $this->verifyPageCache($empty_entity_listing_url, 'HIT', $empty_entity_listing_cache_tags); $this->verifyPageCache($nonempty_entity_listing_url, 'HIT', $nonempty_entity_listing_cache_tags); }
/** * Tests cache tags presence and invalidation of the entity when referenced. * * Tests the following cache tags: * - entity type view cache tag: "<entity type>_view" * - entity cache tag: "<entity type>:<entity ID>" * - entity type list cache tag: "<entity type>_list" * - referencing entity type view cache tag: "<referencing entity type>_view" * - referencing entity type cache tag: "<referencing entity type>:<referencing entity ID>" */ public function testReferencedEntity() { $entity_type = $this->entity->getEntityTypeId(); $referencing_entity_path = $this->referencing_entity->getSystemPath(); $non_referencing_entity_path = $this->non_referencing_entity->getSystemPath(); $listing_path = 'entity_test/list/' . $entity_type . '_reference/' . $entity_type . '/' . $this->entity->id(); $empty_entity_listing_path = 'entity_test/list_empty/' . $entity_type; $nonempty_entity_listing_path = 'entity_test/list_labels_alphabetically/' . $entity_type; $render_cache_tags = array('rendered'); $theme_cache_tags = array('theme:stark', 'theme_global_settings'); $view_cache_tag = array(); if ($this->entity->getEntityType()->hasHandlerClass('view_builder')) { $view_cache_tag = \Drupal::entityManager()->getViewBuilder($entity_type)->getCacheTags(); } // Generate the cache tags for the (non) referencing entities. $referencing_entity_cache_tags = Cache::mergeTags($this->referencing_entity->getCacheTags(), \Drupal::entityManager()->getViewBuilder('entity_test')->getCacheTags(), $this->entity->getCacheTags(), $this->getAdditionalCacheTagsForEntity($this->entity), $view_cache_tag); $non_referencing_entity_cache_tags = Cache::mergeTags($this->non_referencing_entity->getCacheTags(), \Drupal::entityManager()->getViewBuilder('entity_test')->getCacheTags()); // Generate the cache tags for all two possible entity listing paths. // 1. list cache tag only (listing query has no match) // 2. list cache tag plus entity cache tag (listing query has a match) $empty_entity_listing_cache_tags = Cache::mergeTags($this->entity->getEntityType()->getListCacheTags(), $theme_cache_tags, $render_cache_tags); $nonempty_entity_listing_cache_tags = Cache::mergeTags($this->entity->getEntityType()->getListCacheTags(), $this->entity->getCacheTags(), $this->getAdditionalCacheTagsForEntityListing($this->entity), $theme_cache_tags, $render_cache_tags); $this->pass("Test referencing entity.", 'Debug'); $this->verifyPageCache($referencing_entity_path, 'MISS'); // Verify a cache hit, but also the presence of the correct cache tags. $tags = Cache::mergeTags($render_cache_tags, $theme_cache_tags, $referencing_entity_cache_tags); $this->verifyPageCache($referencing_entity_path, 'HIT', $tags); // Also verify the existence of an entity render cache entry. $cid = 'entity_view:entity_test:' . $this->referencing_entity->id() . ':full:stark:r.anonymous:' . date_default_timezone_get(); $tags = Cache::mergeTags($render_cache_tags, $referencing_entity_cache_tags); $this->verifyRenderCache($cid, $tags); $this->pass("Test non-referencing entity.", 'Debug'); $this->verifyPageCache($non_referencing_entity_path, 'MISS'); // Verify a cache hit, but also the presence of the correct cache tags. $tags = Cache::mergeTags($render_cache_tags, $theme_cache_tags, $non_referencing_entity_cache_tags); $this->verifyPageCache($non_referencing_entity_path, 'HIT', $tags); // Also verify the existence of an entity render cache entry. $cid = 'entity_view:entity_test:' . $this->non_referencing_entity->id() . ':full:stark:r.anonymous:' . date_default_timezone_get(); $tags = Cache::mergeTags($render_cache_tags, $non_referencing_entity_cache_tags); $this->verifyRenderCache($cid, $tags); $this->pass("Test listing of referencing entities.", 'Debug'); // Prime the page cache for the listing of referencing entities. $this->verifyPageCache($listing_path, 'MISS'); // Verify a cache hit, but also the presence of the correct cache tags. $tags = Cache::mergeTags($render_cache_tags, $theme_cache_tags, $referencing_entity_cache_tags); $this->verifyPageCache($listing_path, 'HIT', $tags); $this->pass("Test empty listing.", 'Debug'); // Prime the page cache for the empty listing. $this->verifyPageCache($empty_entity_listing_path, 'MISS'); // Verify a cache hit, but also the presence of the correct cache tags. $this->verifyPageCache($empty_entity_listing_path, 'HIT', $empty_entity_listing_cache_tags); $this->pass("Test listing containing referenced entity.", 'Debug'); // Prime the page cache for the listing containing the referenced entity. $this->verifyPageCache($nonempty_entity_listing_path, 'MISS'); // Verify a cache hit, but also the presence of the correct cache tags. $this->verifyPageCache($nonempty_entity_listing_path, 'HIT', $nonempty_entity_listing_cache_tags); // Verify that after modifying the referenced entity, there is a cache miss // for every route except the one for the non-referencing entity. $this->pass("Test modification of referenced entity.", 'Debug'); $this->entity->save(); $this->verifyPageCache($referencing_entity_path, 'MISS'); $this->verifyPageCache($listing_path, 'MISS'); $this->verifyPageCache($empty_entity_listing_path, 'MISS'); $this->verifyPageCache($nonempty_entity_listing_path, 'MISS'); $this->verifyPageCache($non_referencing_entity_path, 'HIT'); // Verify cache hits. $this->verifyPageCache($referencing_entity_path, 'HIT'); $this->verifyPageCache($listing_path, 'HIT'); $this->verifyPageCache($empty_entity_listing_path, 'HIT'); $this->verifyPageCache($nonempty_entity_listing_path, 'HIT'); // Verify that after modifying the referencing entity, there is a cache miss // for every route except the ones for the non-referencing entity and the // empty entity listing. $this->pass("Test modification of referencing entity.", 'Debug'); $this->referencing_entity->save(); $this->verifyPageCache($referencing_entity_path, 'MISS'); $this->verifyPageCache($listing_path, 'MISS'); $this->verifyPageCache($nonempty_entity_listing_path, 'HIT'); $this->verifyPageCache($non_referencing_entity_path, 'HIT'); $this->verifyPageCache($empty_entity_listing_path, 'HIT'); // Verify cache hits. $this->verifyPageCache($referencing_entity_path, 'HIT'); $this->verifyPageCache($listing_path, 'HIT'); $this->verifyPageCache($nonempty_entity_listing_path, 'HIT'); // Verify that after modifying the non-referencing entity, there is a cache // miss only for the non-referencing entity route. $this->pass("Test modification of non-referencing entity.", 'Debug'); $this->non_referencing_entity->save(); $this->verifyPageCache($referencing_entity_path, 'HIT'); $this->verifyPageCache($listing_path, 'HIT'); $this->verifyPageCache($empty_entity_listing_path, 'HIT'); $this->verifyPageCache($nonempty_entity_listing_path, 'HIT'); $this->verifyPageCache($non_referencing_entity_path, 'MISS'); // Verify cache hits. $this->verifyPageCache($non_referencing_entity_path, 'HIT'); if ($this->entity->getEntityType()->hasHandlerClass('view_builder')) { // Verify that after modifying the entity's display, there is a cache miss // for both the referencing entity, and the listing of referencing // entities, but not for any other routes. $referenced_entity_view_mode = $this->selectViewMode($this->entity->getEntityTypeId()); $this->pass("Test modification of referenced entity's '{$referenced_entity_view_mode}' display.", 'Debug'); $entity_display = entity_get_display($entity_type, $this->entity->bundle(), $referenced_entity_view_mode); $entity_display->save(); $this->verifyPageCache($referencing_entity_path, 'MISS'); $this->verifyPageCache($listing_path, 'MISS'); $this->verifyPageCache($non_referencing_entity_path, 'HIT'); $this->verifyPageCache($empty_entity_listing_path, 'HIT'); $this->verifyPageCache($nonempty_entity_listing_path, 'HIT'); // Verify cache hits. $this->verifyPageCache($referencing_entity_path, 'HIT'); $this->verifyPageCache($listing_path, 'HIT'); } $bundle_entity_type = $this->entity->getEntityType()->getBundleEntityType(); if ($bundle_entity_type !== 'bundle') { // Verify that after modifying the corresponding bundle entity, there is a // cache miss for both the referencing entity, and the listing of // referencing entities, but not for any other routes. $this->pass("Test modification of referenced entity's bundle entity.", 'Debug'); $bundle_entity = entity_load($bundle_entity_type, $this->entity->bundle()); $bundle_entity->save(); $this->verifyPageCache($referencing_entity_path, 'MISS'); $this->verifyPageCache($listing_path, 'MISS'); $this->verifyPageCache($non_referencing_entity_path, 'HIT'); // Special case: entity types may choose to use their bundle entity type // cache tags, to avoid having excessively granular invalidation. $is_special_case = $bundle_entity->getCacheTags() == $this->entity->getCacheTags() && $bundle_entity->getEntityType()->getListCacheTags() == $this->entity->getEntityType()->getListCacheTags(); if ($is_special_case) { $this->verifyPageCache($empty_entity_listing_path, 'MISS'); $this->verifyPageCache($nonempty_entity_listing_path, 'MISS'); } else { $this->verifyPageCache($empty_entity_listing_path, 'HIT'); $this->verifyPageCache($nonempty_entity_listing_path, 'HIT'); } // Verify cache hits. $this->verifyPageCache($referencing_entity_path, 'HIT'); $this->verifyPageCache($listing_path, 'HIT'); if ($is_special_case) { $this->verifyPageCache($empty_entity_listing_path, 'HIT'); $this->verifyPageCache($nonempty_entity_listing_path, 'HIT'); } } if ($this->entity->getEntityType()->get('field_ui_base_route')) { // Verify that after modifying a configurable field on the entity, there // is a cache miss. $this->pass("Test modification of referenced entity's configurable field.", 'Debug'); $field_storage_name = $this->entity->getEntityTypeId() . '.configurable_field'; $field_storage = entity_load('field_storage_config', $field_storage_name); $field_storage->save(); $this->verifyPageCache($referencing_entity_path, 'MISS'); $this->verifyPageCache($listing_path, 'MISS'); $this->verifyPageCache($empty_entity_listing_path, 'HIT'); $this->verifyPageCache($nonempty_entity_listing_path, 'HIT'); $this->verifyPageCache($non_referencing_entity_path, 'HIT'); // Verify cache hits. $this->verifyPageCache($referencing_entity_path, 'HIT'); $this->verifyPageCache($listing_path, 'HIT'); // Verify that after modifying a configurable field on the entity, there // is a cache miss. $this->pass("Test modification of referenced entity's configurable field.", 'Debug'); $field_name = $this->entity->getEntityTypeId() . '.' . $this->entity->bundle() . '.configurable_field'; $field = entity_load('field_config', $field_name); $field->save(); $this->verifyPageCache($referencing_entity_path, 'MISS'); $this->verifyPageCache($listing_path, 'MISS'); $this->verifyPageCache($empty_entity_listing_path, 'HIT'); $this->verifyPageCache($nonempty_entity_listing_path, 'HIT'); $this->verifyPageCache($non_referencing_entity_path, 'HIT'); // Verify cache hits. $this->verifyPageCache($referencing_entity_path, 'HIT'); $this->verifyPageCache($listing_path, 'HIT'); } // Verify that after invalidating the entity's cache tag directly, there is // a cache miss for every route except the ones for the non-referencing // entity and the empty entity listing. $this->pass("Test invalidation of referenced entity's cache tag.", 'Debug'); Cache::invalidateTags($this->entity->getCacheTags()); $this->verifyPageCache($referencing_entity_path, 'MISS'); $this->verifyPageCache($listing_path, 'MISS'); $this->verifyPageCache($nonempty_entity_listing_path, 'MISS'); $this->verifyPageCache($non_referencing_entity_path, 'HIT'); $this->verifyPageCache($empty_entity_listing_path, 'HIT'); // Verify cache hits. $this->verifyPageCache($referencing_entity_path, 'HIT'); $this->verifyPageCache($listing_path, 'HIT'); $this->verifyPageCache($nonempty_entity_listing_path, 'HIT'); // Verify that after invalidating the entity's list cache tag directly, // there is a cache miss for both the empty entity listing and the non-empty // entity listing routes, but not for other routes. $this->pass("Test invalidation of referenced entity's list cache tag.", 'Debug'); Cache::invalidateTags($this->entity->getEntityType()->getListCacheTags()); $this->verifyPageCache($empty_entity_listing_path, 'MISS'); $this->verifyPageCache($nonempty_entity_listing_path, 'MISS'); $this->verifyPageCache($referencing_entity_path, 'HIT'); $this->verifyPageCache($non_referencing_entity_path, 'HIT'); $this->verifyPageCache($listing_path, 'HIT'); // Verify cache hits. $this->verifyPageCache($empty_entity_listing_path, 'HIT'); $this->verifyPageCache($nonempty_entity_listing_path, 'HIT'); if (!empty($view_cache_tag)) { // Verify that after invalidating the generic entity type's view cache tag // directly, there is a cache miss for both the referencing entity, and the // listing of referencing entities, but not for other routes. $this->pass("Test invalidation of referenced entity's 'view' cache tag.", 'Debug'); Cache::invalidateTags($view_cache_tag); $this->verifyPageCache($referencing_entity_path, 'MISS'); $this->verifyPageCache($listing_path, 'MISS'); $this->verifyPageCache($non_referencing_entity_path, 'HIT'); $this->verifyPageCache($empty_entity_listing_path, 'HIT'); $this->verifyPageCache($nonempty_entity_listing_path, 'HIT'); // Verify cache hits. $this->verifyPageCache($referencing_entity_path, 'HIT'); $this->verifyPageCache($listing_path, 'HIT'); } // Verify that after deleting the entity, there is a cache miss for every // route except for the the non-referencing entity one. $this->pass('Test deletion of referenced entity.', 'Debug'); $this->entity->delete(); $this->verifyPageCache($referencing_entity_path, 'MISS'); $this->verifyPageCache($listing_path, 'MISS'); $this->verifyPageCache($empty_entity_listing_path, 'MISS'); $this->verifyPageCache($nonempty_entity_listing_path, 'MISS'); $this->verifyPageCache($non_referencing_entity_path, 'HIT'); // Verify cache hits. $referencing_entity_cache_tags = Cache::mergeTags($this->referencing_entity->getCacheTags(), \Drupal::entityManager()->getViewBuilder('entity_test')->getCacheTags()); $tags = Cache::mergeTags($render_cache_tags, $theme_cache_tags, $referencing_entity_cache_tags); $this->verifyPageCache($referencing_entity_path, 'HIT', $tags); $tags = Cache::mergeTags($render_cache_tags, $theme_cache_tags); $this->verifyPageCache($listing_path, 'HIT', $tags); $tags = Cache::mergeTags($render_cache_tags, $theme_cache_tags, $this->entity->getEntityType()->getListCacheTags()); $this->verifyPageCache($empty_entity_listing_path, 'HIT', $tags); $tags = Cache::mergeTags($tags, $this->getAdditionalCacheTagsForEntityListing()); $this->verifyPageCache($nonempty_entity_listing_path, 'HIT', $tags); }
/** * {@inheritdoc} */ public function isBundleForModeratableEntity(EntityInterface $entity) { $type = $entity->getEntityType(); return $type instanceof ConfigEntityTypeInterface && ($bundle_of = $type->get('bundle_of')) && $this->entityTypeManager->getDefinition($bundle_of)->isRevisionable() && $this->currentUser->hasPermission('administer moderation states'); }
/** * Creates or updates the moderation state of an entity. * * @param \Drupal\Core\Entity\EntityInterface $entity * The entity to update or create a moderation state for. */ protected function updateOrCreateFromEntity(EntityInterface $entity) { $moderation_state = $entity->moderation_state->target_id; /** @var \Drupal\Core\Entity\ContentEntityInterface $entity */ if (!$moderation_state) { $moderation_state = $this->moderationInfo->loadBundleEntity($entity->getEntityType()->getBundleEntityType(), $entity->bundle())->getThirdPartySetting('content_moderation', 'default_moderation_state'); } // @todo what if $entity->moderation_state->target_id is null at this point? $entity_type_id = $entity->getEntityTypeId(); $entity_id = $entity->id(); $entity_revision_id = $entity->getRevisionId(); $entity_langcode = $entity->language()->getId(); $storage = $this->entityTypeManager->getStorage('content_moderation_state'); $entities = $storage->loadByProperties(['content_entity_type_id' => $entity_type_id, 'content_entity_id' => $entity_id]); /** @var \Drupal\content_moderation\ContentModerationStateInterface $content_moderation_state */ $content_moderation_state = reset($entities); if (!$content_moderation_state instanceof ContentModerationStateInterface) { $content_moderation_state = $storage->create(['content_entity_type_id' => $entity_type_id, 'content_entity_id' => $entity_id]); } else { // Create a new revision. $content_moderation_state->setNewRevision(TRUE); } // Sync translations. if (!$content_moderation_state->hasTranslation($entity_langcode)) { $content_moderation_state->addTranslation($entity_langcode); } if ($content_moderation_state->language()->getId() !== $entity_langcode) { $content_moderation_state = $content_moderation_state->getTranslation($entity_langcode); } // Create the ContentModerationState entity for the inserted entity. $content_moderation_state->set('content_entity_revision_id', $entity_revision_id); $content_moderation_state->set('moderation_state', $moderation_state); ContentModerationState::updateOrCreateFromEntity($content_moderation_state); }
/** * Initializes the translation form state. * * @param \Drupal\Core\Form\FormStateInterface $form_state * @param \Drupal\Core\Entity\EntityInterface $host */ protected function initIsTranslating(FormStateInterface $form_state, EntityInterface $host) { if ($this->isTranslating != NULL) { return; } $this->isTranslating = FALSE; if (!$host->isTranslatable()) { return; } if (!$host->getEntityType()->hasKey('default_langcode')) { return; } $default_langcode_key = $host->getEntityType()->getKey('default_langcode'); if (!$host->hasField($default_langcode_key)) { return; } if (!empty($form_state->get('content_translation'))) { // Adding a language through the ContentTranslationController. $this->isTranslating = TRUE; } if ($host->hasTranslation($form_state->get('langcode')) && $host->getTranslation($form_state->get('langcode'))->get($default_langcode_key)->value == 0) { // Editing a translation. $this->isTranslating = TRUE; } }
/** * Adds an operation on bundles that should have a Moderation form. * * @param \Drupal\Core\Entity\EntityInterface $entity * The entity on which to define an operation. * * @return array * An array of operation definitions. * * @see hook_entity_operation(). */ public function entityOperation(EntityInterface $entity) { $operations = []; $type = $entity->getEntityType(); if ($this->moderationInfo->isBundleForModeratableEntity($entity)) { $operations['manage-moderation'] = ['title' => t('Manage moderation'), 'weight' => 27, 'url' => Url::fromRoute("entity.{$type->id()}.moderation", [$entity->getEntityTypeId() => $entity->id()])]; } return $operations; }