danielebarchiesi@2: name; danielebarchiesi@2: } danielebarchiesi@2: danielebarchiesi@2: public function getContents() { danielebarchiesi@2: return $this->contents; danielebarchiesi@2: } danielebarchiesi@2: danielebarchiesi@2: public function getSettings() { danielebarchiesi@2: return $this->settings; danielebarchiesi@2: } danielebarchiesi@2: danielebarchiesi@2: public function getTitle() { danielebarchiesi@2: return isset($this->settings['title']) ? $this->translateString($this->settings['title'], 'title') : $this->name; danielebarchiesi@2: } danielebarchiesi@2: danielebarchiesi@2: /** danielebarchiesi@2: * Instantiate, populate and return a QuickSet object wrapped in a renderer. danielebarchiesi@2: * danielebarchiesi@2: * @param $name danielebarchiesi@2: * The unique name (machine name) of the QuickSet instance. danielebarchiesi@2: * danielebarchiesi@2: * @param $contents danielebarchiesi@2: * The array of content items, each one itself an array with at least a 'type' danielebarchiesi@2: * key, a 'title' key, and the other info necessary for that type. danielebarchiesi@2: * danielebarchiesi@2: * @param $renderer danielebarchiesi@2: * The plugin key for this renderer plugin danielebarchiesi@2: * danielebarchiesi@2: * @param $settings danielebarchiesi@2: * An array of settings determining the behaviour of this QuickSet instance. danielebarchiesi@2: * danielebarchiesi@2: */ danielebarchiesi@2: public static function QuickSetRendererFactory($name, $contents, $renderer, $settings) { danielebarchiesi@2: ctools_include('plugins'); danielebarchiesi@2: if ($class = ctools_plugin_load_class('quicktabs', 'renderers', $renderer, 'handler')) { danielebarchiesi@2: try { danielebarchiesi@2: $qs = new self($name, $contents, $settings); danielebarchiesi@2: } danielebarchiesi@2: catch (InvalidQuickSetException $e) { danielebarchiesi@2: watchdog('Quicktabs', $e->getMessage()); danielebarchiesi@2: return NULL; danielebarchiesi@2: } danielebarchiesi@2: return new $class($qs); danielebarchiesi@2: } danielebarchiesi@2: } danielebarchiesi@2: danielebarchiesi@2: /** danielebarchiesi@2: * Returns a reference to an object that implements the QuickContentRenderable danielebarchiesi@2: * interface. danielebarchiesi@2: */ danielebarchiesi@2: public static function getContentRenderer($tab) { danielebarchiesi@2: if ($tab['type'] == 'prerendered') { danielebarchiesi@2: return new QuickPreRenderedContent($tab); danielebarchiesi@2: } danielebarchiesi@2: if ($content = QuickContent::factory($tab['type'], $tab)) { danielebarchiesi@2: return $content; danielebarchiesi@2: } danielebarchiesi@2: return NULL; danielebarchiesi@2: } danielebarchiesi@2: danielebarchiesi@2: /** danielebarchiesi@2: * Static method to retrieve content from an ajax call. This is called by the danielebarchiesi@2: * quicktabs_ajax() callback in quicktabs.module. danielebarchiesi@2: */ danielebarchiesi@2: public static function ajaxRenderContent($type, $args) { danielebarchiesi@2: if ($renderer = self::getContentRenderer(array('type' => $type))) { danielebarchiesi@2: $output = $renderer->render(FALSE, $args); danielebarchiesi@2: return !empty($output) ? drupal_render($output) : ''; danielebarchiesi@2: } danielebarchiesi@2: return ''; danielebarchiesi@2: } danielebarchiesi@2: danielebarchiesi@2: /** danielebarchiesi@2: * Ensure sensible default settings for each QuickSet object. danielebarchiesi@2: */ danielebarchiesi@2: private static function getDefaultSettings() { danielebarchiesi@2: return array( danielebarchiesi@2: 'title' => '', danielebarchiesi@2: 'style' => 'nostyle', danielebarchiesi@2: 'hide_empty_tabs' => 0, danielebarchiesi@2: 'ajax' => 0, danielebarchiesi@2: 'default_tab' => 0, danielebarchiesi@2: 'options' => array(), danielebarchiesi@2: ); danielebarchiesi@2: } danielebarchiesi@2: danielebarchiesi@2: /** danielebarchiesi@2: * Constructor danielebarchiesi@2: */ danielebarchiesi@2: public function __construct($name, $contents, $settings) { danielebarchiesi@2: $this->name = $name; danielebarchiesi@2: $this->contents = array(); danielebarchiesi@2: foreach ($contents as $key => $item) { danielebarchiesi@2: // Instantiate a content renderer object and add it to the contents array. danielebarchiesi@2: if ($renderer = self::getContentRenderer($item)) { danielebarchiesi@2: $this->contents[$key] = $renderer; danielebarchiesi@2: } danielebarchiesi@2: } danielebarchiesi@2: $default_settings = self::getDefaultSettings(); danielebarchiesi@2: $this->settings = array_merge($default_settings, $settings); danielebarchiesi@2: danielebarchiesi@2: $this->prepareContents(); danielebarchiesi@2: // Set the default style if necessary. danielebarchiesi@2: if ($this->settings['style'] == 'default') { danielebarchiesi@2: $this->settings['style'] = variable_get('quicktabs_tabstyle', 'nostyle'); danielebarchiesi@2: } danielebarchiesi@2: } danielebarchiesi@2: danielebarchiesi@2: /** danielebarchiesi@2: * Returns an ajax path to be used on ajax-enabled tab links. danielebarchiesi@2: * danielebarchiesi@2: * @param $index The index of the tab, i.e where it fits into the QuickSet danielebarchiesi@2: * instance. danielebarchiesi@2: * danielebarchiesi@2: * @param $type The type of content we are providing an ajax path for. danielebarchiesi@2: */ danielebarchiesi@2: public function getAjaxPath($index, $type) { danielebarchiesi@2: return 'quicktabs/ajax/'. $this->name .'/'. $index . '/'. $type; danielebarchiesi@2: } danielebarchiesi@2: danielebarchiesi@2: /** danielebarchiesi@2: * Translates Quicktabs user-defined strings if the i18n module is danielebarchiesi@2: * enabled. danielebarchiesi@2: */ danielebarchiesi@2: public function translateString($string, $type = 'tab', $index = 0) { danielebarchiesi@2: switch ($type) { danielebarchiesi@2: case 'tab': danielebarchiesi@2: $name = "tab:{$this->name}-{$index}:title"; danielebarchiesi@2: break; danielebarchiesi@2: case 'title': danielebarchiesi@2: $name = "title:{$this->name}"; danielebarchiesi@2: break; danielebarchiesi@2: } danielebarchiesi@2: return quicktabs_translate($name, $string); danielebarchiesi@2: } danielebarchiesi@2: danielebarchiesi@2: /** danielebarchiesi@2: * This method does some initial set-up of the tab contents, such as hiding danielebarchiesi@2: * tabs with no content if the hide_empty_tabs option is set. It also makes sure danielebarchiesi@2: * that prerendered contents are never attempted to be loaded via ajax. danielebarchiesi@2: * danielebarchiesi@2: * @throws InvalidQuickSetException if there are no contents to render. danielebarchiesi@2: */ danielebarchiesi@2: protected function prepareContents() { danielebarchiesi@2: if (!count($this->contents)) { danielebarchiesi@2: throw new InvalidQuickSetException('There are no contents to render.'); danielebarchiesi@2: } danielebarchiesi@2: if ($this->settings['hide_empty_tabs'] && !$this->settings['ajax']) { danielebarchiesi@2: // Check if any tabs need to be hidden because of empty content. danielebarchiesi@2: $renderable_contents = 0; danielebarchiesi@2: foreach ($this->contents as $key => $tab) { danielebarchiesi@2: $contents = $tab->render(TRUE); danielebarchiesi@2: if (empty($contents)) { danielebarchiesi@2: // Rather than removing the item, we set it to NULL. This way we retain danielebarchiesi@2: // the same indices across tabs, so that permanent links to particular danielebarchiesi@2: // tabs can be relied upon. danielebarchiesi@2: $this->contents[$key] = NULL; danielebarchiesi@2: // The default tab must not be a hidden tab. danielebarchiesi@2: if ($this->settings['default_tab'] == $key) { danielebarchiesi@2: $this->settings['default_tab'] = ($key + 1) % count($this->contents); danielebarchiesi@2: } danielebarchiesi@2: } danielebarchiesi@2: else { danielebarchiesi@2: $renderable_contents++; danielebarchiesi@2: } danielebarchiesi@2: } danielebarchiesi@2: if (!$renderable_contents) { danielebarchiesi@2: throw new InvalidQuickSetException('There are no contents to render.'); danielebarchiesi@2: } danielebarchiesi@2: } danielebarchiesi@2: elseif ($this->settings['ajax']) { danielebarchiesi@2: // Make sure that there is at most 1 prerendered tab and it is the default tab. danielebarchiesi@2: // Prerendered content cannot be rendered via ajax. danielebarchiesi@2: $has_prerendered = FALSE; // keep track of whether we have found a prerendered tab. danielebarchiesi@2: foreach ($this->contents as $key => $tab) { danielebarchiesi@2: $type = $tab->getType(); danielebarchiesi@2: if ($type == 'prerendered') { danielebarchiesi@2: if (!$has_prerendered) { danielebarchiesi@2: $has_prerendered = TRUE; danielebarchiesi@2: $this->settings['default_tab'] = $key; danielebarchiesi@2: // In the case of a direct link to a different tab, the 'default_tab' danielebarchiesi@2: // will be overridden, so we need to make sure it does not attempt danielebarchiesi@2: // to load a pre-rendered tab via ajax. Turn ajax option off. danielebarchiesi@2: if ($this->getActiveTab() !== $key) { danielebarchiesi@2: $this->settings['ajax'] = 0; danielebarchiesi@2: } danielebarchiesi@2: } danielebarchiesi@2: else { danielebarchiesi@2: // We are on a second custom tab and the ajax option is set, we cannot danielebarchiesi@2: // render custom tabs via ajax, so we skip out of the loop, set the danielebarchiesi@2: // ajax option to off, and call the method again. danielebarchiesi@2: $this->settings['ajax'] = 0; danielebarchiesi@2: $this->prepareContents(); danielebarchiesi@2: return; danielebarchiesi@2: } danielebarchiesi@2: } danielebarchiesi@2: } danielebarchiesi@2: } danielebarchiesi@2: } danielebarchiesi@2: danielebarchiesi@2: /** danielebarchiesi@2: * Returns the active tab for a given Quicktabs instance. This could be coming danielebarchiesi@2: * from the URL or just from the settings for this instance. If neither, it danielebarchiesi@2: * defaults to 0. danielebarchiesi@2: */ danielebarchiesi@2: public function getActiveTab() { danielebarchiesi@2: $active_tab = isset($this->settings['default_tab']) ? $this->settings['default_tab'] : key($this->contents); danielebarchiesi@2: $active_tab = isset($_GET['qt-' . $this->name]) ? $_GET['qt-' . $this->name] : $active_tab; danielebarchiesi@2: $active_tab = (isset($active_tab) && isset($this->contents[$active_tab])) ? $active_tab : 0; danielebarchiesi@2: return $active_tab; danielebarchiesi@2: } danielebarchiesi@2: } danielebarchiesi@2: danielebarchiesi@2: /** danielebarchiesi@2: * Abstract base class for QuickSet Renderers. danielebarchiesi@2: * danielebarchiesi@2: * A renderer object contains a reference to a QuickSet object, which it can danielebarchiesi@2: * then render. danielebarchiesi@2: */ danielebarchiesi@2: abstract class QuickRenderer { danielebarchiesi@2: danielebarchiesi@2: /** danielebarchiesi@2: * @var QuickSet danielebarchiesi@2: */ danielebarchiesi@2: protected $quickset; danielebarchiesi@2: danielebarchiesi@2: /** danielebarchiesi@2: * Constructor danielebarchiesi@2: */ danielebarchiesi@2: public function __construct($quickset) { danielebarchiesi@2: $this->quickset = $quickset; danielebarchiesi@2: } danielebarchiesi@2: danielebarchiesi@2: /** danielebarchiesi@2: * Accessor method for the title. danielebarchiesi@2: */ danielebarchiesi@2: public function getTitle() { danielebarchiesi@2: return $this->quickset->getTitle(); danielebarchiesi@2: } danielebarchiesi@2: danielebarchiesi@2: /** danielebarchiesi@2: * The only method that renderer plugins must implement. danielebarchiesi@2: * danielebarchiesi@2: * @return A render array to be passed to drupal_render(). danielebarchiesi@2: */ danielebarchiesi@2: abstract public function render(); danielebarchiesi@2: danielebarchiesi@2: danielebarchiesi@2: /** danielebarchiesi@2: * Method for returning the form elements to display for this renderer type on danielebarchiesi@2: * the admin form. danielebarchiesi@2: danielebarchiesi@2: * @param $qt An object representing the Quicktabs instance that the tabs are danielebarchiesi@2: * being built for. danielebarchiesi@2: */ danielebarchiesi@2: public static function optionsForm($qt) { danielebarchiesi@2: return array(); danielebarchiesi@2: } danielebarchiesi@2: danielebarchiesi@2: } danielebarchiesi@2: danielebarchiesi@2: /******************************************************* danielebarchiesi@2: * The classes below relate to individual tab content * danielebarchiesi@2: *******************************************************/ danielebarchiesi@2: danielebarchiesi@2: /** danielebarchiesi@2: * Each QuickSet object has a "contents" property which is an array of objects danielebarchiesi@2: * that implement the QuickContentRenderable interface. danielebarchiesi@2: */ danielebarchiesi@2: interface QuickContentRenderable { danielebarchiesi@2: danielebarchiesi@2: /** danielebarchiesi@2: * Returns the short type name of the content plugin, e.g. 'block', 'node', danielebarchiesi@2: * 'prerendered'. danielebarchiesi@2: */ danielebarchiesi@2: public static function getType(); danielebarchiesi@2: danielebarchiesi@2: /** danielebarchiesi@2: * Returns the tab title. danielebarchiesi@2: */ danielebarchiesi@2: public function getTitle(); danielebarchiesi@2: danielebarchiesi@2: /** danielebarchiesi@2: * Returns an array of settings specific to the type of content. danielebarchiesi@2: */ danielebarchiesi@2: public function getSettings(); danielebarchiesi@2: danielebarchiesi@2: /** danielebarchiesi@2: * Renders the content. danielebarchiesi@2: * danielebarchiesi@2: * @param $hide_emtpy If set to true, then the renderer should return an empty danielebarchiesi@2: * array if there is no content to display, for example if the user does not danielebarchiesi@2: * have access to the requested content. danielebarchiesi@2: * danielebarchiesi@2: * @param $args Used during an ajax call to pass in the settings necessary to danielebarchiesi@2: * render this type of content. danielebarchiesi@2: */ danielebarchiesi@2: public function render($hide_empty = FALSE, $args = array()); danielebarchiesi@2: danielebarchiesi@2: /** danielebarchiesi@2: * Returns an array of keys to use for constructing the correct arguments for danielebarchiesi@2: * an ajax callback to retrieve content of this type. The order of the keys danielebarchiesi@2: * returned affects the order of the args passed in to the render method when danielebarchiesi@2: * called via ajax (see the render() method above). danielebarchiesi@2: */ danielebarchiesi@2: public function getAjaxKeys(); danielebarchiesi@2: danielebarchiesi@2: } danielebarchiesi@2: danielebarchiesi@2: /** danielebarchiesi@2: * Abstract base class for content plugins. danielebarchiesi@2: */ danielebarchiesi@2: abstract class QuickContent implements QuickContentRenderable { danielebarchiesi@2: danielebarchiesi@2: /** danielebarchiesi@2: * Used as the title of the tab. danielebarchiesi@2: * @var string danielebarchiesi@2: */ danielebarchiesi@2: protected $title; danielebarchiesi@2: danielebarchiesi@2: /** danielebarchiesi@2: * An array containing the information that defines the tab content, specific danielebarchiesi@2: * to its type. danielebarchiesi@2: * @var array danielebarchiesi@2: */ danielebarchiesi@2: protected $settings; danielebarchiesi@2: danielebarchiesi@2: /** danielebarchiesi@2: * A render array of the contents. danielebarchiesi@2: * @var array danielebarchiesi@2: */ danielebarchiesi@2: protected $rendered_content; danielebarchiesi@2: danielebarchiesi@2: danielebarchiesi@2: /** danielebarchiesi@2: * Constructor danielebarchiesi@2: */ danielebarchiesi@2: public function __construct($item) { danielebarchiesi@2: $this->title = isset($item['title']) ? $item['title'] : ''; danielebarchiesi@2: // We do not need to store title, type or weight in the settings array, which danielebarchiesi@2: // is for type-specific settings. danielebarchiesi@2: unset($item['title'], $item['type'], $item['weight']); danielebarchiesi@2: $this->settings = $item; danielebarchiesi@2: } danielebarchiesi@2: danielebarchiesi@2: danielebarchiesi@2: /** danielebarchiesi@2: * Accessor for the tab title. danielebarchiesi@2: */ danielebarchiesi@2: public function getTitle() { danielebarchiesi@2: return $this->title; danielebarchiesi@2: } danielebarchiesi@2: danielebarchiesi@2: /** danielebarchiesi@2: * Accessor for the tab settings. danielebarchiesi@2: */ danielebarchiesi@2: public function getSettings() { danielebarchiesi@2: return $this->settings; danielebarchiesi@2: } danielebarchiesi@2: danielebarchiesi@2: /** danielebarchiesi@2: * Instantiate a content type object. danielebarchiesi@2: * danielebarchiesi@2: * @param $name danielebarchiesi@2: * The type name of the plugin. danielebarchiesi@2: * danielebarchiesi@2: * @param $item danielebarchiesi@2: * An array containing the item definition danielebarchiesi@2: * danielebarchiesi@2: */ danielebarchiesi@2: public static function factory($name, $item) { danielebarchiesi@2: ctools_include('plugins'); danielebarchiesi@2: if ($class = ctools_plugin_load_class('quicktabs', 'contents', $name, 'handler')) { danielebarchiesi@2: // We now need to check the plugin's dependencies, to make sure they're installed. danielebarchiesi@2: // This info has already been statically cached at this point so there's no danielebarchiesi@2: // harm in making a call to ctools_get_plugins(). danielebarchiesi@2: $plugin = ctools_get_plugins('quicktabs', 'contents', $name); danielebarchiesi@2: if (isset($plugin['dependencies'])) { danielebarchiesi@2: foreach ($plugin['dependencies'] as $dep) { danielebarchiesi@2: // If any dependency is missing we cannot instantiate our class. danielebarchiesi@2: if (!module_exists($dep)) return NULL; danielebarchiesi@2: } danielebarchiesi@2: } danielebarchiesi@2: return new $class($item); danielebarchiesi@2: } danielebarchiesi@2: return NULL; danielebarchiesi@2: } danielebarchiesi@2: danielebarchiesi@2: /** danielebarchiesi@2: * Method for returning the form elements to display for this tab type on danielebarchiesi@2: * the admin form. danielebarchiesi@2: * danielebarchiesi@2: * @param $delta Integer representing this tab's position in the tabs array. danielebarchiesi@2: * danielebarchiesi@2: * @param $qt An object representing the Quicktabs instance that the tabs are danielebarchiesi@2: * being built for. danielebarchiesi@2: */ danielebarchiesi@2: abstract public function optionsForm($delta, $qt); danielebarchiesi@2: danielebarchiesi@2: } danielebarchiesi@2: danielebarchiesi@2: /** danielebarchiesi@2: * This class implements the same interface that content plugins do but it is not danielebarchiesi@2: * a content plugin. It is a special class for pre-rendered content which is used danielebarchiesi@2: * when "custom" tabs are added to existing Quicktabs instances in a call to danielebarchiesi@2: * quicktabs_build_quicktabs(). danielebarchiesi@2: */ danielebarchiesi@2: class QuickPreRenderedContent implements QuickContentRenderable { danielebarchiesi@2: danielebarchiesi@2: public static function getType() { danielebarchiesi@2: return 'prerendered'; danielebarchiesi@2: } danielebarchiesi@2: danielebarchiesi@2: /** danielebarchiesi@2: * Used as the title of the tab. danielebarchiesi@2: * @var title danielebarchiesi@2: */ danielebarchiesi@2: protected $title; danielebarchiesi@2: danielebarchiesi@2: /** danielebarchiesi@2: * A render array of the contents. danielebarchiesi@2: * @var array danielebarchiesi@2: */ danielebarchiesi@2: protected $rendered_content; danielebarchiesi@2: danielebarchiesi@2: danielebarchiesi@2: /** danielebarchiesi@2: * Constructor danielebarchiesi@2: */ danielebarchiesi@2: public function __construct($item) { danielebarchiesi@2: danielebarchiesi@2: $contents = isset($item['contents']) ? $item['contents'] : array(); danielebarchiesi@2: if (!is_array($contents)) { danielebarchiesi@2: $contents = array('#markup' => $contents); danielebarchiesi@2: } danielebarchiesi@2: $this->rendered_content = $contents; danielebarchiesi@2: danielebarchiesi@2: $this->title = isset($item['title']) ? $item['title'] : ''; danielebarchiesi@2: } danielebarchiesi@2: danielebarchiesi@2: /** danielebarchiesi@2: * Accessor for the tab title. danielebarchiesi@2: */ danielebarchiesi@2: public function getTitle() { danielebarchiesi@2: return $this->title; danielebarchiesi@2: } danielebarchiesi@2: danielebarchiesi@2: /** danielebarchiesi@2: * Prerendered content doesn't need any extra settings. danielebarchiesi@2: */ danielebarchiesi@2: public function getSettings() { danielebarchiesi@2: return array(); danielebarchiesi@2: } danielebarchiesi@2: danielebarchiesi@2: danielebarchiesi@2: /** danielebarchiesi@2: * The render method simply returns the contents that were passed in and danielebarchiesi@2: * stored during construction. danielebarchiesi@2: */ danielebarchiesi@2: public function render($hide_empty = FALSE, $args = array()) { danielebarchiesi@2: return $this->rendered_content; danielebarchiesi@2: } danielebarchiesi@2: danielebarchiesi@2: /** danielebarchiesi@2: * This content cannot be rendered via ajax so we don't return any ajax keys. danielebarchiesi@2: */ danielebarchiesi@2: public function getAjaxKeys() { danielebarchiesi@2: return array(); danielebarchiesi@2: } danielebarchiesi@2: danielebarchiesi@2: } danielebarchiesi@2: danielebarchiesi@2: /** danielebarchiesi@2: * Create our own exception class. danielebarchiesi@2: */ danielebarchiesi@2: class InvalidQuickSetException extends Exception { danielebarchiesi@2: danielebarchiesi@2: }