From b66a014f1811eb47c52d356d5b5a657598bb4551 Mon Sep 17 00:00:00 2001
From: amontero <amontero@166637.no-reply.drupal.org>
Date: Sun, 25 Aug 2013 20:03:09 +0200
Subject: [PATCH] Allow menu items without path.

---
 core/includes/menu.inc                             |  8 ++-
 core/includes/path.inc                             |  2 +-
 core/includes/theme.inc                            |  7 +-
 .../lib/Drupal/menu_link/Entity/MenuLink.php       |  4 +-
 .../Drupal/menu_link/MenuLinkFormController.php    |  2 +-
 .../Drupal/menu_link/Tests/EmptyLinkPathTest.php   | 74 ++++++++++++++++++++++
 6 files changed, 91 insertions(+), 6 deletions(-)
 create mode 100644 core/modules/menu_link/lib/Drupal/menu_link/Tests/EmptyLinkPathTest.php

diff --git a/core/includes/menu.inc b/core/includes/menu.inc
index d205ac0..08f56ae 100644
--- a/core/includes/menu.inc
+++ b/core/includes/menu.inc
@@ -1662,7 +1662,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'] != '<none>') {
+    $output = l($element['#title'], $element['#href'], $element['#localized_options']);
+  }
+  else {
+    $output = '<span class="no-link">' . $element['#title'] . '</span>';
+  }
   return '<li' . new Attribute($element['#attributes']) . '>' . $output . $sub_menu . "</li>\n";
 }
 
diff --git a/core/includes/path.inc b/core/includes/path.inc
index ba1da88..848572f 100644
--- a/core/includes/path.inc
+++ b/core/includes/path.inc
@@ -195,7 +195,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 == '<front>' || url_is_external($path)) {
+  if ($path == '<front>' || $path == '<none>' || 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 122cc63..62f6f2b 100644
--- a/core/includes/theme.inc
+++ b/core/includes/theme.inc
@@ -1719,7 +1719,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'] != '<none>') {
+            $item = l($link['title'], $link['href'], $link);
+          }
+          else {
+            $item = '<span class="no-link">' . $link['title'] . '</span>';
+          }
         }
       }
       // Handle title-only text items.
diff --git a/core/modules/menu_link/lib/Drupal/menu_link/Entity/MenuLink.php b/core/modules/menu_link/lib/Drupal/menu_link/Entity/MenuLink.php
index f51b585..350ecac 100644
--- a/core/modules/menu_link/lib/Drupal/menu_link/Entity/MenuLink.php
+++ b/core/modules/menu_link/lib/Drupal/menu_link/Entity/MenuLink.php
@@ -427,9 +427,9 @@ public static function postDelete(EntityStorageControllerInterface $storage_cont
    * {@inheritdoc}
    */
   public function preSave(EntityStorageControllerInterface $storage_controller) {
-    // This is the easiest way to handle the unique internal path '<front>',
+    // This is the easiest way to handle unique paths '<front>' and '<none>',
     // since a path marked as external does not need to match a router path.
-    $this->external = (url_is_external($this->link_path) || $this->link_path == '<front>') ? 1 : 0;
+    $this->external = (url_is_external($this->link_path) || $this->link_path == '<front>' || $this->link_path == '<none>') ? 1 : 0;
 
     // Try to find a parent link. If found, assign it and derive its menu.
     $parent_candidates = !empty($this->parentCandidates) ? $this->parentCandidates : array();
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 e94a0a6..b2f949b 100644
--- a/core/modules/menu_link/lib/Drupal/menu_link/MenuLinkFormController.php
+++ b/core/modules/menu_link/lib/Drupal/menu_link/MenuLinkFormController.php
@@ -115,7 +115,7 @@ public function form(array $form, array &$form_state) {
         '#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' => '<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' => '<front>', '%add-node' => 'node/add', '%drupal' => 'http://drupal.org', '%none' => '<none>')),
         '#required' => TRUE,
       );
     }
diff --git a/core/modules/menu_link/lib/Drupal/menu_link/Tests/EmptyLinkPathTest.php b/core/modules/menu_link/lib/Drupal/menu_link/Tests/EmptyLinkPathTest.php
new file mode 100644
index 0000000..bec63e2
--- /dev/null
+++ b/core/modules/menu_link/lib/Drupal/menu_link/Tests/EmptyLinkPathTest.php
@@ -0,0 +1,74 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\menu_link\Tests\EmptyLinkPathTest.
+ */
+
+namespace Drupal\menu_link\Tests;
+
+use Drupal\simpletest\DrupalUnitTestBase;
+
+/**
+ * Tests for menu links.
+ */
+class EmptyLinkPathTest extends DrupalUnitTestBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function getInfo() {
+    return array(
+      'name' => 'Menu link with empty path',
+      'description' => 'Test handling of empty link path.',
+      'group' => 'Menu',
+    );
+  }
+
+  /**
+   * Tests creation of empty menu links.
+   */
+  public function testEmptyLinkPath($module = 'menu_test') {
+    $path = '<none>';
+
+    // Verify that the path is valid.
+    $path_is_valid = drupal_valid_path($path);
+    $this->assertTrue($path_is_valid, '&lt;none&gt; is a valid path.');
+
+    // Verify that url return an empty string.
+    $link = url($path);
+    $this->assertEqual($link, '', 'Url &lt;none&gt; 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 = '<span class="no-link">' . $link_title . '</span>';
+    $contains_link = (strpos($menu_link_string, $expected_link_string) !== FALSE);
+    $this->assertTrue($contains_link, 'Menu items without a path are displayed as a &lt;span&gt; element.');
+
+    // Verify that theme_links() also accept <none> 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 &lt;none&gt; path.');
+  }
+
+}
-- 
1.8.3.4

