From 1da3c7fe0474a688fa7bd2d20fd7498f4b3a7982 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?"J.=20Rene=CC=81e=20Beach"?= <splendidnoise@gmail.com>
Date: Sun, 9 Dec 2012 01:00:59 -0500
Subject: [PATCH] Issue #1847198 by benjifisher, jessebeach: Refactor
 toolbar_view and the definition of the array returned by
 hook_toolbar
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Signed-off-by: J. Renée Beach <splendidnoise@gmail.com>
---
 core/modules/shortcut/shortcut.module              |   23 +-
 core/modules/toolbar/css/toolbar.base.css          |    4 +-
 core/modules/toolbar/js/toolbar.js                 |    2 +-
 core/modules/toolbar/templates/toolbar.twig        |   30 +--
 .../tests/modules/toolbar_test/toolbar_test.module |   26 ++-
 core/modules/toolbar/toolbar.module                |  236 ++++++++++++--------
 core/modules/user/user.module                      |   33 +--
 7 files changed, 205 insertions(+), 149 deletions(-)

diff --git a/core/modules/shortcut/shortcut.module b/core/modules/shortcut/shortcut.module
index 8677c72..42c049b 100644
--- a/core/modules/shortcut/shortcut.module
+++ b/core/modules/shortcut/shortcut.module
@@ -734,24 +734,25 @@ function shortcut_toolbar() {
     );
   }
 
-  $links_tray = array(
-    '#heading' => t('User-defined shortcuts'),
-    'shortcuts' => $links,
-    'configure' => $configure_link,
-  );
-
   $items['shortcuts'] = array(
-    'tab' => array(
-      'title' => t('Shortcuts'),
-      'href' => 'admin/config/user-interface/shortcut',
+    "#theme_wrappers" => array('toolbar_tab_wrapper'),
+    '#theme' => 'toolbar_tray_toggle',
+    '#text' => t('Shortcuts'),
+    '#path' => 'admin/config/user-interface/shortcut',
+    '#options' => array(
       'html' => FALSE,
       'attributes' => array(
         'title' => t('Shortcuts'),
         'class' => array('icon', 'icon-shortcut'),
       ),
     ),
-    'tray' => $links_tray,
-    'weight' => -10,
+    '#tray' => array(
+      '#theme_wrappers' => array('toolbar_tray_wrapper'),
+      '#heading' => t('User-defined shortcuts'),
+      'shortcuts' => $links,
+      'configure' => $configure_link,
+    ),
+    '#weight' => -10,
   );
   return $items;
 }
diff --git a/core/modules/toolbar/css/toolbar.base.css b/core/modules/toolbar/css/toolbar.base.css
index cbdf153..1dc640b 100644
--- a/core/modules/toolbar/css/toolbar.base.css
+++ b/core/modules/toolbar/css/toolbar.base.css
@@ -43,8 +43,8 @@ html.js .toolbar {
 .js .toolbar .menu li {
   display: block;
 }
-.js .toolbar .bar li,
-.js .toolbar .horizontal li  {
+.js .toolbar .bar .tab,
+.js .toolbar .horizontal .tab  {
   float: left; /* LTR */
 }
 .js .toolbar a {
diff --git a/core/modules/toolbar/js/toolbar.js b/core/modules/toolbar/js/toolbar.js
index 08a68d2..cd97b1d 100644
--- a/core/modules/toolbar/js/toolbar.js
+++ b/core/modules/toolbar/js/toolbar.js
@@ -78,7 +78,7 @@ Drupal.behaviors.toolbar = {
       // query application decide the orientation.
       changeOrientation((locked) ? 'vertical' : ((mql.wide.matches) ? 'horizontal' : 'vertical'), locked);
       // Render the main menu as a nested, collapsible accordion.
-      $toolbar.find('.administration.tray .toolbar-menu > .menu').toolbarMenu();
+      $toolbar.find('.tray-administration .toolbar-menu > .menu').toolbarMenu();
       // Call setHeight on screen resize. Wrap it in debounce to prevent
       // setHeight from being called too frequently.
       var setHeight = Drupal.debounce(Drupal.toolbar.setHeight, 200);
diff --git a/core/modules/toolbar/templates/toolbar.twig b/core/modules/toolbar/templates/toolbar.twig
index c2181df..2e52bd2 100644
--- a/core/modules/toolbar/templates/toolbar.twig
+++ b/core/modules/toolbar/templates/toolbar.twig
@@ -5,16 +5,15 @@
  *
  * Available variables:
  *
- * - tabs: Themed links for the top level tabs.
- * - trays: An array of trays. It contains:
- *   - content: The themed tray content.
- *   - attributes: HTML attributes for the surrounding element. It includes:
- *     - id: The unique id of the tray. This corresponds to the module name
- *       registered the tray.
- *     - class: A list of classes to target the trays for styling.
  * - attributes: HTML attributes for the surrounding element. It includes:
  *   - id: The unique id of the toolbar.
  *   - class: A list of classes to target the toolbar for styling.
+ * - bar: Administration bar meta data and renderable content. It includes:
+ *   - attributes: HTML attributes for the surrounding element. It includes:
+ *     - id: The unique id of the bar.
+ *     - class: A list of classes to target the bar for styling.
+ *   - items: An array of renderable content.
+ * - trays: An array of renderable content.
  *
  * @see template_preprocess()
  * @see template_preprocess_toolbar()
@@ -24,16 +23,11 @@
 #}
 
 <nav id="{{ attributes.id }}" class="{{ attributes.class }}"{{ attributes }}>
-  <!-- Tabs -->
-  {{ tabs }}
 
-  <!-- Trays -->
-  {% for tray in trays %}
-    <div id="{{ tray.attributes.id }}" class="{{ tray.attributes.class }}"{{ tray.attributes }}>
-      <div class="lining clearfix">
-        <h3 class="element-invisible">{{ tray.heading }}</h3>
-        {{ tray.content }}
-      </div>
-    </div>
-  {% endfor %}
+  <div id="{{ bar.attributes.id }}" class="{{ bar.attributes.class }}"{{ bar.attributes }}>
+    {{ bar.items }}
+  </div>
+
+  {{ trays }}
+
 </nav>
diff --git a/core/modules/toolbar/tests/modules/toolbar_test/toolbar_test.module b/core/modules/toolbar/tests/modules/toolbar_test/toolbar_test.module
index cadddc9..63497c5 100644
--- a/core/modules/toolbar/tests/modules/toolbar_test/toolbar_test.module
+++ b/core/modules/toolbar/tests/modules/toolbar_test/toolbar_test.module
@@ -6,34 +6,36 @@
  */
 
 /**
- * Um
+ * Implements hook_toolbar().
  */
 function toolbar_test_toolbar() {
-  $tray_items = array(
-    l('link 1', '<front>'),
-    l('link 2', '<front>'),
-    l('link 3', '<front>'),
-  );
   $items['testing'] = array(
-    'tab' => array(
-      'title' => t('Test tab'),
-      'href' => '',
+    "#theme_wrappers" => array('toolbar_tab_wrapper'),
+    '#theme' => 'toolbar_tray_toggle',
+    '#text' => t('Test tab'),
+    '#path' => '',
+    '#options' => array(
       'html' => FALSE,
       'attributes' => array(
         'title' => t('Test tab'),
       ),
     ),
-    'tray' => array(
+    '#tray' => array(
       '#heading' => t('Test tray'),
+      '#theme_wrappers' => array('toolbar_tray_wrapper'),
       'content' => array(
         '#theme' => 'item_list',
-        '#items' => $tray_items,
+        '#items' => array(
+          l(t('link 1'), '<front>'),
+          l(t('link 2'), '<front>'),
+          l(t('link 3'), '<front>'),
+        ),
         '#attributes' => array(
           'class' => array('menu'),
         ),
       ),
     ),
-    'weight' => 50,
+    '#weight' => 50,
   );
 
   return $items;
diff --git a/core/modules/toolbar/toolbar.module b/core/modules/toolbar/toolbar.module
index e543326..4b4e0d8 100644
--- a/core/modules/toolbar/toolbar.module
+++ b/core/modules/toolbar/toolbar.module
@@ -41,9 +41,18 @@ function toolbar_permission() {
  */
 function toolbar_theme($existing, $type, $theme, $path) {
   $items['toolbar'] = array(
-    'render element' => 'toolbar',
+    'render element' => 'elements',
     'template' => 'toolbar',
   );
+  $items['toolbar_tab_wrapper'] = array(
+    'render element' => 'element',
+  );
+  $items['toolbar_tray_toggle'] = array(
+    'variables' => array('text' => NULL, 'path' => NULL, 'options' => array(), 'toolbar_identifier' => NULL),
+  );
+  $items['toolbar_tray_wrapper'] = array(
+    'render element' => 'element',
+  );
   return $items;
 }
 
@@ -160,30 +169,116 @@ function template_preprocess_toolbar(&$variables) {
     // The 'overlay-displace-top' class pushes the overlay down, so it appears
     // below the toolbar.
     'class' => array('toolbar', 'overlay-displace-top'),
-    'role' => 'navigation'
+    'role' => 'navigation',
   ));
+  // Provide a convenience variable for the bar.
+  $variables['bar'] = array(
+    'items' => array(),
+    'attributes' => new Attribute(
+      array(
+        'id' => 'toolbar-bar',
+        'class' => array('bar', 'overlay-displace-top', 'clearfix'),
+      )
+    ),
+  );
+  // Provide a convenience variable for the trays.
+  $variables['trays'] = array();
+  // Loop through the items. Pull out trays if defined under the #tray key.
+  foreach($variables['elements']['#items'] as $key => $item) {
+    $id = drupal_html_class($key);
+    $variables['bar']['items'][$key] = $item;
+    $variables['bar']['items'][$key]['#toolbar_identifier'] = $id;
+    if (in_array('#tray', array_keys($item)) && isset($item['#tray'])) {
+      $variables['trays'][$key] = $item['#tray'];
+      $variables['trays'][$key]['#toolbar_identifier'] = $id;
+    }
+  }
+  unset($variables['elements']['#items']);
+}
 
-  // Provide a convenience variable for the themed tabs.
-  $variables['toolbar']['tabs']['#attributes']['class'][] = 'overlay-displace-top';
-  $variables['tabs'] = $variables['toolbar']['tabs'];
-
-  // Place the tabs in a top-level variable so that attribute information
-  // can be associated with each one.
-  foreach ($variables['toolbar']['trays'] as $key => $tray) {
-    // Create tray heading text. Prefer the provided heading text if it exists.
-    $heading = isset($tray['#heading']) ? $tray['#heading'] : t('@group actions', array('@group' => $key));
-
-    $variables['trays'][$key] = array(
-      'heading' => $heading,
-      'content' => $variables['toolbar']['trays'][$key],
-      'attributes' => new Attribute(array(
-          'id' => 'toolbar-tray-' . $key,
-          'class' => array('tray', 'overlay-displace-top', $key),
-          'data-toolbar-tray' => $key,
-          'aria-owned-by' => 'toolbar-tab-' . $key,
-        )
-      ),
-    );
+/**
+ * Returns HTML for wrapping a toolbar tab.
+ *
+ * Toolbar tabs have a common styling and placement with the toolbar's bar area.
+ *
+ * @param array $variables
+ *   An associative array containing:
+ *   - element: An associative array containing the properties and children of
+ *     the tray. Properties used: #children and #attributes.
+ */
+function theme_toolbar_tab_wrapper(&$variables) {
+  $element = $variables['element'];
+  if (!empty($element['#children'])) {
+    $element['#attributes']['class'][] = 'tab';
+    return '<div' . new Attribute($element['#attributes']) . '>' . $element['#children'] . '</div>';
+  }
+}
+
+/**
+ * Implements template_preprocess_HOOK().
+ */
+function template_preprocess_toolbar_tray_toggle(&$variables) {
+  // Provide an id, a data attribute linking each tab to the corresponding
+  // tray and ARIA information.
+  if (!isset($variables['options']['attributes'])) {
+    $variables['options']['attributes'] = array();
+  }
+  $variables['options']['attributes']['id'] = 'toolbar-tab-' . $variables['toolbar_identifier'];
+  $variables['options']['attributes']['data-toolbar-tray'] = $variables['toolbar_identifier'];
+  $variables['options']['attributes']['aria-owns'] = 'toolbar-tray-' . $variables['toolbar_identifier'];
+  $variables['options']['attributes']['role'] = 'button';
+  $variables['options']['attributes']['class'][] = 'trigger';
+  $variables['options']['attributes']['aria-pressed'] = 'false';
+}
+
+/**
+ * Returns HTML for toolbar tab that opens and closes a tray.
+ *
+ * Use in combination with theme_toolbar_tray_wrapper to create an
+ * association between a link tag in the administration bar and a tray.
+ *
+ * @param $variables
+ *   An associative array containing the keys 'text', 'path', and 'options'. See
+ *   the l() function for information about these variables.
+ *
+ * @see l()
+ */
+function theme_toolbar_tray_toggle(&$variables) {
+  return l($variables['text'], $variables['path'], $variables['options']);
+}
+
+/**
+ * Returns HTML for wrapping a toolbar tray.
+ *
+ * Used in combination with theme_toolbar_tray_toggle() to create an
+ * association between a link tag in the administration bar and a tray.
+ *
+ * @param array $variables
+ *   An associative array containing:
+ *   - element: An associative array containing the properties and children of
+ *     the tray. Properties used: #children, #toolbar_identifier, #attributes
+ *     and #heading.
+ */
+function theme_toolbar_tray_wrapper(&$variables) {
+  $element = $variables['element'];
+  if (!empty($element['#children'])) {
+    if (!empty($element['#toolbar_identifier'])) {
+      $group = $element['#toolbar_identifier'];
+      if (!isset($element['#attributes'])) {
+        $element['#attributes'] = array();
+      }
+      $element['#attributes'] += array(
+        'id' => 'toolbar-tray-' . $group,
+        'data-toolbar-tray' => $group,
+        'aria-owned-by' => 'toolbar-tab-' . $group,
+      );
+      $element['#attributes']['class'][] = 'tray';
+      $element['#attributes']['class'][] = 'tray-' . $group;
+      $element['#attributes']['class'][] = 'overlay-displace-top';
+    }
+    // Print a heading in the tray if one exists.
+    $heading = (isset($element['#heading'])) ? '<h2 class="element-invisible">' . $element['#heading'] . '</h2>' : '';
+    return '<div' . new Attribute($element['#attributes']) . '><div class="lining clearfix">' . $heading . $variables['element']['#children'] . '</div></div>';
   }
 }
 
@@ -211,16 +306,18 @@ function toolbar_toolbar() {
 
   // The 'Home' tab is a simple link, with no corresponding tray.
   $items['home'] = array(
-    'tab' => array(
-      'title' => t('Home'),
-      'href' => '<front>',
+    '#theme_wrappers' => array('toolbar_tab_wrapper'),
+    '#theme' => 'link',
+    '#text' => t('Home'),
+    '#path' => '<front>',
+    '#options' => array(
       'html' => FALSE,
       'attributes' => array(
         'title' => t('Home page'),
         'class' => array('icon', 'icon-home'),
       ),
     ),
-    'weight' => -20,
+    '#weight' => -20,
   );
 
   // Retrieve the administration menu from the database.
@@ -230,6 +327,8 @@ function toolbar_toolbar() {
   toolbar_menu_navigation_links($tree);
 
   $menu = array(
+    '#theme_wrappers' => array('toolbar_tray_wrapper'),
+    '#heading' => t('Administration menu'),
     'toolbar_administration' => array(
       '#type' => 'container',
       '#attributes' => array(
@@ -237,7 +336,6 @@ function toolbar_toolbar() {
       ),
       'administration_menu' => menu_tree_output($tree),
     ),
-    '#heading' => t('Administration menu'),
   );
 
   // To conserve bandwidth, we only include the top-level links in the HTML.
@@ -247,18 +345,22 @@ function toolbar_toolbar() {
   // @see toolbar_subtrees_jsonp()
   $menu['toolbar_administration']['#attached']['js'][url('toolbar/subtrees/' . _toolbar_get_subtree_hash())] = array('type' => 'external');
 
+  // The administration element has a link that is themed to correspond to
+  // a toolbar tray. The tray contains the full administrative menu of the site.
   $items['administration'] = array(
-    'tab' => array(
-      'title' => t('Menu'),
-      'href' => 'admin',
+    "#theme_wrappers" => array('toolbar_tab_wrapper'),
+    '#theme' => 'toolbar_tray_toggle',
+    '#text' => t('Menu'),
+    '#path' => 'admin',
+    '#options' => array(
       'html' => FALSE,
       'attributes' => array(
         'title' => t('Admin menu'),
         'class' => array('icon', 'icon-menu'),
       ),
     ),
-    'tray' => $menu,
-    'weight' => -15,
+    '#tray' => $menu,
+    '#weight' => -15,
   );
 
   return $items;
@@ -269,7 +371,7 @@ function toolbar_toolbar() {
  *
  * @return
  *   A renderable arrray, with two children:
- *   - 'tabs': an array of links, rendered by theme('links').
+ *   - 'tabs': an array of render elements, with default type 'link'.
  *   - 'trays': an array of render elements displayed when the corresponding tab
  *     is activated.
  */
@@ -278,74 +380,30 @@ function toolbar_view() {
   $build = array('#theme' => 'toolbar');
   $build['#attached']['library'][] = array('toolbar', 'toolbar');
 
-  // Get the configured breakpoint to switch from vertical to horizontal
+  // Get the configured breakpoints to switch from vertical to horizontal
   // toolbar presentation.
   $breakpoints = entity_load('breakpoint_group', 'module.toolbar.toolbar');
   if (!empty($breakpoints)) {
     $media_queries = array();
     $media_queries['toolbar']['breakpoints'] = array_map(
-      function($object) {return $object->mediaQuery;},
-      $breakpoints->breakpoints);
+      function ($object) {
+        return $object->mediaQuery;
+      },
+      $breakpoints->breakpoints
+    );
 
     $build['#attached']['js'][] = array(
       'data' => $media_queries,
       'type' => 'setting',
     );
-    // // Load the breakpoints for toolbar.
-    foreach ($breakpoints->breakpoints as $key => $breakpoint) {
-      $build['#attached']['js'][0]['data']['toolbar']['breakpoints'][$key] = $breakpoint->mediaQuery;
-    }
   }
 
-  // Get toolbar items from all modules that implement hook_toolbar() or
-  // hook_toolbar_alter().
-  $toolbar_groups = module_invoke_all('toolbar');
-  drupal_alter('toolbar', $toolbar_groups);
-  uasort($toolbar_groups, 'drupal_sort_weight');
-
-  // Build the tabs and trays from the toolbar groups.
-  $build['trays'] = array();
-  $build['tabs'] = array(
-    '#theme' => 'links',
-    '#links' => array(),
-    '#attributes' => array(
-      'class' => array('bar', 'clearfix'),
-    ),
-    '#heading' => array('text' => t('Toolbar'), 'level' => 'h2', 'class' => 'element-invisible'),
-  );
-  $tab_defaults = array(
-    'title' => '',
-    'href' => '',
-    'html' => FALSE,
-    'attributes' => new Attribute(),
-  );
+  // Get toolbar items from all modules that implement hook_toolbar().
+  $toolbar_items = module_invoke_all('toolbar');
+  // Allow for altering of hook_toolbar().
+  drupal_alter('toolbar', $toolbar_items);
 
-  foreach ($toolbar_groups as $group => $items) {
-    if ($tab = $items['tab']) {
-      // Merge in the defaults.
-      $tab += $tab_defaults;
-    }
-    // Register a tray if one is associated with this tab.
-    if (!empty($items['tray'])) {
-      // Provide an id, a data attribute linking each tab to the corresponding
-      // tray and aria information.
-      $tab['attributes']['id'] = 'toolbar-tab-' . $group;
-      $tab['attributes']['data-toolbar-tray'] = $group;
-      $tab['attributes']['aria-owns'] = 'toolbar-tray-' . $group;
-      $tab['attributes']['role'] = 'button';
-      $tab['attributes']['aria-pressed'] = 'false';
-
-      if (array_key_exists($group, $build['trays'])) {
-        array_merge($build['trays'][$group], $items['tray']);
-      }
-      else {
-        // Assign the tray to the build.
-        $build['trays'][$group] = $items['tray'];
-      }
-    }
-    // Assign the tabs to the build.
-    $build['tabs']['#links'][$group] = $tab;
-  }
+  $build['#items'] = $toolbar_items;
 
   return $build;
 }
diff --git a/core/modules/user/user.module b/core/modules/user/user.module
index 7dabb6d..e1266a8 100644
--- a/core/modules/user/user.module
+++ b/core/modules/user/user.module
@@ -3027,29 +3027,30 @@ function user_toolbar() {
     );
   }
 
-  $user_tray = array(
-    '#heading' => t('User account actions'),
-    'content' => array(
-      '#theme' => 'links__toolbar_user',
-      '#links' => $links,
-      '#attributes' => array(
-        'class' => array('menu',),
-      ),
-    ),
-  );
-
   $items['user'] = array(
-    'tab' => array(
-      'title' => user_format_name($user),
-      'href' => 'user',
+    "#theme_wrappers" => array('toolbar_tab_wrapper'),
+    '#theme' => 'toolbar_tray_toggle',
+    '#text' => user_format_name($user),
+    '#path' => 'user',
+    '#options' => array(
       'html' => FALSE,
       'attributes' => array(
         'title' => t('My account'),
         'class' => array('icon', 'icon-user'),
       ),
     ),
-    'tray' => $user_tray,
-    'weight' => 100,
+    '#tray' => array(
+      '#theme_wrappers' => array('toolbar_tray_wrapper'),
+      '#heading' => t('User account actions'),
+      'user_links' => array(
+        '#theme' => 'links__toolbar_user',
+        '#links' => $links,
+        '#attributes' => array(
+          'class' => array('menu',),
+        ),
+      ),
+    ),
+    '#weight' => 100,
   );
 
   return $items;
-- 
1.7.10.4

