? custom_breadcrumbs-754494-3.patch
? custom_breadcrumbs_identifiers
Index: custom_breadcrumbs.module
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/custom_breadcrumbs/custom_breadcrumbs.module,v
retrieving revision 1.6.2.5.2.35
diff -u -r1.6.2.5.2.35 custom_breadcrumbs.module
--- custom_breadcrumbs.module	21 Mar 2010 01:08:57 -0000	1.6.2.5.2.35
+++ custom_breadcrumbs.module	5 Apr 2010 15:09:14 -0000
@@ -267,49 +267,10 @@
  */
 function custom_breadcrumbs_set_breadcrumb($breadcrumb, $objs = array()) {
   if ($breadcrumb && !custom_breadcrumbs_exclude_path()) {
-    $location = array();
-    $trail = custom_breadcrumbs_home_crumb();
-    if (!empty($trail)) {
-      $title = variable_get('custom_breadcrumb_home', t('Home'));
-      $location = array('title' => variable_get('custom_breadcrumb_home', t('Home')), 'href' => '<front>', 'localized_options' => array());
-    }
-    if (variable_get('custom_breadcrumbs_use_php_in_titles', FALSE)) {
-      $titles = extract_php($breadcrumb->titles, $objs);
-      // Titles and paths arrays can also be provided as elements of an associative array.
-      if (isset($titles['titles']) && is_array($titles['titles']) && isset($titles['paths']) && is_array($titles['paths'])) {
-        $paths = $titles['paths'];
-        $titles = $titles['titles'];
-      }
-      else {
-        $paths = extract_php($breadcrumb->paths, $objs);
-      }
-    }
-    if (!isset($titles) || is_null($titles)) {
-      $titles = preg_split("/[\n]+/", $breadcrumb->titles);
-    }
-    if (!isset($paths) || is_null($paths)) {
-      $paths = preg_split("/[\n]+/", $breadcrumb->paths);
-    }
-
-    if (module_exists('token')) {
-      // Do token replacement.
-      $types = custom_breadcrumbs_token_types($objs);
-      $titles = token_replace_multiple($titles, $types);
-      $paths = token_replace_multiple($paths, $types);
-    }
-  
-    for ($i = 0; $i < count($titles); $i++) {
-      $title = trim($titles[$i]);
-      if (($title != '') && ($title != '<none>')) {
-        // Create breadcrumb only if there is a title.
-        // Include optional html attributes.
-        $options = _custom_breadcrumbs_identifiers_option($i+1, $breadcrumb->bid);
-        $trail[] = _custom_breadcrumbs_create_crumb($title, trim($paths[$i]), $options);
-        $location[] = array('title' => $title, 'href' => drupal_get_normal_path(trim($paths[$i])));
-      }
-    }
+    $locations = array();
+    $trail = _custom_breadcrumbs_get_breadcrumb($breadcrumb, $objs, $locations);
     if (variable_get('custom_breadcrumbs_force_active_trail', FALSE)) {
-      menu_set_active_trail($location);
+      menu_set_active_trail($locations);
     }
     drupal_set_breadcrumb($trail);
     // Optionally save the unique breadcrumb id of the last set breadcrumb.
@@ -318,6 +279,112 @@
 }
 
 /**
+ * Helper function to get custom breadcrumb.
+ *
+ * This function is used to retrieve the breadcrumb trail before actually setting it.
+ * @see custom_breadcrumbs_get_breadcrumb().
+ *
+ * @param $breadcrumb
+ *   The breadcrumb object
+ * @param $objs
+ *   A node object (if available) for building token substituions.
+ * @param $locations
+ *   Locations array to be able to set active menu trail - will be passed by
+ *   reference.
+ *
+ * @return
+ *   array of html crumbs
+ */
+function _custom_breadcrumbs_get_breadcrumb($breadcrumb, $objs, &$locations) {
+  // Assure locations is an array.
+  if (!is_array($locations)) {
+    $locations = array();
+  }
+  // avoid invalid nesting
+  elseif (isset($locations['title']) || isset($locations['href'])) {
+    $locations = array($locations);
+  }
+
+  $trail = custom_breadcrumbs_home_crumb();
+  if (!empty($trail)) {
+    $title = variable_get('custom_breadcrumb_home', t('Home'));
+    $locations[] = array('title' => variable_get('custom_breadcrumb_home', t('Home')), 'href' => '<front>', 'localized_options' => array());
+  }
+ 
+  if (variable_get('custom_breadcrumbs_use_php_in_titles', FALSE)) {
+    $titles = extract_php($breadcrumb->titles, $objs);
+    // Titles and paths arrays can also be provided as elements of an associative array.
+    if (isset($titles['titles']) && is_array($titles['titles']) && isset($titles['paths']) && is_array($titles['paths'])) {
+      $paths = $titles['paths'];
+      $titles = $titles['titles'];
+    }
+    else {
+      $paths = extract_php($breadcrumb->paths, $objs);
+    }
+  }
+  if (!isset($titles) || is_null($titles)) {
+    $titles = preg_split("/[\n]+/", $breadcrumb->titles);
+  }
+  if (!isset($paths) || is_null($paths)) {
+    $paths = preg_split("/[\n]+/", $breadcrumb->paths);
+  }
+
+  // Token replacement for titles and paths
+  if (module_exists('token')) {
+    // Do token replacement.
+    $types = custom_breadcrumbs_token_types($objs);
+    $titles = token_replace_multiple($titles, $types);
+    $paths = token_replace_multiple($paths, $types);
+  }
+  $items = _custom_breadcrumbs_get_trail_items($breadcrumb, $titles, $paths);
+
+  // Use the returned items to set the trail.
+  foreach ($items as $item) {
+    if ($item['crumb']) {
+      $trail[] = $item['crumb'];
+    }
+    if ($set_active_trail) {  // TODO: only do this if $item['href'] is set?
+      $locations[] = array('title' => $item['title'], 'href' => drupal_get_normal_path(trim($item['href'])));
+    }
+  }
+
+  return $trail;
+}
+
+/**
+ * Builds the trail items for a given breadcrumb specification.
+ *
+ * @param $breadcrumb
+ *   The breadcrumb object
+ * @param $titles
+ *   array of titles (after token replacement)
+ * @param $paths
+ *   array of paths (after token replacement), may contain special identifiers
+ *
+ * @return
+ *   An associative array of trail items with keys
+ *   'title' - the title of the item
+ *   'href' -  the path of the item used to set the active trail
+ *   'crumb'- the html crumb for use in the breadcrumb
+ */
+function _custom_breadcrumbs_get_trail_items($breadcrumb, $titles, $paths) {
+  $trail_items = array();
+
+  for ($i = 0; $i < count($titles); $i++) {
+    $title = trim($titles[$i]);
+
+    if (($title != '') && ($title != '<none>')) {
+      // Create a breadcrumb only if there is a title.
+      // Include optional html attributes.
+      $options = _custom_breadcrumbs_identifiers_option($i+1, $breadcrumb->bid);
+      $crumb_items = _custom_breadcrumbs_create_crumb_items($title, trim($paths[$i]), $options);
+      $trail_items = array_merge($trail_items, $crumb_items);
+    }
+  }
+  return $trail_items;
+}
+
+/**
  *  Set or get the unique breadcrumb id.
  *
  *  @param $type
@@ -410,8 +477,8 @@
  *   Any additional submodule function to call after breadcrumb has been deleted.
  */
 function _custom_breadcrumbs_delete_breadcrumb($module, $key, $bid) {
-  $info = module_invoke($module, 'cb_breadcrumb_info'); 
-  if (isset($info[$key]['table'])) {  
+  $info = module_invoke($module, 'cb_breadcrumb_info');
+  if (isset($info[$key]['table'])) {
     db_query('DELETE FROM {'. $info[$key]['table'] .'} WHERE bid = %d', $bid);
   }
 }
@@ -436,19 +503,24 @@
 }
 
 /**
- * Private function for custom breadcrumb to create a crumb item.
+ * Creates one ore more crumb items out of a custom bread crumb definition line.
  *
  * @param $title
- *   The human readable title to be rendered by the browser.
+ *  title line string of custom breadcrumb definition, after token replacement
  * @param $original_path
- *   The desired URI and/or special identifier.
- * @param $attr
- *   An associative array containing the HTML attributes to apply to the anchor tag.
- *
- * @return
- *   An HTML string containing a link to the original path.
- */
-function _custom_breadcrumbs_create_crumb($title, $original_path, $attr = array()) {
+ *  path line string of custom breadcrumb definition, after token replacment,
+ *  can contain a special identifier
+ * @param $attributes
+ *  additional attributes for the breadcrumb item
+ * @return
+ *   array of one or multiple crumb items
+ *   - in most cases , especially without an identifier it is only an array of
+ *     one item
+ */
+function _custom_breadcrumbs_create_crumb_items($title, $original_path, $attributes = array()) {
+  // The list to return
+  $crumbs = array();
+
   // Decode title to properly handle special characters.
   $title = decode_entities($title);
   // Collapse double slashes to one.
@@ -456,33 +528,41 @@
   // Removing leading and trailing slashes.
   $original_path = preg_replace('/^\/|\/+$/', '', $original_path);
   list($identifier, $path) = explode("|", $original_path, 2);
-  if (isset($path) && $path !== '') {
-    $options = parse_url($path);
-    $options = array_merge($options, $attr);
-    switch (trim($identifier)) {
-      case '<pathauto>':
-        if (module_exists('pathauto')) {
-          module_load_include('inc', 'pathauto', 'pathauto');
-          $crumb = l($title, pathauto_cleanstring($options['path'], FALSE), $options);
-        }
-        break;
-      default:
-        $crumb = l($title, $options['path'], $options);
+  $identifier = trim($identifier);
+  // Replace identifiers provided by modules implementing hook_cb_identifier_values.
+  $values = array();
+  $obj = array('title' => $title, 'path' => $path, 'attributes' => $attributes);
+  foreach (module_implements('cb_identifier_values') as $module) {
+    $values = module_invoke($module, 'cb_identifier_values', $identifier, $obj);
+    if (!empty($values)) break;
+  }
+  if (!empty($values)) {
+    // Ease return values for callbacks
+    if (!is_array($values)) {
+      $crumbs[] = array('crumb' => $values, 'title' => $title, 'href' => $path);
+    }
+    elseif (isset($values['crumb']) || isset($values['title']) || isset($values['href'])) {
+      $crumbs[] = $values;
+    }
+    else {
+      $crumbs = $values;
     }
   }
   else {
-    // This may be just be a single identifier.
-    $options = parse_url($original_path);
-    $options = array_merge($options, $attr);
-    switch ($identifier) {
-      case '<none>':
-        $crumb = check_plain($title);
-        break;
-      default:
-        $crumb = l($title, $options['path'], $options);
+    // Use original path if no pipe was given.
+    if (!isset($path)) {
+      $path = $original_path;
     }
+    $options = parse_url($path);
+    $options = array_merge($options, $attributes);
+    $crumbs[] = array(
+      'crumb' => l($title, $options['path'], $options),
+      'title' => $title,
+      'href'  => $options['path'],
+    );
   }
-  return $crumb;
+
+  return $crumbs;
 }
 
 /**
@@ -491,20 +571,23 @@
  * @ingroup themeable
  */
 function theme_custom_breadcrumbs_help_identifiers()  {
+
+  $identifiers = module_invoke_all('cb_identifier_list');
   $headers = array(t('Identifier'), t('Behaviour'));
   $rows = array();
-  $row = array();
-  // <none> identifier.
-  $row[] = check_plain('<none>');
-  $row[] = t('This will result in a plain text crumb. This identifier should not be used with the pipe (|) symbol.');
-  $rows[] = $row;
-
-  // <pathauto> identifier.
-  if (module_exists('pathauto')) {
-    $row = array();
-    $row[] = check_plain('<pathauto>');
-    $row[] = t('Cleans the given path using your pathauto replacement rules.');
-    $rows[] = $row;
+
+  if (!empty($identifiers)) {
+    foreach ($identifiers as $id => $description) {
+      $rows[] = array(check_plain($id), $description);
+    }
+  }
+  else {
+    $rows[] = array(
+      array(
+        'data' => t('No special identifiers have been defined. You must <a href="@link">enable the custom breadcrumbs identifiers module</a> or another module that implements hook_cb_identifier_list and hook_cb_identifier_values to enable this feature.', array('@link' => url('admin/build/modules'))),
+        'colspan' => 2,
+      ),
+    );
   }
   return theme('table', $headers, $rows, array('class' => 'description'));
 }
@@ -616,7 +699,7 @@
 
 /**
  * Set the breadcrumb trail to match the menu structure.
- * 
+ *
  * This function uses the same approach as in the menu_breadcrumb module.
  */
 function custom_breadcrumbs_set_menu_breadcrumb() {
