$user->name, '%path' => $_GET['referer']), WATCHDOG_WARNING); return; } } else { // not ajax, register the callback before executing magic_tabs_register_callback($callback); } if (!magic_tabs_check_callback($callback)) { // callback not registered. report and bail out watchdog('magic_tabs', 'User %user tried to use a callback %callback without it being registered first.', array('%user' => $user->name, '%callback' => $callback), WATCHDOG_WARNING); return; } $output = ''; $index = 0; if (isset($_GET[$callback .'_tab'])) { // save user's request $_SESSION['magic_tabs'][$callback .'_tab'] = $_GET[$callback .'_tab']; } if (isset($_SESSION['magic_tabs'][$callback .'_tab'])) { // serve user's last request $active = $_SESSION['magic_tabs'][$callback .'_tab']; } if (!is_numeric($active)) { switch ($active) { case 'last': $active = -1; break; default: // case 'first', but cover up for garbage $active = 0; } } $tabs = call_user_func($callback, $active); // go back to our original path if ($_ajax) { unset($_menu['items']); menu_set_active_item($q); } if (empty($tabs)) { return; } // ensure $active is within limits and positive $tabs_count = count($tabs); $active = ($active + $tabs_count) % $tabs_count; // extract titles and contents foreach ($tabs as $tab) { $title = $tab['title'] ? $tab['title'] : t('Tab %index', array('%index' => $index)); $class = strtolower(preg_replace('/[^a-zA-Z0-9]+/ ', '-', $tab['title'])); $path = $_ajax ? $_GET['referer'] :$_GET['q']; $cb_path = url("magic_tabs/$callback", "${callback}_tab=$index&referer=". urlencode($path)); $items[] = l( ''. $title .'', $path, array( 'class' => ($index == $active) ? 'selected '. $class .'-active' : $class, 'onclick' => " $('#$callback .magic_content').addClass('hidden'); $('#$callback .loading').removeClass('hidden'); $('#$callback').load('$cb_path'); return false", ), magic_tabs_query_string(array($callback .'_tab' => $index, 'referer' => urlencode($path))), NULL, FALSE, TRUE ); $index++; } // find the correct theme function $theme_func = $callback; if (!theme_get_function($theme_func)) { // default theme for tabs $theme_func = 'magic_tabs'; } $output = theme($theme_func, $callback, $items, $tabs[$active]['content']) .''; if (!$_ajax) { $output = "
". $output .'
'; } return $output; } /** * Implementation of hook_menu(). */ function magic_tabs_menu() { $items['magic_tabs'] = array( 'title' => 'Magic Tabs', 'callback' => 'magic_tabs_ajax', 'access' => user_access('access content'), 'type' => MENU_CALLBACK, ); return $items; } function magic_tabs_ajax($callback) { global $theme; /** * need to initialize the theme engine first, otherwise, $callback might not * be in the function list */ if (!isset($theme)) { init_theme(); } if (function_exists($callback)) { print magic_tabs_get($callback, 'first', TRUE); exit(); } } function magic_tabs_query_string($params = array()) { $q = array_merge($_GET, $params); $querystring = array(); unset($q['q']); foreach ($q as $key => $value) { if ($key != 'q') { $querystring[] = $key .'='. $value; } } return !empty($querystring) ? implode('&', $querystring) : NULL; } function magic_tabs_register_callback($callback) { // register a callback in the cache. This is not to benefit performance, but to allow saving a temporary state // other solutions considered where: // 1. $_SESSION - not good due to page_cache, // 2. variable table - entries might get stored for too long and provide security risk $callbacks = array(); if (($cache = cache_get('magic_tabs_callbacks')) && !empty($cache->data)) { $callbacks = $cache->data; } $callbacks[$callback] = TRUE; cache_set('magic_tabs_callbacks', $callbacks, CACHE_TEMPORARY); } function magic_tabs_check_callback($callback) { // check if the callback is in the list of allowed callbacks if (($cache = cache_get('magic_tabs_callbacks')) && !empty($cache->data)) { $callbacks = $cache->data; return $callbacks[$callback]; } } /** * Themeable functions */ function theme_magic_tabs($callback, $items, $content) { return theme('item_list', $items, NULL, 'ul', array('class' => 'tabs')) .'
'. $content .'
'; } /** * Stuck here -- the function above needs to be adapted to the below?? http://drupal.org/node/114774#theme_registry */ function theme_magic_tabs_theme() { return array( 'something_format' => array( 'file' => 'something.inc', 'template' => 'something-format', 'arguments' => array('content' => NULL), ), ); } /** * Example callback function */ function magic_tabs_example_callback($active = 0) { $tabs[] = array( 'title' => t('First magic tab'), 'content' => t('Content of first magic tab'), ); $tabs[] = array( 'title' => t('Second magic tab'), 'content' => t('Content of the second magic tab'), ); $tabs[] = array( 'title' => t('Third magic tab'), 'content' => ($active == 2 || $active == -1) ? magic_tabs_get('magic_tabs_inline_callback') : '', ); /* ** Uncomment to display a custom block with $bid==2 */ /* $bid = 2; $block = (object)module_invoke('block', 'block', 'view', $bid); $block->module = 'block'; $block->delta = $bid; $tabs[] = array( 'title' => t('Display a custom block'), 'content' => theme('block', $block), ); */ return $tabs; } function magic_tabs_inline_callback() { $tabs[] = array( 'title' => t('First nested magic tab'), 'content' => t('Content of first nested magic tab'), ); $tabs[] = array( 'title' => t('2nd nested magic tab'), 'content' => t('Content of the second nested magic tab'), ); return $tabs; }