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, SectionStorageLocalTaskProviderInterface {
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);
202 public function buildLocalTasks($base_plugin_definition) {
204 foreach ($this->getEntityTypes() as $entity_type_id => $entity_type) {
205 $local_tasks["layout_builder.defaults.$entity_type_id.view"] = $base_plugin_definition + [
206 'route_name' => "layout_builder.defaults.$entity_type_id.view",
207 'title' => $this->t('Manage layout'),
208 'base_route' => "layout_builder.defaults.$entity_type_id.view",
210 $local_tasks["layout_builder.defaults.$entity_type_id.save"] = $base_plugin_definition + [
211 'route_name' => "layout_builder.defaults.$entity_type_id.save",
212 'title' => $this->t('Save Layout'),
213 'parent_id' => "layout_builder_ui:layout_builder.defaults.$entity_type_id.view",
215 $local_tasks["layout_builder.defaults.$entity_type_id.cancel"] = $base_plugin_definition + [
216 'route_name' => "layout_builder.defaults.$entity_type_id.cancel",
217 'title' => $this->t('Cancel Layout'),
219 'parent_id' => "layout_builder_ui:layout_builder.defaults.$entity_type_id.view",
226 * Returns an array of relevant entity types.
228 * @return \Drupal\Core\Entity\EntityTypeInterface[]
229 * An array of entity types.
231 protected function getEntityTypes() {
232 return array_filter($this->entityTypeManager->getDefinitions(), function (EntityTypeInterface $entity_type) {
233 return $entity_type->entityClassImplements(FieldableEntityInterface::class) && $entity_type->hasViewBuilderClass() && $entity_type->get('field_ui_base_route');
240 public function extractIdFromRoute($value, $definition, $name, array $defaults) {
241 if (is_string($value) && strpos($value, '.') !== FALSE) {
245 // If a bundle is not provided but a value corresponding to the bundle key
246 // is, use that for the bundle value.
247 if (empty($defaults['bundle']) && isset($defaults['bundle_key']) && !empty($defaults[$defaults['bundle_key']])) {
248 $defaults['bundle'] = $defaults[$defaults['bundle_key']];
251 if (!empty($defaults['entity_type_id']) && !empty($defaults['bundle']) && !empty($defaults['view_mode_name'])) {
252 return $defaults['entity_type_id'] . '.' . $defaults['bundle'] . '.' . $defaults['view_mode_name'];
259 public function getSectionListFromId($id) {
260 if (strpos($id, '.') === FALSE) {
261 throw new \InvalidArgumentException(sprintf('The "%s" ID for the "%s" section storage type is invalid', $id, $this->getStorageType()));
264 $storage = $this->entityTypeManager->getStorage('entity_view_display');
265 // If the display does not exist, create a new one.
266 if (!$display = $storage->load($id)) {
267 list($entity_type_id, $bundle, $view_mode) = explode('.', $id, 3);
268 $display = $storage->create([
269 'targetEntityType' => $entity_type_id,
271 'mode' => $view_mode,
281 public function getContexts() {
282 $display = $this->getDisplay();
283 $entity = $this->sampleEntityGenerator->get($display->getTargetEntityTypeId(), $display->getTargetBundle());
286 $contexts['layout_builder.entity'] = EntityContext::fromEntity($entity);
293 public function label() {
294 return $this->getDisplay()->label();
300 public function save() {
301 return $this->getDisplay()->save();
307 public function isOverridable() {
308 return $this->getDisplay()->isOverridable();
314 public function setOverridable($overridable = TRUE) {
315 $this->getDisplay()->setOverridable($overridable);
322 public function setThirdPartySetting($module, $key, $value) {
323 $this->getDisplay()->setThirdPartySetting($module, $key, $value);
330 public function isLayoutBuilderEnabled() {
331 return $this->getDisplay()->isLayoutBuilderEnabled();
337 public function enableLayoutBuilder() {
338 $this->getDisplay()->enableLayoutBuilder();
345 public function disableLayoutBuilder() {
346 $this->getDisplay()->disableLayoutBuilder();
353 public function getThirdPartySetting($module, $key, $default = NULL) {
354 return $this->getDisplay()->getThirdPartySetting($module, $key, $default);
360 public function getThirdPartySettings($module) {
361 return $this->getDisplay()->getThirdPartySettings($module);
367 public function unsetThirdPartySetting($module, $key) {
368 $this->getDisplay()->unsetThirdPartySetting($module, $key);
375 public function getThirdPartyProviders() {
376 return $this->getDisplay()->getThirdPartyProviders();
382 public function access($operation, AccountInterface $account = NULL, $return_as_object = FALSE) {
383 $result = AccessResult::allowedIf($this->isLayoutBuilderEnabled());
384 return $return_as_object ? $result : $result->isAllowed();