diff --git a/core/lib/Drupal/Core/Field/TypedData/FieldItemDataDefinition.php b/core/lib/Drupal/Core/Field/TypedData/FieldItemDataDefinition.php index e8dbe48..d59199d 100644 --- a/core/lib/Drupal/Core/Field/TypedData/FieldItemDataDefinition.php +++ b/core/lib/Drupal/Core/Field/TypedData/FieldItemDataDefinition.php @@ -44,6 +44,8 @@ public static function createFromDataType($data_type) { /** * Returns the field definition. + * + * @return array */ public function getFieldDefinition() { return $this->fieldDefinition; diff --git a/core/modules/menu_link/src/Plugin/Field/FieldType/MenuLinkItem.php b/core/modules/menu_link/src/Plugin/Field/FieldType/MenuLinkItem.php index 5fd1bb3..306cc07 100644 --- a/core/modules/menu_link/src/Plugin/Field/FieldType/MenuLinkItem.php +++ b/core/modules/menu_link/src/Plugin/Field/FieldType/MenuLinkItem.php @@ -34,38 +34,48 @@ class MenuLinkItem extends FieldItemBase { protected $menuPluginManager; /** + * The menu parent form selector. + * + * @var \Drupal\Core\Menu\MenuParentFormSelectorInterface + */ + protected $menuParentFormSelector; + + /** * {@inheritdoc} */ public function __construct(DataDefinitionInterface $definition, $name = NULL, TypedDataInterface $parent = NULL) { parent::__construct($definition, $name, $parent); $this->menuPluginManager = \Drupal::service('plugin.manager.menu.link'); + $this->menuParentFormSelector = \Drupal::service('menu.parent_form_selector'); } /** * {@inheritdoc} */ - public static function defaultInstanceSettings() { - $settings = parent::defaultInstanceSettings(); + public static function defaultFieldSettings() { + $settings = parent::defaultFieldSettings(); - $settings['available_menus'] = array('main'); + $settings['available_menus'] = ['main']; + $settings['parent'] = ['main:']; return $settings; } /** * {@inheritdoc} */ - public function instanceSettingsForm(array $form, FormStateInterface $form_state) { - $form = parent::instanceSettingsForm($form, $form_state); + public function fieldSettingsForm(array $form, FormStateInterface $form_state) { + $form = parent::fieldSettingsForm($form, $form_state); $menu_options = menu_ui_get_menus(); - $form['available_menus'] = array( + $form['available_menus'] = [ '#type' => 'checkboxes', '#title' => $this->t('Available menus'), '#default_value' => $this->getSetting('available_menus'), '#options' => $menu_options, '#description' => $this->t('The menus available to place links in for this content type.'), - ); + ]; + $form['parent'] = $this->menuParentFormSelector->getParentSelectOptions($this->getSetting('parent')); return $form; } @@ -74,7 +84,7 @@ public function instanceSettingsForm(array $form, FormStateInterface $form_state * {@inheritdoc} */ public static function propertyDefinitions(FieldStorageDefinitionInterface $field_definition) { - $definitions = array(); + $definitions = []; $definitions['menu_name'] = DataDefinition::create('string') ->setLabel(t('Menu')); @@ -94,39 +104,39 @@ public static function propertyDefinitions(FieldStorageDefinitionInterface $fiel * {@inheritdoc} */ public static function schema(FieldStorageDefinitionInterface $field_definition) { - $schema = array(); + $schema = []; - $schema['columns']['title'] = array( + $schema['columns']['title'] = [ 'description' => 'The link text.', 'type' => 'varchar', 'length' => 255, 'not null' => FALSE, - ); + ]; - $schema['columns']['menu_name'] = array( + $schema['columns']['menu_name'] = [ 'description' => 'The menu of the link', 'type' => 'varchar', 'length' => 255, 'not null' => FALSE, - ); - $schema['columns']['parent'] = array( + ]; + $schema['columns']['parent'] = [ 'description' => 'The parent of the menu link', 'type' => 'varchar', 'length' => 255, 'not null' => FALSE, - ); + ]; - $schema['columns']['weight'] = array( + $schema['columns']['weight'] = [ 'description' => 'The weight of the menu link', 'type' => 'int', - ); + ]; - $schema['columns']['description'] = array( + $schema['columns']['description'] = [ 'description' => 'The link description.', 'type' => 'blob', 'size' => 'big', 'not null' => FALSE, - ); + ]; return $schema; } @@ -179,7 +189,7 @@ protected function doSave() { * * @return string */ - protected function getMenuPluginId() { + public function getMenuPluginId() { $field_name = $this->definition->getFieldDefinition()->getName(); $entity_type_id = $this->getEntity()->getEntityTypeId(); return 'menu_link_field:' . "{$entity_type_id}_{$field_name}_{$this->getEntity()->uuid()}"; @@ -191,12 +201,12 @@ protected function getMenuPluginId() { * @return array */ protected function getMenuPluginDefinition() { - $menu_definition = array(); + $menu_definition = []; $menu_definition['id'] = $this->getPluginId(); $menu_definition['title'] = $this->values['title']; $menu_definition['menu_name'] = $this->values['menu_name']; $menu_definition['parent'] = $this->values['parent']; - $menu_definition['weight'] = $this->values['weight']; + $menu_definition['weight'] = isset($this->values['weight']) ? $this->values['weight'] : 0; $menu_definition['class'] = '\Drupal\menu_link\Plugin\Menu\MenuLinkField'; $menu_definition['form_class'] = '\Drupal\menu_link\Plugin\Menu\Form\MenuLinkFieldForm'; $menu_definition['metadata']['entity_id'] = $this->getEntity()->id(); diff --git a/core/modules/menu_link/src/Plugin/Field/FieldWidget/MenuLinkWidget.php b/core/modules/menu_link/src/Plugin/Field/FieldWidget/MenuLinkWidget.php index c80985d..e8d40dd 100644 --- a/core/modules/menu_link/src/Plugin/Field/FieldWidget/MenuLinkWidget.php +++ b/core/modules/menu_link/src/Plugin/Field/FieldWidget/MenuLinkWidget.php @@ -76,62 +76,107 @@ public function formElement(FieldItemListInterface $items, $delta, array $elemen $default_description = isset($items[$delta]->description) ? $items[$delta]->description : NULL; $default_weight = isset($items[$delta]->weight) ? $items[$delta]->weight : 0; - $available_menus = $items->getSetting('available_menus'); - $menu = isset($items[$delta]->menu) ? $items[$delta]->menu : reset($available_menus); - $parent = isset($items[$delta]->parent) ? $items[$delta]->parent : ""; + $available_menus = array_filter($items->getSetting('available_menus')); + $available_menus = array_combine($available_menus, $available_menus); + $menu_names = array_keys($available_menus); + $menu = !empty($items[$delta]->menu) ? $items[$delta]->menu : reset($menu_names); + $parent = !empty($items[$delta]->parent) ? $items[$delta]->parent : ''; $default_menu_parent = "$menu:$parent"; if ($this->account->hasPermission('administer menu')) { - $element['title'] = array( + + /** @var \Drupal\Core\Entity\EntityInterface $entity */ + $entity = $items->getParent()->getValue(); + $element['#pre_render'][] = [$this, 'preRenderMenuDetails']; + $element['enabled'] = [ + '#type' => 'checkbox', + '#title' => t('Provide a menu link'), + '#default_value' => (int) (bool) $items[$delta]->getPluginId(), + ]; + $element['menu'] = [ + '#type' => 'details', + '#title' => t('Menu settings'), + '#open' => (bool) $items[$delta]->getPluginId(), + '#tree' => FALSE, + '#weight' => $entity->getEntityTypeId() == 'node' ? -2 : 0, + '#group' => $entity->getEntityTypeId() == 'node' ? 'advanced' : NULL, + '#attributes' => ['class' => ['menu-link-form']], + '#attached' => [ + 'library' => ['menu_ui/drupal.menu_ui'], + ], + ]; + + $element['title'] = [ '#type' => 'textfield', '#title' => t('Menu link title'), '#default_value' => $default_title, - ); + ]; - $element['description'] = array( + $element['description'] = [ '#type' => 'textarea', '#title' => t('Description'), '#default_value' => $default_description, '#rows' => 1, '#description' => t('Shown when hovering over the menu link.'), - ); + ]; - $plugin_id = $parent ? $default_menu_parent : ''; - $parent_element = $this->menuParentSelector->parentSelectElement($default_menu_parent, $plugin_id, array_filter($available_menus)); + $plugin_id = $items[$delta]->getMenuPluginId(); + $parent_element = $this->menuParentSelector->parentSelectElement($default_menu_parent, $plugin_id, $available_menus); $element['menu_parent'] = $parent_element; $element['menu_parent']['#title'] = t('Parent item'); $element['menu_parent']['#attributes']['class'][] = 'menu-parent-select'; - $element['weight'] = array( + $element['weight'] = [ '#type' => 'number', '#title' => t('Weight'), '#default_value' => $default_weight, '#description' => t('Menu links with lower weights are displayed before links with higher weights.'), - ); + ]; } else { - $element['title'] = array( + $element['title'] = [ '#type' => 'value', '#value' => $default_title, - ); - $element['description'] = array( + ]; + $element['description'] = [ '#type' => 'value', '#value' => $default_description, - ); - $element['weight'] = array( + ]; + $element['weight'] = [ '#type' => 'value', '#value' => $default_weight, - ); - $element['menu_parent'] = array( + ]; + $element['menu_parent'] = [ '#type' => 'value', '#value' => $default_menu_parent, - ); + ]; } return $element; } + public function preRenderMenuDetails($element) { + $element['menu']['enabled'] = $element['enabled']; + $element['menu']['title'] = $element['title']; + $element['menu']['description'] = $element['description']; + $element['menu']['menu_parent'] = $element['menu_parent']; + $element['menu']['weight'] = $element['weight']; + + // Hide the elements when enabled is disabled. + foreach (['title', 'description', 'menu_parent', 'weight'] as $form_element) { + $element['menu'][$form_element]['#states'] = [ + 'invisible' => [ + 'input[name="' . $element['menu']['enabled']['#name'] . '"' => ['checked' => FALSE], + ], + ]; + } + + unset($element['enabled'], $element['title'], $element['description'], $element['menu_parent'], $element['weight']); + + return $element; + } + /** * {@inheritdoc} */ @@ -140,7 +185,14 @@ public function extractFormValues(FieldItemListInterface $items, array $form, Fo // Extract menu/parent from single select element. foreach ($items as $delta => $item) { - list($item->menu_name, $item->parent) = explode(':', $item->menu_parent, 2); + if (!empty($item->enabled) && !empty($item->menu_parent) && $item->menu_parent != ':') { + list($item->menu_name, $item->parent) = explode(':', $item->menu_parent, 2); + } + else { + $item->menu_name = ''; + $item->parent = ''; + } + unset($item->enabled); unset($item->menu_parent); } } diff --git a/core/modules/menu_link/src/Plugin/Menu/Form/MenuLinkFieldForm.php b/core/modules/menu_link/src/Plugin/Menu/Form/MenuLinkFieldForm.php index 8dab8d4..ef460f8 100644 --- a/core/modules/menu_link/src/Plugin/Menu/Form/MenuLinkFieldForm.php +++ b/core/modules/menu_link/src/Plugin/Menu/Form/MenuLinkFieldForm.php @@ -28,14 +28,14 @@ public function buildConfigurationForm(array $form, FormStateInterface $form_sta $entity = $this->menuLink->getEntity(); - $form['info'] = array( + $form['info'] = [ '#type' => 'item', - '#title' => $this->t('This link is provided by the %type: @label. The path cannot be edited.', array( + '#title' => $this->t('This link is provided by the %type: @label. The path cannot be edited.', [ '%type' => $entity->getEntityType()->getLabel(), '@url' => $entity->url(), '@label' => $entity->label(), - )), - ); + ]), + ]; return $form; } diff --git a/core/modules/menu_link/src/Plugin/Menu/MenuLinkField.php b/core/modules/menu_link/src/Plugin/Menu/MenuLinkField.php index 73985dd..9252047 100644 --- a/core/modules/menu_link/src/Plugin/Menu/MenuLinkField.php +++ b/core/modules/menu_link/src/Plugin/Menu/MenuLinkField.php @@ -76,7 +76,7 @@ public function updateLink(array $new_definition_values, $persist) { $this->pluginDefinition = $new_definition_values + $this->getPluginDefinition(); if ($persist) { - $updated = array(); + $updated = []; foreach ($new_definition_values as $key => $value) { $field = $this->getEntity()->{$field_name}; if (isset($field->{$key})) { diff --git a/core/modules/menu_ui/src/Tests/MenuNodeTest.php b/core/modules/menu_ui/src/Tests/MenuNodeTest.php index 99798d6..fe88faf 100644 --- a/core/modules/menu_ui/src/Tests/MenuNodeTest.php +++ b/core/modules/menu_ui/src/Tests/MenuNodeTest.php @@ -7,6 +7,8 @@ namespace Drupal\menu_ui\Tests; +use Drupal\field\Entity\FieldConfig; +use Drupal\field\Entity\FieldStorageConfig; use Drupal\simpletest\WebTestBase; use Drupal\menu_link_content\Entity\MenuLinkContent; @@ -22,7 +24,22 @@ class MenuNodeTest extends WebTestBase { * * @var array */ - public static $modules = array('menu_ui', 'test_page_test', 'node', 'block'); + public static $modules = array('menu_ui', 'test_page_test', 'node', 'block', 'menu_link'); + + /** + * @var \Drupal\field\Entity\FieldConfig + */ + protected $fieldConfig; + + /** + * @var \Drupal\field\Entity\FieldStorageConfig + */ + protected $fieldStorageConfig; + + /** + * @var \Drupal\field\Entity\FieldStorageConfig + */ + protected $fieldConfigArticle; protected function setUp() { parent::setUp(); @@ -30,6 +47,25 @@ protected function setUp() { $this->drupalPlaceBlock('system_menu_block:main'); $this->drupalCreateContentType(array('type' => 'page', 'name' => 'Basic page')); + $this->drupalCreateContentType(array('type' => 'article', 'name' => 'Article')); + + $this->fieldStorageConfig = FieldStorageConfig::create(['field_name' => 'menu', 'entity_type' => 'node', 'type' => 'menu_link']); + $this->fieldStorageConfig->save(); + $this->fieldConfig = FieldConfig::create(['entity_type' => 'node', 'bundle' => 'page', 'field_name' => 'menu']); + $this->fieldConfig->save(); + entity_get_form_display('node', 'page', 'default') + ->setComponent('menu', [ + 'type' => 'menu_link_default', + ]) + ->save(); + + $this->fieldConfigArticle = FieldConfig::create(['entity_type' => 'node', 'bundle' => 'article', 'field_name' => 'menu']); + $this->fieldConfigArticle->save(); + entity_get_form_display('node', 'article', 'default') + ->setComponent('menu', [ + 'type' => 'menu_link_default', + ]) + ->save(); $this->admin_user = $this->drupalCreateUser(array( 'access administration pages', @@ -47,15 +83,12 @@ protected function setUp() { */ function testMenuNodeFormWidget() { // Disable the default main menu, so that no menus are enabled. - $edit = array( - 'menu_options[main]' => FALSE, - ); - $this->drupalPostForm('admin/structure/types/manage/page', $edit, t('Save content type')); + $this->fieldConfig->settings['available_menus']['menu'] = FALSE; + $this->fieldConfig->save(); // Verify that no menu settings are displayed and nodes can be created. $this->drupalGet('node/add/page'); $this->assertText(t('Create Basic page')); - $this->assertNoText(t('Menu settings')); $node_title = $this->randomMachineName(); $edit = array( 'title[0][value]' => $node_title, @@ -67,22 +100,15 @@ function testMenuNodeFormWidget() { // Test that we cannot set a menu item from a menu that is not set as // available. - $edit = array( - 'menu_options[tools]' => 1, - 'menu_parent' => 'main:', - ); - $this->drupalPostForm('admin/structure/types/manage/page', $edit, t('Save content type')); - $this->assertText(t('The selected menu item is not under one of the selected menus.')); - $this->assertNoRaw(t('The content type %name has been updated.', array('%name' => 'Basic page'))); + $this->fieldConfig->settings['available_menus']['tools'] = 'tools'; + $this->fieldConfig->settings['parent'] = 'main:'; + $this->fieldConfig->save(); // Enable Tools menu as available menu. - $edit = array( - 'menu_options[main]' => 1, - 'menu_options[tools]' => 1, - 'menu_parent' => 'main:', - ); - $this->drupalPostForm('admin/structure/types/manage/page', $edit, t('Save content type')); - $this->assertRaw(t('The content type %name has been updated.', array('%name' => 'Basic page'))); + $this->fieldConfig->settings['available_menus']['tools'] = 'tools'; + $this->fieldConfig->settings['available_menus']['main'] = 'main'; + $this->fieldConfig->settings['parent'] = 'main:'; + $this->fieldConfig->save(); // Create a node. $node_title = $this->randomMachineName(); @@ -98,7 +124,7 @@ function testMenuNodeFormWidget() { // Edit the node, enable the menu link setting, but skip the link title. $edit = array( - 'menu[enabled]' => 1, + 'menu[0][enabled]' => 1, ); $this->drupalPostForm('node/' . $node->id() . '/edit', $edit, t('Save')); // Assert that there is no link for the node. @@ -107,9 +133,9 @@ function testMenuNodeFormWidget() { // Edit the node and create a menu link. $edit = array( - 'menu[enabled]' => 1, - 'menu[title]' => $node_title, - 'menu[weight]' => 17, + 'menu[0][enabled]' => 1, + 'menu[0][title]' => $node_title, + 'menu[0][weight]' => 17, ); $this->drupalPostForm('node/' . $node->id() . '/edit', $edit, t('Save')); // Assert that the link exists. @@ -117,11 +143,11 @@ function testMenuNodeFormWidget() { $this->assertLink($node_title); $this->drupalGet('node/' . $node->id() . '/edit'); - $this->assertFieldById('edit-menu-weight', 17, 'Menu weight correct in edit form'); + $this->assertFieldById('edit-menu-0-weight', 17, 'Menu weight correct in edit form'); // Edit the node and remove the menu link. $edit = array( - 'menu[enabled]' => FALSE, + 'menu[0][enabled]' => FALSE, ); $this->drupalPostForm('node/' . $node->id() . '/edit', $edit, t('Save')); // Assert that there is no link for the node. @@ -129,13 +155,9 @@ function testMenuNodeFormWidget() { $this->assertNoLink($node_title); // Add a menu link to the Administration menu. - $item = entity_create('menu_link_content', array( - 'route_name' => 'entity.node.canonical', - 'route_parameters' => array('node' => $node->id()), - 'title' => $this->randomMachineName(16), - 'menu_name' => 'admin', - )); - $item->save(); + $node->menu->title = $this->randomMachineName(16); + $node->menu->menu_name = 'admin'; + $node->save(); // Assert that disabled Administration menu is not shown on the // node/$nid/edit page. @@ -143,29 +165,27 @@ function testMenuNodeFormWidget() { $this->assertText('Provide a menu link', 'Link in not allowed menu not shown in node edit form'); // Assert that the link is still in the Administration menu after save. $this->drupalPostForm('node/' . $node->id() . '/edit', $edit, t('Save')); - $link = MenuLinkContent::load($item->id()); + $link = \Drupal::service('plugin.manager.menu.link')->getDefinition($node->menu->first()->getMenuPluginId()); $this->assertTrue($link, 'Link in not allowed menu still exists after saving node'); // Move the menu link back to the Tools menu. - $item->menu_name->value = 'tools'; - $item->save(); + $node->menu->menu_name = 'tools'; + $node->save(); + // Create a second node. $child_node = $this->drupalCreateNode(array('type' => 'article')); // Assign a menu link to the second node, being a child of the first one. - $child_item = entity_create('menu_link_content', array( - 'route_name' => 'entity.node.canonical', - 'route_parameters' => array('node' => $child_node->id()), - 'title' => $this->randomMachineName(16), - 'parent' => $item->getPluginId(), - 'menu_name' => $item->getMenuName(), - )); - $child_item->save(); + $child_node->menu->title = $this->randomMachineName(16); + $child_node->menu->parent = $node->menu->first()->getMenuPluginId(); + $child_node->menu->menu_name = $node->menu->menu_name; + $child_node->save(); + // Edit the first node. $this->drupalGet('node/'. $node->id() .'/edit'); // Assert that it is not possible to set the parent of the first node to itself or the second node. - $this->assertNoOption('edit-menu-menu-parent', 'tools:'. $item->getPluginId()); - $this->assertNoOption('edit-menu-menu-parent', 'tools:'. $child_item->getPluginId()); + $this->assertNoOption('edit-menu-0-menu-parent', 'tools:'. $node->menu->first()->getMenuPluginId()); + $this->assertNoOption('edit-menu-0-menu-parent', 'tools:'. $child_node->menu->first()->getMenuPluginId()); // Assert that unallowed Administration menu is not available in options. - $this->assertNoOption('edit-menu-menu-parent', 'admin:'); + $this->assertNoOption('edit-menu-0-menu-parent', 'admin:'); } } diff --git a/core/modules/system/src/Tests/Menu/BreadcrumbTest.php b/core/modules/system/src/Tests/Menu/BreadcrumbTest.php index 0351b89..ecf6a28 100644 --- a/core/modules/system/src/Tests/Menu/BreadcrumbTest.php +++ b/core/modules/system/src/Tests/Menu/BreadcrumbTest.php @@ -9,7 +9,8 @@ use Drupal\Component\Utility\String; use Drupal\Component\Utility\Unicode; -use Drupal\node\Entity\NodeType; +use Drupal\field\Entity\FieldConfig; +use Drupal\field\Entity\FieldStorageConfig; /** * Tests breadcrumbs functionality. @@ -23,7 +24,7 @@ class BreadcrumbTest extends MenuTestBase { * * @var array */ - public static $modules = array('menu_test', 'block'); + public static $modules = array('menu_test', 'block', 'menu_link'); /** * Test paths in the Standard profile. @@ -158,11 +159,20 @@ function testBreadCrumbs() { // @todo Also test all themes? Manually testing led to the suspicion that // breadcrumbs may differ, possibly due to theme overrides. $menus = array('main', 'tools'); - // Alter node type menu settings. - $node_type = NodeType::load($type); - $node_type->setThirdPartySetting('menu_ui', 'available_menus', $menus); - $node_type->setThirdPartySetting('menu_ui', 'parent', 'tools:'); - $node_type->save(); + + // Add a menu field to the node type. + $field_storage_config = FieldStorageConfig::create(['field_name' => 'menu', 'entity_type' => 'node', 'type' => 'menu_link']); + $field_storage_config->save(); + $field_config = FieldConfig::create(['entity_type' => 'node', 'bundle' => $type, 'field_name' => 'menu']); + $field_config->settings['available_menus'] = array_combine($menus, $menus); + $field_config->settings['parent'] = 'tools:'; + $field_config->save(); + + entity_get_form_display('node', $type, 'default') + ->setComponent('menu', [ + 'type' => 'menu_link_default', + ]) + ->save(); foreach ($menus as $menu) { // Create a parent node in the current menu. @@ -170,14 +180,11 @@ function testBreadCrumbs() { $node2 = $this->drupalCreateNode(array( 'type' => $type, 'title' => $title, - 'menu' => array( - 'enabled' => 1, - 'title' => 'Parent ' . $title, - 'description' => '', - 'menu_name' => $menu, - 'parent' => '', - ), )); + $node2->menu->title = 'Parent ' . $title; + $node2->menu->description = ''; + $node2->menu->menu_name = $menu; + $node2->menu->parent = ''; if ($menu == 'tools') { $parent = $node2; @@ -196,7 +203,7 @@ function testBreadCrumbs() { $link = reset($menu_links); $edit = array( - 'menu[menu_parent]' => $link->getMenuName() . ':' . $link->getPluginId(), + 'menu[0][menu_parent]' => $link->getMenuName() . ':' . $link->getPluginId(), ); $this->drupalPostForm('node/' . $parent->id() . '/edit', $edit, t('Save and keep published')); $expected = array(