diff --git css/views-admin.css css/views-admin.css index b0aeb1e..4a94cba 100644 --- css/views-admin.css +++ css/views-admin.css @@ -404,6 +404,10 @@ html.js #views-ajax-pad { padding-left: 30px; } +#views-ajax-pad .breadcrumb-custom-settings input { + width: 95%; +} + /* * Add, Rearrange and Configure buttons using sprites */ diff --git includes/admin.inc includes/admin.inc index 850cb35..a3f1887 100644 --- includes/admin.inc +++ includes/admin.inc @@ -4087,3 +4087,30 @@ function theme_views_ui_style_plugin_table($form) { return $output; } +/** + * Theme the form for the table style plugin + */ +function theme_views_ui_breadcrumb_plugin_custom($form) { + $output = drupal_render($form['description_markup']); + + $header = array( + t('Path'), + t('Title'), + t('Link Title'), + ); + $rows = array(); + foreach (element_children($form['items']['path']) as $path_id) { + $row = array(); + $title_id = str_replace('path_', 'title_', $path_id); + $link_title_id = str_replace('path_', 'link_title_', $path_id); + $row[] = drupal_render($form['items']['path'][$path_id]); + $row[] = drupal_render($form['items']['title'][$title_id]); + $row[] = drupal_render($form['items']['link_title'][$link_title_id]); + $rows[] = $row; + } + + $output .= theme('table', $header, $rows, array('class' => 'breadcrumb-custom-settings')); + $output .= drupal_render($form); + return $output; +} + diff --git includes/plugins.inc includes/plugins.inc index 4a80885..69f606e 100644 --- includes/plugins.inc +++ includes/plugins.inc @@ -274,6 +274,26 @@ function views_views_plugins() { 'help topic' => 'cache-time', ), ), + '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-default', + ), + ), 'exposed_form' => array( 'parent' => array( 'no ui' => TRUE, diff --git includes/view.inc includes/view.inc index 2b38dab..3f551a6 100644 --- includes/view.inc +++ includes/view.inc @@ -71,10 +71,7 @@ class view extends views_db_object { var $style_plugin; /** - * The current - - /** - * Constructor + * The current constructor */ function __construct() { parent::init(); @@ -1250,30 +1247,13 @@ 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 = array(); + $breadcrumb_plugin = $this->display_handler->get_plugin('breadcrumb'); - 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); - } + $breadcrumb = array(); + if ($breadcrumb_plugin) { + $breadcrumb = $breadcrumb_plugin->get_breadcrumb($set); } + return $breadcrumb; } diff --git plugins/views_plugin_breadcrumb.inc plugins/views_plugin_breadcrumb.inc new file mode 100644 index 0000000..7fcb005 --- /dev/null +++ plugins/views_plugin_breadcrumb.inc @@ -0,0 +1,30 @@ +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 plugins/views_plugin_breadcrumb_custom.inc plugins/views_plugin_breadcrumb_custom.inc new file mode 100644 index 0000000..81ab5f3 --- /dev/null +++ plugins/views_plugin_breadcrumb_custom.inc @@ -0,0 +1,192 @@ + array( + 'title' => array(), + 'path' => array(), + 'link_title' => array(), + ), + 'unpack_translatable' => 'unpack_breadcrumbs_translatable', + ); + return $options; + } + + function options_form(&$form, &$form_state) { + $custom_breadcrumb = $this->options['items']; + + // Provide at least five items to configure the breadcrumb + $max_count = max(count(array_filter($custom_breadcrumb['title'])), 3) + 2; + + for ($breadcrumb_item = 0; $breadcrumb_item < $max_count; $breadcrumb_item++) { + $form['items']['path']['path_' . $breadcrumb_item] = array( + '#type' => 'textfield', + '#default_value' => !empty($custom_breadcrumb['path']['path_' . $breadcrumb_item]) ? $custom_breadcrumb['path']['path_' . $breadcrumb_item] : '', + '#size' => 40, + ); + + $form['items']['title']['title_' . $breadcrumb_item] = array( + '#type' => 'textfield', + '#default_value' => !empty($custom_breadcrumb['title']['title_' . $breadcrumb_item]) ? $custom_breadcrumb['title']['title_' . $breadcrumb_item] : '', + ); + + $form['items']['link_title']['link_title_' . $breadcrumb_item] = array( + '#type' => 'textfield', + '#default_value' => !empty($custom_breadcrumb['link_title']['link_title_' . $breadcrumb_item]) ? $custom_breadcrumb['link_title']['link_title_' . $breadcrumb_item] : '', + ); + } + + $count = 0; + 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. + // We have some options, so make a list. + $output = '

' . t('The following tokens are available for this breadcrumb.') . '

'; + if (!empty($options)) { + 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', $items, $type); + } + } + } + $global_tokens = array( + 'front' => t('You can use <front> in the path section to link the tilte to the front page.'), + ); + $output .= theme('item_list', $global_tokens, t('Global tokens')); + + $form['description_markup'] = array( + '#prefix' => '
', + '#suffix' => '
', + '#value' => t('Here you can configure the breadcrumb to display when this view page is rendered. If the path is not defined, "title" will be rendered as normal text without link.') . $output, + ); + $form['#theme'] = 'views_ui_breadcrumb_plugin_custom'; + } + + 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(); + + $custom_breadcrumb = $this->options['items']; + $tokens = $this->get_arguments_tokens(); + + foreach (array_filter($custom_breadcrumb['title']) as $id => $title) { + $path_id = str_replace('title_', 'path_', $id); + $link_title_id = str_replace('title_', 'link_title_', $id); + $path = $custom_breadcrumb['path'][$path_id]; + $link_title = $custom_breadcrumb['link_title'][$link_title_id]; + + if (!empty($path)) { + if (strpos($path, '!') !== FALSE || strpos($path, '%') !== FALSE) { + $path = strtr($path, $tokens); + } + } + if (strpos($title, '!') !== FALSE || strpos($title, '%') !== FALSE) { + $title = strtr($title, $tokens); + } + if (strpos($link_title, '!') !== FALSE || strpos($link_title, '%') !== FALSE) { + $link_title = strtr($link_title, $tokens); + } + + if (!empty($title)) { + if (!empty($path)) { + if (!empty($link_title)) { + $breadcrumb[] = l($title, $path, array('html' => TRUE, 'attributes' => array('title' => check_plain($link_title)))); + } + else { + $breadcrumb[] = l($title, $path, array('html' => TRUE)); + } + } + 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; + } + + function unpack_options(&$storage, $options, $definition = NULL, $all = TRUE, $check = TRUE, $localization_keys = array()) { + if ($check && !is_array($options)) { + return; + } + // Provide translatables for dynamic items + foreach ($options as $key => $value) { + if (!empty($value) && (strpos($key, 'title_') !== FALSE)) { + $definition[$key]['translatable'] = TRUE; + } + } + // Then continue unpacking as usual + parent::unpack_options($storage, $options, $definition, $all, $check, $localization_keys); + } + + /** + * Custom implementation of unpack_translatable + */ + function unpack_breadcrumbs_translatable(&$translatable, $storage, $option, $definition, $parents, $keys = array()) { + $options = $storage['items']['title']; + $translation_keys = array_merge($keys, array($key)); + foreach ($options as $key => $value) { + $translatable[] = array( + 'value' => $value, + 'keys' => $translation_keys, + 'format' => NULL, + ); + } + return $translatable; + } + +} diff --git plugins/views_plugin_breadcrumb_default.inc plugins/views_plugin_breadcrumb_default.inc new file mode 100644 index 0000000..74978b4 --- /dev/null +++ plugins/views_plugin_breadcrumb_default.inc @@ -0,0 +1,45 @@ +view->build_info['breadcrumb'])) { + foreach ($this->view->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 plugins/views_plugin_display_page.inc plugins/views_plugin_display_page.inc index adac656..e5bf46a 100644 --- plugins/views_plugin_display_page.inc +++ plugins/views_plugin_display_page.inc @@ -40,7 +40,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; } @@ -278,6 +283,24 @@ class views_plugin_display_page extends views_plugin_display { if ($menu['type'] == 'default tab') { $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']['links']['breadcrumb_options'] = t('Change settings for breadcrumb'); + } } /** @@ -295,9 +318,9 @@ class views_plugin_display_page extends views_plugin_display { '#type' => 'textfield', '#description' => t('This view will be displayed by visiting this path on your site. You may use "%" in your URL to represent values that will be used for arguments: For example, "node/%/feed".'), '#default_value' => $this->get_option('path'), - '#field_prefix' => '' . url(NULL, array('absolute' => TRUE)) . (variable_get('clean_url', 0) ? '' : '?q='), - '#field_suffix' => '‎', - '#attributes' => array('dir'=>'ltr'), + '#field_prefix' => '' . url(NULL, array('absolute' => TRUE)) . (variable_get('clean_url', 0) ? '' : '?q='), + '#field_suffix' => '‎', + '#attributes' => array('dir'=>'ltr'), ); break; case 'menu': @@ -458,6 +481,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' => '
', + '#suffix' => '
', + '#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' => '
', + '#suffix' => '
', + '#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; } } @@ -516,6 +581,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 views_ui.module views_ui.module index fbb581c..03c0c79 100644 --- views_ui.module +++ views_ui.module @@ -239,6 +239,10 @@ function views_ui_theme() { 'arguments' => array('form' => NULL), 'file' => 'includes/admin.inc', ), + 'views_ui_breadcrumb_plugin_custom' => array( + 'arguments' => array('form' => NULL), + 'file' => 'includes/admin.inc', + ), ); }