From 0d84399d8295710ccb10aa2c9907e23a0ee22572 Mon Sep 17 00:00:00 2001
From: Mariano D'Agostino <dagmar@154086.no-reply.drupal.org>
Date: Tue, 27 Dec 2011 12:05:32 -0300
Subject: [PATCH] Issue #1052312: Pluggable breadcrumbs

---
 includes/plugins.inc                        |   20 ++++
 includes/view.inc                           |   26 +----
 plugins/views_plugin_breadcrumb.inc         |   30 ++++++
 plugins/views_plugin_breadcrumb_custom.inc  |  150 +++++++++++++++++++++++++++
 plugins/views_plugin_breadcrumb_default.inc |   45 ++++++++
 plugins/views_plugin_display_page.inc       |   89 ++++++++++++++++
 views.info                                  |    3 +
 7 files changed, 341 insertions(+), 22 deletions(-)
 create mode 100644 plugins/views_plugin_breadcrumb.inc
 create mode 100644 plugins/views_plugin_breadcrumb_custom.inc
 create mode 100644 plugins/views_plugin_breadcrumb_default.inc

diff --git a/includes/plugins.inc b/includes/plugins.inc
index 9f6459f..1ff379e 100644
--- a/includes/plugins.inc
+++ b/includes/plugins.inc
@@ -246,6 +246,26 @@ function views_views_plugins() {
         'handler' => 'views_plugin_query_default'
       ),
     ),
+    'breadcrumb' => array(
+      'parent' => array(
+        'no ui' => TRUE,
+        'handler' => 'views_plugin_breadcrumb',
+        'parent' => '',
+      ),
+      'default' => array(
+        'title' => t('Default'),
+        'help' => t('Default breadcrumb system.'),
+        'handler' => 'views_plugin_breadcrumb_default',
+        'help topic' => 'breadcrumb-default',
+      ),
+      'custom' => array(
+        'title' => t('Custom'),
+        'help' => t('Customized breadcrumb system.'),
+        'handler' => 'views_plugin_breadcrumb_custom',
+        'uses options' => TRUE,
+        'help topic' => 'breadcrumb-custom',
+      ),
+    ),
     'cache' => array(
       'parent' => array(
         'no ui' => TRUE,
diff --git a/includes/view.inc b/includes/view.inc
index 69ba6cf..0eb7a36 100644
--- a/includes/view.inc
+++ b/includes/view.inc
@@ -1434,30 +1434,12 @@ class view extends views_db_object {
    *   If true, use drupal_set_breadcrumb() to install the breadcrumb.
    */
   function get_breadcrumb($set = FALSE) {
-    // Now that we've built the view, extract the breadcrumb.
-    $base = TRUE;
+    $breadcrumb_plugin = $this->display_handler->get_plugin('breadcrumb');
     $breadcrumb = array();
-
-    if (!empty($this->build_info['breadcrumb'])) {
-      foreach ($this->build_info['breadcrumb'] as $path => $title) {
-        // Check to see if the frontpage is in the breadcrumb trail; if it
-        // is, we'll remove that from the actual breadcrumb later.
-        if ($path == variable_get('site_frontpage', 'node')) {
-          $base = FALSE;
-          $title = t('Home');
-        }
-        if ($title) {
-          $breadcrumb[] = l($title, $path, array('html' => true));
-        }
-      }
-
-      if ($set) {
-        if ($base) {
-          $breadcrumb = array_merge(drupal_get_breadcrumb(), $breadcrumb);
-        }
-        drupal_set_breadcrumb($breadcrumb);
-      }
+    if ($breadcrumb_plugin) {
+      $breadcrumb = $breadcrumb_plugin->get_breadcrumb($set);
     }
+
     return $breadcrumb;
   }
 
diff --git a/plugins/views_plugin_breadcrumb.inc b/plugins/views_plugin_breadcrumb.inc
new file mode 100644
index 0000000..7fcb005
--- /dev/null
+++ b/plugins/views_plugin_breadcrumb.inc
@@ -0,0 +1,30 @@
+<?php
+
+class views_plugin_breadcrumb extends views_plugin {
+
+  /**
+   * Initialize the plugin.
+   *
+   * @param $view
+   *   The view object.
+   * @param $display
+   *   The display handler.
+   */
+  function init(&$view, &$display, $options = array()) {
+    $this->view = &$view;
+    $this->display = &$display;
+
+    $this->unpack_options($this->options, $options);
+  }
+
+  /**
+   * Return a string to display as the clickable title for the
+   * access control.
+   */
+  function summary_title() {
+    return t('Unknown');
+  }
+
+  function get_breadcrumb($set = FALSE) { }
+
+}
diff --git a/plugins/views_plugin_breadcrumb_custom.inc b/plugins/views_plugin_breadcrumb_custom.inc
new file mode 100644
index 0000000..175ecde
--- /dev/null
+++ b/plugins/views_plugin_breadcrumb_custom.inc
@@ -0,0 +1,150 @@
+<?php
+
+/**
+ * Plugin to customize the page breadcrumb.
+ */
+class views_plugin_breadcrumb_custom extends views_plugin_breadcrumb {
+
+  function option_definition() {
+    $options = parent::option_definition();
+    $options['items'] = array('default' => '', 'translatable' => TRUE);
+
+    return $options;
+  }
+
+  function options_form(&$form, &$form_state) {
+
+
+    // Get a list of the available fields and arguments for token replacement.
+    $options = array();
+
+    $count = 0; // This lets us prepare the key as we want it printed.
+    foreach ($this->view->display_handler->get_handlers('argument') as $arg => $handler) {
+      $options[t('Arguments')]['%' . ++$count] = t('@argument title', array('@argument' => $handler->ui_name()));
+      $options[t('Arguments')]['!' . $count] = t('@argument input', array('@argument' => $handler->ui_name()));
+    }
+
+
+    // Default text.
+    $output = '';
+    // We have some options, so make a list.
+    if (!empty($options)) {
+      $output = t('<p>The following tokens are available for this breadcrumb.</p>');
+      foreach (array_keys($options) as $type) {
+        if (!empty($options[$type])) {
+          $items = array();
+          foreach ($options[$type] as $key => $value) {
+            $items[] = $key . ' == ' . $value;
+          }
+          $output .= theme('item_list',
+            array(
+              'items' => $items,
+              'type' => $type
+            ));
+        }
+      }
+    }
+    $output .= '<p>' . t('You can use ***FRONT*** in the path section to link the tilte to the front page.') . '</p>';
+
+    $form['description_markup'] = array(
+      '#prefix' => '<div class="description form-item">',
+      '#suffix' => '</div>',
+      '#markup' => t('Here you can configure the breadcrumb to display when this view page is rendered.') . $output,
+    );
+    $form['items'] = array(
+      '#type' => 'textarea',
+      '#default_value' => !empty($this->options['items']) ? $this->options['items'] : '',
+      '#rows' => 5,
+      '#description' => t('Use a line per item, using the following format: <strong>Title|Url|Description</strong>. Url and Description are optional. If the path is not defined, "Title" will be rendered as normal text without link.'),
+    );
+
+  }
+
+  function summary_title() {
+    return t('Custom');
+  }
+
+  /**
+   * Get the breadcrumb used for this view.
+   *
+   * @param $set
+   *   If true, use drupal_set_breadcrumb() to install the breadcrumb.
+   */
+  function get_breadcrumb($set = FALSE) {
+    // Now that we've built the view, extract the breadcrumb.
+    $breadcrumb = array();
+    $items = explode("\n", $this->options['items']);
+    $items = array_map('trim', $items);
+    $items = array_filter($items, 'strlen');
+
+    $tokens = $this->get_arguments_tokens();
+
+    foreach ($items as $item) {
+      $item_exploded = explode('|', $item);
+      $title = $item_exploded[0];
+      $path = NULL;
+      if (!empty($item_exploded[1])) {
+        $path =  $item_exploded[1];
+      }
+      if (!empty($item_exploded[2])) {
+        $description = $item_exploded[2];
+      }
+      else {
+        $description = '';
+      }
+
+      if (!empty($path)) {
+        if (strpos($path, '!') !== FALSE || strpos($path, '%') !== FALSE) {
+          $path = strtr($path, $tokens);
+        }
+        $path = str_replace('***FRONT***', '<front>', $path);
+      }
+      if (strpos($title, '!') !== FALSE || strpos($title, '%') !== FALSE) {
+        $title = strtr($title, $tokens);
+      }
+
+      if (!empty($title)) {
+        if (!empty($path)) {
+          $breadcrumb[] = l($title, $path, array('html' => TRUE, 'attributes' => array('title' => $description)));
+        }
+        else {
+          $breadcrumb[] = check_plain($title);
+        }
+      }
+    }
+
+    if ($set) {
+      drupal_set_breadcrumb($breadcrumb);
+    }
+
+    return $breadcrumb;
+  }
+
+  /**
+   * Returns to tokens for arguments.
+   *
+   * This function is similar to views_handler_field::get_render_tokens()
+   * but without fields tokens.
+   */
+  function get_arguments_tokens() {
+    $tokens = array();
+    if (!empty($this->view->build_info['substitutions'])) {
+      $tokens = $this->view->build_info['substitutions'];
+    }
+    $count = 0;
+    foreach ($this->view->display_handler->get_handlers('argument') as $arg => $handler) {
+      $token = '%' . ++$count;
+      if (!isset($tokens[$token])) {
+        $tokens[$token] = '';
+      }
+
+      // Use strip tags as there should never be HTML in the path.
+      // However, we need to preserve special characters like " that
+      // were removed by check_plain().
+      $tokens['!' . $count] = isset($this->view->args[$count - 1]) ? strip_tags(html_entity_decode($this->view->args[$count - 1])) : '';
+    }
+
+    return $tokens;
+  }
+
+}
diff --git a/plugins/views_plugin_breadcrumb_default.inc b/plugins/views_plugin_breadcrumb_default.inc
new file mode 100644
index 0000000..c83bbd5
--- /dev/null
+++ b/plugins/views_plugin_breadcrumb_default.inc
@@ -0,0 +1,45 @@
+<?php
+
+/**
+ * Default breadcrumb plugin.
+ */
+class views_plugin_breadcrumb_default extends views_plugin_breadcrumb {
+
+  function summary_title() {
+    return t('Default');
+  }
+
+  /**
+   * Get the breadcrumb used for this view.
+   *
+   * @param $set
+   *   If true, use drupal_set_breadcrumb() to install the breadcrumb.
+   */
+  function get_breadcrumb($set = FALSE) {
+    // Now that we've built the view, extract the breadcrumb.
+    $base = TRUE;
+    $breadcrumb = array();
+
+    if (!empty($this->build_info['breadcrumb'])) {
+      foreach ($this->build_info['breadcrumb'] as $path => $title) {
+        // Check to see if the frontpage is in the breadcrumb trail; if it
+        // is, we'll remove that from the actual breadcrumb later.
+        if ($path == variable_get('site_frontpage', 'node')) {
+          $base = FALSE;
+          $title = t('Home');
+        }
+        if ($title) {
+          $breadcrumb[] = l($title, $path, array('html' => true));
+        }
+      }
+
+      if ($set) {
+        if ($base) {
+          $breadcrumb = array_merge(drupal_get_breadcrumb(), $breadcrumb);
+        }
+        drupal_set_breadcrumb($breadcrumb);
+      }
+    }
+    return $breadcrumb;
+  }
+}
diff --git a/plugins/views_plugin_display_page.inc b/plugins/views_plugin_display_page.inc
index 77f2100..2cfd29d 100644
--- a/plugins/views_plugin_display_page.inc
+++ b/plugins/views_plugin_display_page.inc
@@ -41,6 +41,12 @@ class views_plugin_display_page extends views_plugin_display {
         'name' => array('default' => 'navigation'),
        ),
     );
+    $options['breadcrumb'] = array(
+      'contains' => array(
+        'type' => array('default' => 'default', 'export' => 'export_plugin', 'unpack_translatable' => 'unpack_plugin'),
+        'options' => array('default' => array(), 'export' => FALSE),
+       ),
+    );
 
     return $options;
   }
@@ -286,6 +292,25 @@ class views_plugin_display_page extends views_plugin_display {
       $options['menu']['setting'] = t('Parent menu item');
       $options['menu']['links']['tab_options'] = t('Change settings for the parent menu');
     }
+
+    $breadcrumb_plugin = $this->get_plugin('breadcrumb');
+    if (!$breadcrumb_plugin) {
+      // use default breadcrumb plugin.
+      $breadcrumb_plugin = views_get_plugin('breadcrumb', 'default');
+    }
+
+    $breadcrumb_str = $breadcrumb_plugin->summary_title();
+
+    $options['breadcrumb'] = array(
+      'category' => 'page',
+      'title' => t('Breadcrumb'),
+      'value' => $breadcrumb_str,
+    );
+
+    if (!empty($breadcrumb_plugin->definition['uses options'])) {
+      $options['breadcrumb']['setting'] = t('Breadcrumb');
+      $options['breadcrumb']['links']['breadcrumb_options'] = t('Change settings for breadcrumb');
+    }
   }
 
   /**
@@ -455,6 +480,48 @@ class views_plugin_display_page extends views_plugin_display {
           '#dependency' => array('radio:tab_options[type]' => array('tab')),
         );
         break;
+      case 'breadcrumb':
+        $form['#title'] .= t('Breadcrumb');
+        $form['breadcrumb'] = array(
+          '#prefix' => '<div class="clear-block">',
+          '#suffix' => '</div>',
+          '#tree' => TRUE,
+        );
+
+        $breadcrumb = $this->get_option('breadcrumb');
+        $form['breadcrumb']['type'] =  array(
+          '#type' => 'radios',
+          '#options' => views_fetch_plugin_names('breadcrumb'),
+          '#default_value' => $breadcrumb['type'],
+        );
+
+        $breadcrumb_plugin = views_fetch_plugin_data('breadcrumb', $breadcrumb['type']);
+        if (!empty($breadcrumb_plugin['uses options'])) {
+          $form['markup'] = array(
+            '#prefix' => '<div class="form-item description">',
+            '#suffix' => '</div>',
+            '#value' => t('You may also adjust the !settings for the currently selected breadcrumb mechanism by clicking on the icon.', array('!settings' => $this->option_link(t('settings'), 'breadcrumb_options'))),
+          );
+        }
+        break;
+      case 'breadcrumb_options':
+        $breadcrumb = $this->get_option('breadcrumb');
+        $plugin = $this->get_plugin('breadcrumb');
+        $form['#title'] .= t('Breadcrumb options');
+        if ($plugin) {
+          $form['#help_topic'] = $plugin->definition['help topic'];
+          $form['#help_module'] = $plugin->definition['module'];
+
+          $form['breadcrumb_options'] = array(
+            '#tree' => TRUE,
+          );
+          $form['breadcrumb_options']['type'] = array(
+            '#type' => 'value',
+            '#value' => $breadcrumb['type'],
+          );
+          $plugin->options_form($form['breadcrumb_options'], $form_state);
+        }
+        break;
     }
   }
 
@@ -513,6 +580,28 @@ class views_plugin_display_page extends views_plugin_display {
       case 'tab_options':
         $this->set_option('tab_options', $form_state['values']['tab_options']);
         break;
+      case 'breadcrumb':
+        $breadcrumb = $this->get_option('breadcrumb');
+        if ($breadcrumb['type'] != $form_state['values']['breadcrumb']['type']) {
+          $plugin = views_get_plugin('breadcrumb', $form_state['values']['breadcrumb']['type']);
+          if ($plugin) {
+            $breadcrumb = array('type' => $form_state['values']['breadcrumb']['type']);
+            $this->set_option('breadcrumb', $breadcrumb);
+            if (!empty($plugin->definition['uses options'])) {
+              views_ui_add_form_to_stack('display', $this->view, $this->display->id, array('breadcrumb_options'));
+            }
+          }
+        }
+        break;
+      case 'breadcrumb_options':
+        $plugin = $this->get_plugin('breadcrumb');
+        if ($plugin) {
+          $breadcrumb = $this->get_option('breadcrumb');
+          $plugin->options_submit($form['breadcrumb_options'], $form_state);
+          $breadcrumb['options'] = $form_state['values'][$form_state['section']];
+          $this->set_option('breadcrumb', $breadcrumb);
+        }
+        break;
     }
   }
 
diff --git a/views.info b/views.info
index 2559e15..8f1065e 100644
--- a/views.info
+++ b/views.info
@@ -204,6 +204,9 @@ files[] = plugins/views_plugin_argument_default_raw.inc
 files[] = plugins/views_plugin_argument_validate.inc
 files[] = plugins/views_plugin_argument_validate_numeric.inc
 files[] = plugins/views_plugin_argument_validate_php.inc
+files[] = plugins/views_plugin_breadcrumb.inc
+files[] = plugins/views_plugin_breadcrumb_default.inc
+files[] = plugins/views_plugin_breadcrumb_custom.inc
 files[] = plugins/views_plugin_cache.inc
 files[] = plugins/views_plugin_cache_none.inc
 files[] = plugins/views_plugin_cache_time.inc
-- 
1.7.1

