3 namespace Drupal\layout_builder\Plugin\SectionStorage;
5 use Drupal\Component\Utility\NestedArray;
6 use Drupal\Core\Access\AccessResult;
7 use Drupal\Core\Entity\EntityTypeBundleInfoInterface;
8 use Drupal\Core\Entity\EntityTypeInterface;
9 use Drupal\Core\Entity\EntityTypeManagerInterface;
10 use Drupal\Core\Entity\FieldableEntityInterface;
11 use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
12 use Drupal\Core\Plugin\Context\EntityContext;
13 use Drupal\Core\Session\AccountInterface;
15 use Drupal\field_ui\FieldUI;
16 use Drupal\layout_builder\DefaultsSectionStorageInterface;
17 use Drupal\layout_builder\Entity\LayoutBuilderSampleEntityGenerator;
18 use Drupal\layout_builder\Entity\LayoutEntityDisplayInterface;
19 use Drupal\layout_builder\SectionListInterface;
20 use Symfony\Component\DependencyInjection\ContainerInterface;
21 use Symfony\Component\Routing\RouteCollection;
24 * Defines the 'defaults' section storage type.
31 * Layout Builder is currently experimental and should only be leveraged by
32 * experimental modules and development releases of contributed modules.
33 * See https://www.drupal.org/core/experimental for more information.
35 class DefaultsSectionStorage extends SectionStorageBase implements ContainerFactoryPluginInterface, DefaultsSectionStorageInterface {
38 * The entity type manager.
40 * @var \Drupal\Core\Entity\EntityTypeManagerInterface
42 protected $entityTypeManager;
45 * The entity type bundle info.
47 * @var \Drupal\Core\Entity\EntityTypeBundleInfoInterface
49 protected $entityTypeBundleInfo;
54 * @var \Drupal\layout_builder\Entity\LayoutEntityDisplayInterface
56 protected $sectionList;
59 * The sample entity generator.
61 * @var \Drupal\layout_builder\Entity\LayoutBuilderSampleEntityGenerator
63 protected $sampleEntityGenerator;
68 public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityTypeManagerInterface $entity_type_manager, EntityTypeBundleInfoInterface $entity_type_bundle_info, LayoutBuilderSampleEntityGenerator $sample_entity_generator) {
69 parent::__construct($configuration, $plugin_id, $plugin_definition);
71 $this->entityTypeManager = $entity_type_manager;
72 $this->entityTypeBundleInfo = $entity_type_bundle_info;
73 $this->sampleEntityGenerator = $sample_entity_generator;
79 public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
84 $container->get('entity_type.manager'),
85 $container->get('entity_type.bundle.info'),
86 $container->get('layout_builder.sample_entity_generator')
93 public function setSectionList(SectionListInterface $section_list) {
94 if (!$section_list instanceof LayoutEntityDisplayInterface) {
95 throw new \InvalidArgumentException('Defaults expect a display-based section list');
98 return parent::setSectionList($section_list);
102 * Gets the entity storing the overrides.
104 * @return \Drupal\layout_builder\Entity\LayoutEntityDisplayInterface
105 * The entity storing the defaults.
107 protected function getDisplay() {
108 return $this->getSectionList();
114 public function getStorageId() {
115 return $this->getDisplay()->id();
121 public function getRedirectUrl() {
122 return Url::fromRoute("entity.entity_view_display.{$this->getDisplay()->getTargetEntityTypeId()}.view_mode", $this->getRouteParameters());
128 public function getLayoutBuilderUrl($rel = 'view') {
129 return Url::fromRoute("layout_builder.{$this->getStorageType()}.{$this->getDisplay()->getTargetEntityTypeId()}.$rel", $this->getRouteParameters());
133 * Provides the route parameters needed to generate a URL for this object.
136 * An associative array of parameter names and values.
138 protected function getRouteParameters() {
139 $display = $this->getDisplay();
140 $entity_type = $this->entityTypeManager->getDefinition($display->getTargetEntityTypeId());
141 $route_parameters = FieldUI::getRouteBundleParameter($entity_type, $display->getTargetBundle());
142 $route_parameters['view_mode_name'] = $display->getMode();
143 return $route_parameters;
149 public function buildRoutes(RouteCollection $collection) {
150 foreach ($this->getEntityTypes() as $entity_type_id => $entity_type) {
151 // Try to get the route from the current collection.
152 if (!$entity_route = $collection->get($entity_type->get('field_ui_base_route'))) {
156 $path = $entity_route->getPath() . '/display-layout/{view_mode_name}';
159 $defaults['entity_type_id'] = $entity_type_id;
160 // If the entity type has no bundles and it doesn't use {bundle} in its
161 // admin path, use the entity type.
162 if (strpos($path, '{bundle}') === FALSE) {
163 if (!$entity_type->hasKey('bundle')) {
164 $defaults['bundle'] = $entity_type_id;
167 $defaults['bundle_key'] = $entity_type->getBundleEntityType();
172 $requirements['_field_ui_view_mode_access'] = 'administer ' . $entity_type_id . ' display';
174 $options = $entity_route->getOptions();
175 $options['_admin_route'] = FALSE;
177 $this->buildLayoutRoutes($collection, $this->getPluginDefinition(), $path, $defaults, $requirements, $options, $entity_type_id);
180 "entity.entity_view_display.{$entity_type_id}.default",
181 "entity.entity_view_display.{$entity_type_id}.view_mode",
183 foreach ($route_names as $route_name) {
184 if (!$route = $collection->get($route_name)) {
188 $route->addDefaults([
189 'section_storage_type' => $this->getStorageType(),
190 'section_storage' => '',
192 $parameters['section_storage']['layout_builder_tempstore'] = TRUE;
193 $parameters = NestedArray::mergeDeep($parameters, $route->getOption('parameters') ?: []);
194 $route->setOption('parameters', $parameters);
200 * Returns an array of relevant entity types.
202 * @return \Drupal\Core\Entity\EntityTypeInterface[]
203 * An array of entity types.
205 protected function getEntityTypes() {
206 return array_filter($this->entityTypeManager->getDefinitions(), function (EntityTypeInterface $entity_type) {
207 return $entity_type->entityClassImplements(FieldableEntityInterface::class) && $entity_type->hasViewBuilderClass() && $entity_type->get('field_ui_base_route');
214 public function extractIdFromRoute($value, $definition, $name, array $defaults) {
215 if (is_string($value) && strpos($value, '.') !== FALSE) {
219 // If a bundle is not provided but a value corresponding to the bundle key
220 // is, use that for the bundle value.
221 if (empty($defaults['bundle']) && isset($defaults['bundle_key']) && !empty($defaults[$defaults['bundle_key']])) {
222 $defaults['bundle'] = $defaults[$defaults['bundle_key']];
225 if (!empty($defaults['entity_type_id']) && !empty($defaults['bundle']) && !empty($defaults['view_mode_name'])) {
226 return $defaults['entity_type_id'] . '.' . $defaults['bundle'] . '.' . $defaults['view_mode_name'];
233 public function getSectionListFromId($id) {
234 if (strpos($id, '.') === FALSE) {
235 throw new \InvalidArgumentException(sprintf('The "%s" ID for the "%s" section storage type is invalid', $id, $this->getStorageType()));
238 $storage = $this->entityTypeManager->getStorage('entity_view_display');
239 // If the display does not exist, create a new one.
240 if (!$display = $storage->load($id)) {
241 list($entity_type_id, $bundle, $view_mode) = explode('.', $id, 3);
242 $display = $storage->create([
243 'targetEntityType' => $entity_type_id,
245 'mode' => $view_mode,
255 public function getContexts() {
256 $display = $this->getDisplay();
257 $entity = $this->sampleEntityGenerator->get($display->getTargetEntityTypeId(), $display->getTargetBundle());
260 $contexts['layout_builder.entity'] = EntityContext::fromEntity($entity);
267 public function label() {
268 return $this->getDisplay()->label();
274 public function save() {
275 return $this->getDisplay()->save();
281 public function isOverridable() {
282 return $this->getDisplay()->isOverridable();
288 public function setOverridable($overridable = TRUE) {
289 $this->getDisplay()->setOverridable($overridable);
296 public function setThirdPartySetting($module, $key, $value) {
297 $this->getDisplay()->setThirdPartySetting($module, $key, $value);
304 public function isLayoutBuilderEnabled() {
305 return $this->getDisplay()->isLayoutBuilderEnabled();
311 public function enableLayoutBuilder() {
312 $this->getDisplay()->enableLayoutBuilder();
319 public function disableLayoutBuilder() {
320 $this->getDisplay()->disableLayoutBuilder();
327 public function getThirdPartySetting($module, $key, $default = NULL) {
328 return $this->getDisplay()->getThirdPartySetting($module, $key, $default);
334 public function getThirdPartySettings($module) {
335 return $this->getDisplay()->getThirdPartySettings($module);
341 public function unsetThirdPartySetting($module, $key) {
342 $this->getDisplay()->unsetThirdPartySetting($module, $key);
349 public function getThirdPartyProviders() {
350 return $this->getDisplay()->getThirdPartyProviders();
356 public function access($operation, AccountInterface $account = NULL, $return_as_object = FALSE) {
357 $result = AccessResult::allowedIf($this->isLayoutBuilderEnabled());
358 return $return_as_object ? $result : $result->isAllowed();