From 3f9239e840374d79184f471ec94a4fe61100328d Wed, 1 Jun 2011 22:07:23 +0200
From: casey <caseynotane@live.com>
Date: Wed, 1 Jun 2011 22:06:17 +0200
Subject: [PATCH] #31535: Grouping of rows in theme_table()

diff --git a/includes/common.inc b/includes/common.inc
index 4ec37dc..8abfe9e 100644
--- a/includes/common.inc
+++ b/includes/common.inc
@@ -6434,7 +6434,7 @@
       'variables' => array(),
     ),
     'table' => array(
-      'variables' => array('header' => NULL, 'rows' => NULL, 'attributes' => array(), 'caption' => NULL, 'colgroups' => array(), 'sticky' => TRUE, 'empty' => ''),
+      'variables' => array('header' => NULL, 'rows' => NULL, 'attributes' => array(), 'caption' => NULL, 'colgroups' => array(), 'rowgroup' => array(), 'rowgroups' => array(), 'sticky' => TRUE, 'empty' => ''),
     ),
     'tablesort_indicator' => array(
       'variables' => array('style' => NULL),
diff --git a/includes/theme.inc b/includes/theme.inc
index c211248..0b216b2 100644
--- a/includes/theme.inc
+++ b/includes/theme.inc
@@ -1579,6 +1579,8 @@
  *       )
  *     );
  *     @endcode
+ *     Note that when rowgroups are used rows need to be grouped using an extra
+ *     associative array. See colgroups variable for more info.
  *   - attributes: An array of HTML attributes to apply to the table tag.
  *   - caption: A localized string to use for the <caption> tag.
  *   - colgroups: An array of column groups. Each element of the array can be
@@ -1589,7 +1591,7 @@
  *       include a "data" attribute. To add attributes to COL elements, set the
  *       "data" attribute with an array of columns, each of which is an
  *       associative array of HTML attributes.
- *     Here's an example for $colgroup:
+ *     Here's an example for $colgroups:
  *     @code
  *     $colgroup = array(
  *       // COLGROUP with one COL element.
@@ -1612,6 +1614,43 @@
  *     These optional tags are used to group and set properties on columns
  *     within a table. For example, one may easily group three columns and
  *     apply same background style to all.
+ *   - rowgroup: An array of attributes applied to the TBODY element(s).
+ *     Rowgroup specific attributes set in rowgroups override these attributes.
+ *   - rowgroups: An associative array of row groups. Each element represents a
+ *     rowgroup identifiable by it's key. When this variable is set, rows are
+ *     expected to be grouped by rowgroup using an associative array while their
+ *     keys point to the keys of this variable. Elements of this variable are
+ *     expected to be an array of attributes which will be applied to the TBODY
+ *     element representing that rowgroup.
+ *     Here's an example for $rows combined with $rowgroups:
+ *     @code
+ *     $rowgroups = array(
+ *       'funky' => array( // Attributes for the TBODY element.
+ *         'title' => 'Funky rows',
+ *         'class' => array('funky'),
+ *       ),
+ *       'jazzy' => array( // Attributes for the TBODY element.
+ *         'title' => 'Jazzy rows',
+ *         'class' => array('jazzy'),
+ *       ),
+ *     );
+ *     $rows = array(
+ *       // Rows of the funky rowgroup.
+ *       'funky' => array(
+ *         // Simple row
+ *         array(
+ *           'Cell 1', 'Cell 2', 'Cell 3'
+ *         ),
+ *       ),
+ *       // Rows of the jazzy rowgroup.
+ *       'jazzy' => array(
+ *         // Row with attributes on the row and some of its cells.
+ *         array(
+ *           'data' => array('Cell 1', array('data' => 'Cell 2', 'colspan' => 2)), 'class' => array('funky')
+ *         ),
+ *       ),
+ *     );
+ *     @endcode
  *   - sticky: Use a "sticky" table header.
  *   - empty: The message to display in an extra row if table does not have any
  *     rows.
@@ -1622,6 +1661,8 @@
   $attributes = $variables['attributes'];
   $caption = $variables['caption'];
   $colgroups = $variables['colgroups'];
+  $rowgroup = $variables['rowgroup'];
+  $rowgroups = $variables['rowgroups'];
   $sticky = $variables['sticky'];
   $empty = $variables['empty'];
 
@@ -1705,46 +1746,56 @@
     $ts = array();
   }
 
-  // Format the table rows:
-  if (count($rows)) {
-    $output .= "<tbody>\n";
-    $flip = array('even' => 'odd', 'odd' => 'even');
-    $class = 'even';
-    foreach ($rows as $number => $row) {
-      $attributes = array();
+  if (!count($rowgroups)) {
+    $rowgroups = array();
+    $rows_by_groups = array($rows);
+  }
+  else {
+    $rows_by_groups = $rows;
+  }
 
-      // Check if we're dealing with a simple or complex row
-      if (isset($row['data'])) {
-        foreach ($row as $key => $value) {
-          if ($key == 'data') {
-            $cells = $value;
-          }
-          else {
-            $attributes[$key] = $value;
+  // Format the table rows:
+  $flip = array('even' => 'odd', 'odd' => 'even');
+  foreach ($rows_by_groups as $rowgroup_id => $rows) {
+    if (count($rows)) {
+      $attributes = isset($rowgroups[$rowgroup_id]) ? $rowgroup + $rowgroups[$rowgroup_id] : $rowgroup;
+      $output .= '<tbody' . drupal_attributes($attributes) . ">\n";
+
+      $class = 'even';
+      foreach ($rows as $number => $row) {
+        $attributes = array();
+
+        // Check if we're dealing with a simple or complex row.
+        if (isset($row['data'])) {
+          foreach ($row as $key => $value) {
+            if ($key == 'data') {
+              $cells = $value;
+            }
+            else {
+              $attributes[$key] = $value;
+            }
           }
         }
-      }
-      else {
-        $cells = $row;
-      }
-      if (count($cells)) {
-        // Add odd/even class
-        if (empty($row['no_striping'])) {
+        else {
+          $cells = $row;
+        }
+        if (count($cells)) {
+          // Add odd/even class.
           $class = $flip[$class];
           $attributes['class'][] = $class;
-        }
 
-        // Build row
-        $output .= ' <tr' . drupal_attributes($attributes) . '>';
-        $i = 0;
-        foreach ($cells as $cell) {
-          $cell = tablesort_cell($cell, $header, $ts, $i++);
-          $output .= _theme_table_cell($cell);
+          // Build row.
+          $output .= ' <tr' . drupal_attributes($attributes) . '>';
+          $i = 0;
+          foreach ($cells as $cell) {
+            $cell = tablesort_cell($cell, $header, $ts, $i++);
+            $output .= _theme_table_cell($cell);
+          }
+          $output .= " </tr>\n";
         }
-        $output .= " </tr>\n";
       }
+      $output .= "</tbody>\n";
     }
-    $output .= "</tbody>\n";
   }
 
   $output .= "</table>\n";
diff --git a/modules/user/user.admin.inc b/modules/user/user.admin.inc
index afaddeb..1acbc44 100644
--- a/modules/user/user.admin.inc
+++ b/modules/user/user.admin.inc
@@ -682,10 +682,12 @@
   asort($modules);
 
   foreach ($modules as $module => $display_name) {
+    $form['permission'][$module] = array(
+      '#id' => $module,
+    );
     if ($permissions = module_invoke($module, 'permission')) {
-      $form['permission'][] = array(
+      $form['permission'][$module][] = array(
         '#markup' => $module_info[$module]['name'],
-        '#id' => $module,
       );
       foreach ($permissions as $perm => $perm_item) {
         // Fill in default values for the permission.
@@ -695,7 +697,7 @@
           'warning' => !empty($perm_item['restrict access']) ? t('Warning: Give to trusted roles only; this permission has security implications.') : '',
         );
         $options[$perm] = '';
-        $form['permission'][$perm] = array(
+        $form['permission'][$module][$perm] = array(
           '#type' => 'item',
           '#markup' => $perm_item['title'],
           '#description' => theme('user_permission_description', array('permission_item' => $perm_item, 'hide' => $hide_descriptions)),
@@ -753,32 +755,36 @@
   $form = $variables['form'];
 
   $roles = user_roles();
-  foreach (element_children($form['permission']) as $key) {
-    $row = array();
-    // Module name
-    if (is_numeric($key)) {
-      $row[] = array('data' => drupal_render($form['permission'][$key]), 'class' => array('module'), 'id' => 'module-' . $form['permission'][$key]['#id'], 'colspan' => count($form['role_names']['#value']) + 1);
-    }
-    else {
-      // Permission row.
-      $row[] = array(
-        'data' => drupal_render($form['permission'][$key]),
-        'class' => array('permission'),
-      );
-      foreach (element_children($form['checkboxes']) as $rid) {
-        $form['checkboxes'][$rid][$key]['#title'] = $roles[$rid] . ': ' . $form['permission'][$key]['#markup'];
-        $form['checkboxes'][$rid][$key]['#title_display'] = 'invisible';
-        $row[] = array('data' => drupal_render($form['checkboxes'][$rid][$key]), 'class' => array('checkbox'));
+  foreach (element_children($form['permission']) as $module) {
+    $rowgroups[$module] = array('id' => 'module-' . $form['permission'][$module]['#id']);
+
+    foreach (element_children($form['permission'][$module]) as $key) {
+      $row = array();
+      // Module name
+      if (is_numeric($key)) {
+        $row[] = array('data' => drupal_render($form['permission'][$module][$key]), 'class' => array('module'), 'colspan' => count($form['role_names']['#value']) + 1);
       }
+      // Permission row.
+      else {
+        $row[] = array(
+          'data' => drupal_render($form['permission'][$module][$key]),
+          'class' => array('permission'),
+        );
+        foreach (element_children($form['checkboxes']) as $rid) {
+          $form['checkboxes'][$rid][$key]['#title'] = $roles[$rid] . ': ' . $form['permission'][$module][$key]['#markup'];
+          $form['checkboxes'][$rid][$key]['#title_display'] = 'invisible';
+          $row[] = array('data' => drupal_render($form['checkboxes'][$rid][$key]), 'class' => array('checkbox'));
+        }
+      }
+      $rows[$module][] = $row;
     }
-    $rows[] = $row;
   }
   $header[] = (t('Permission'));
   foreach (element_children($form['role_names']) as $rid) {
     $header[] = array('data' => drupal_render($form['role_names'][$rid]), 'class' => array('checkbox'));
   }
   $output = theme('system_compact_link');
-  $output .= theme('table', array('header' => $header, 'rows' => $rows, 'attributes' => array('id' => 'permissions')));
+  $output .= theme('table', array('header' => $header, 'rows' => $rows, 'rowgroups' => $rowgroups, 'attributes' => array('id' => 'permissions')));
   $output .= drupal_render_children($form);
   return $output;
 }
