diff --git a/core/includes/common.inc b/core/includes/common.inc index 654d849..9593304 100644 --- a/core/includes/common.inc +++ b/core/includes/common.inc @@ -1692,6 +1692,7 @@ function datetime_default_format_type() { * path is assumed already to be the correct path alias, and the alias is * not looked up. * - The special string '' generates a link to the site's base URL. + * - The special string '' generates an empty URL. * - If your external URL contains a query (e.g. http://example.com/foo?a=b), * then you can either URL encode the query keys and values yourself and * include them in $path, or use $options['query'] to let this function @@ -1811,6 +1812,10 @@ function url($path = NULL, array $options = array()) { if ($path == '') { $path = ''; } + // The special path '' links to nothing. + else if ($path == '') { + return ''; + } elseif (!empty($path) && !$options['alias']) { $langcode = isset($options['language']) && isset($options['language']->langcode) ? $options['language']->langcode : ''; $alias = drupal_container()->get('path.alias_manager')->getPathAlias($original_path, $langcode); diff --git a/core/includes/menu.inc b/core/includes/menu.inc index 6b62bf8..2c3fbe6 100644 --- a/core/includes/menu.inc +++ b/core/includes/menu.inc @@ -1591,7 +1591,13 @@ function theme_menu_link(array $variables) { if ($element['#below']) { $sub_menu = drupal_render($element['#below']); } - $output = l($element['#title'], $element['#href'], $element['#localized_options']); + + if ($element['#href'] != '') { + $output = l($element['#title'], $element['#href'], $element['#localized_options']); + } + else { + $output = '' . $element['#title'] . ''; + } return '' . $output . $sub_menu . "\n"; } diff --git a/core/includes/path.inc b/core/includes/path.inc index 182c0c8..39cf147 100644 --- a/core/includes/path.inc +++ b/core/includes/path.inc @@ -199,7 +199,7 @@ function drupal_valid_path($path, $dynamic_allowed = FALSE) { global $menu_admin; // We indicate that a menu administrator is running the menu access check. $menu_admin = TRUE; - if ($path == '' || url_is_external($path)) { + if ($path == '' || $path == '' || url_is_external($path)) { $item = array('access' => TRUE); } elseif ($dynamic_allowed && preg_match('/\/\%/', $path)) { diff --git a/core/includes/theme.inc b/core/includes/theme.inc index 47c99d9..53ceb96 100644 --- a/core/includes/theme.inc +++ b/core/includes/theme.inc @@ -1810,7 +1810,12 @@ function theme_links($variables) { } else { // Pass in $link as $options, they share the same keys. - $item = l($link['title'], $link['href'], $link); + if ($link['href'] != '') { + $item = l($link['title'], $link['href'], $link); + } + else { + $item = '' . $link['title'] . ''; + } } } // Handle title-only text items. diff --git a/core/modules/menu_link/lib/Drupal/menu_link/MenuLinkFormController.php b/core/modules/menu_link/lib/Drupal/menu_link/MenuLinkFormController.php index 9506602..845fdd0 100644 --- a/core/modules/menu_link/lib/Drupal/menu_link/MenuLinkFormController.php +++ b/core/modules/menu_link/lib/Drupal/menu_link/MenuLinkFormController.php @@ -62,7 +62,7 @@ public function form(array $form, array &$form_state, EntityInterface $menu_link '#title' => t('Path'), '#maxlength' => 255, '#default_value' => $path, - '#description' => t('The path for this menu link. This can be an internal Drupal path such as %add-node or an external URL such as %drupal. Enter %front to link to the front page.', array('%front' => '', '%add-node' => 'node/add', '%drupal' => 'http://drupal.org')), + '#description' => t('The path for this menu link. This can be an internal Drupal path such as %add-node or an external URL such as %drupal. Enter %front to link to the front page. Enter %none for unlinked text.', array('%front' => '', '%add-node' => 'node/add', '%drupal' => 'http://drupal.org', '%none' => '')), '#required' => TRUE, ); } diff --git a/core/modules/menu_link/lib/Drupal/menu_link/MenuLinkStorageController.php b/core/modules/menu_link/lib/Drupal/menu_link/MenuLinkStorageController.php index dd4ffcc..22a9506 100644 --- a/core/modules/menu_link/lib/Drupal/menu_link/MenuLinkStorageController.php +++ b/core/modules/menu_link/lib/Drupal/menu_link/MenuLinkStorageController.php @@ -160,9 +160,9 @@ public function save(EntityInterface $entity) { * Overrides DatabaseStorageController::preSave(). */ protected function preSave(EntityInterface $entity) { - // This is the easiest way to handle the unique internal path '', + // This is the easiest way to handle unique paths '' and '', // since a path marked as external does not need to match a router path. - $entity->external = (url_is_external($entity->link_path) || $entity->link_path == '') ? 1 : 0; + $entity->external = (url_is_external($entity->link_path) || $entity->link_path == '' || $entity->link_path == '') ? 1 : 0; // Try to find a parent link. If found, assign it and derive its menu. $parent_candidates = !empty($entity->parentCandidates) ? $entity->parentCandidates : array(); diff --git a/core/modules/system/lib/Drupal/system/Tests/Menu/EmptyLinkPathTest.php b/core/modules/system/lib/Drupal/system/Tests/Menu/EmptyLinkPathTest.php new file mode 100644 index 0000000..a00792e --- /dev/null +++ b/core/modules/system/lib/Drupal/system/Tests/Menu/EmptyLinkPathTest.php @@ -0,0 +1,70 @@ + 'Menu link with empty path', + 'description' => 'Test handling of empty link path.', + 'group' => 'Menu', + ); + } + + /** + * Test automatic reparenting of menu links. + */ + function testEmptyLinkPath($module = 'menu_test') { + $path = ''; + + // Verify that the path is valid. + $path_is_valid = drupal_valid_path($path); + $this->assertTrue($path_is_valid, '<none> is a valid path.'); + + // Verify that url return an empty string. + $link = url($path); + $this->assertEqual($link, '', 'Url <none> return an empty string.'); + + // Menu link without a path return a span. + $link_title = 'Empty path'; + $link = array( + '#title' => $link_title, + '#href' => $path, + '#below' => array(), + '#localized_options' => array(), + '#attributes' => array() + ); + $menu_link = array( + 'element' => $link + ); + $menu_link_string = theme_menu_link($menu_link); + $expected_link_string = '' . $link_title . ''; + $contains_link = (strpos($menu_link_string, $expected_link_string) !== FALSE); + $this->assertTrue($contains_link, 'Menu link without path return a span.'); + + // Verify that theme_links() also accept path. + $links_string = theme_links(array( + 'links' => array( + array( + 'title' => $link_title, + 'href' => $path + ) + ), + 'attributes' => array(), + 'heading' => array() + )); + $contains_link = (strpos($links_string, $expected_link_string) !== FALSE); + $this->assertTrue($contains_link, 'Function theme_links() accept <none> path.'); + } + +}