'views', // This just tells our themes are elsewhere. 'display' => array( 'default' => array( 'title' => t('Defaults'), 'help' => t('Default settings for this view.'), 'handler' => 'views_display_plugin_default', 'no ui' => TRUE, 'no remove' => TRUE, 'js' => array('misc/collapse.js', 'misc/textarea.js', 'misc/tabledrag.js'), 'use pager' => TRUE, ), 'page' => array( 'title' => t('Page'), 'help' => t('Display the view as a page, with a URL and menu links.'), 'handler' => 'views_display_plugin_page', 'uses hook menu' => TRUE, 'use pager' => TRUE, ), 'block' => array( 'title' => t('Block'), 'help' => t('Display the view as a block.'), 'handler' => 'views_display_plugin_block', 'uses hook block' => TRUE, ), ), 'style' => array( 'default' => array( 'title' => t('Unformatted'), 'help' => t('Displays rows one after another.'), 'handler' => 'views_style_plugin_default', 'theme' => 'views_view_unformatted', 'uses row plugin' => TRUE, ), 'list' => array( 'title' => t('List'), 'help' => t('Displays rows as an HTML list.'), 'handler' => 'views_style_plugin_list', 'theme' => 'views_view_list', 'uses row plugin' => TRUE, 'uses options' => TRUE, ), 'table' => array( 'title' => t('Table'), 'help' => t('Displays rows in a table.'), 'handler' => 'views_style_plugin_table', 'theme' => 'views_view_table', 'uses row plugin' => FALSE, 'uses fields' => TRUE, ), 'default_summary' => array( 'title' => t('Default'), 'help' => t('Displays the default summary view'), 'handler' => 'views_style_plugin_summary', 'theme' => 'views_view_summary', 'summary' => TRUE, // only shows up as a summary style 'uses options' => TRUE, ), ), 'row' => array( 'fields' => array( 'title' => t('Fields'), 'help' => t('Displays the fields with an optional template.'), 'handler' => 'views_row_plugin', 'theme' => 'views_view_fields', 'uses fields' => TRUE, ), ), ); } /** * Builds and return a list of all plugins available in the system. * * @return Nested array of plugins, grouped by type and */ function views_discover_plugins() { $cache = array('display' => array(), 'style' => array(), 'row' => array()); // Get plugins from all mdoules. foreach (module_implements('views_plugins') as $module) { $function = $module . '_views_plugins'; $result = $function(); if (!is_array($result)) { continue; } $module_dir = isset($result['module']) ? $result['module'] : $module; // Setup automatic path/file finding for theme registration if ($module_dir == 'views') { $path = drupal_get_path('module', $module_dir) . '/theme'; $file = 'theme.inc'; } else { $path = drupal_get_path('module', $module_dir); $file = "$module.views.inc"; } foreach ($result as $type => $info) { if ($type == 'module') { continue; } foreach ($info as $plugin => $def) { if (isset($def['theme']) && !isset($def['path'])) { $def['path'] = $path; $def['file'] = $file; } // merge the new data in $cache[$type][$plugin] = $def; } } } return $cache; } /** * @defgroup views_display_plugins Views' display plugins * @{ * Display plugins control how Views interact with the rest of Drupal. * * They can handle creating Views from a Drupal page hook; they can * handle creating Views from a Drupal block hook. They can also * handle creating Views from an external module source, such as * a Panels pane, or an insert view, or a CCK field type. */ /** * The default display plugin handler. Display plugins handle options and * basic mechanisms for different output methods. */ class views_display_plugin extends views_object { /** * Fill this plugin in with the view, display, etc. */ function init(&$view, &$display) { $this->view = $view; $this->display = $display; } /** * Determine if this display is the 'default' display which contains * fallback settings */ function is_default_display() { return FALSE; } /** * Static member function to list which sections are defaultable * and what items each section contains. */ function defaultable_sections($section = NULL) { $sections = array( 'access' => array('access'), 'title' => array('title'), 'header' => array('header', 'header_format', 'header_empty'), 'footer' => array('footer', 'footer_format', 'footer_empty'), 'empty' => array('empty', 'empty_format'), 'items_per_page' => array('items_per_page', 'offset', 'use_pager', 'pager_element'), 'use_pager' => array('items_per_page', 'offset', 'use_pager', 'pager_element'), 'link_display' => array('link_display'), // @todo 'php_arg_code' => array('php_arg_code'), 'exposed_options' => array('exposed_options'), // Force these to cascade properly. 'style_plugin' => array('style_plugin', 'style_options', 'row_plugin', 'row_options'), 'style_options' => array('style_plugin', 'style_options', 'row_plugin', 'row_options'), 'row_plugin' => array('style_plugin', 'style_options', 'row_plugin', 'row_options'), 'row_options' => array('style_plugin', 'style_options', 'row_plugin', 'row_options'), // These guys are special 'relationships' => array('relationships'), 'fields' => array('fields'), 'sorts' => array('sorts'), 'arguments' => array('arguments'), 'filters' => array('filters'), ); if ($section) { if (!empty($sections[$section])) { return $sections[$section]; } } else { return $sections; } } /** * This is called when a new display is created that has never been * saved to the database. It provides appropriate defaults for the * display, if necessary. */ function options(&$display) { // Set the following options to use the default display, forcing // them to pass through unless we override. $display->display_options['defaults'] = array( 'access' => TRUE, 'title' => TRUE, 'header' => TRUE, 'header_format' => TRUE, 'header_empty' => TRUE, 'footer' => TRUE, 'footer_format' => TRUE, 'footer_empty' => TRUE, 'empty' => TRUE, 'empty_format' => TRUE, 'items_per_page' => TRUE, 'offset' => TRUE, 'use_pager' => TRUE, 'pager_element' => TRUE, 'link_display' => TRUE, 'php_arg_code' => TRUE, 'exposed_options' => TRUE, 'style_plugin' => TRUE, 'style_options' => TRUE, 'row_plugin' => TRUE, 'row_options' => TRUE, 'relationships' => TRUE, 'fields' => TRUE, 'sorts' => TRUE, 'arguments' => TRUE, 'filters' => TRUE, ); $display->display_options['relationships'] = array(); $display->display_options['fields'] = array(); $display->display_options['sorts'] = array(); $display->display_options['arguments'] = array(); $display->display_options['filters'] = array(); } /** * Check to see if the display has a 'path' field. * * This is a pure function and not just a setting on the definition * because some displays (such as a panel pane) may have a path based * upon configuration. * * By default, displays do not have a path. */ function has_path() { return FALSE; } /** * Check to see which display to use when creating links within * a view using this display. */ function get_link_display() { $display_id = $this->get_option('link_display'); // If unknown, pick the first one. if (empty($display_id) || empty($this->view->display[$display_id])) { foreach ($this->view->display as $display_id => $display) { if ($display->handler->has_path()) { return $display_id; } } } else { return $display_id; } // fall-through returns NULL } /** * Return the base path to use for this display. * * This can be overridden for displays that do strange things * with the path. */ function get_path() { if ($this->has_path()) { return $this->get_option('path'); } $display_id = $this->get_link_display(); if ($display_id && !empty($this->view->display[$display_id])) { return $this->view->display[$display_id]->handler->get_path(); } } /** * Check to see if the display needs a breadcrumb * * By default, displays do not need breadcrumbs */ function uses_breadcrumb() { return FALSE; } /** * Intelligently get an option either from this display or from the * default display, if directed to do so. */ function get_option($option) { if (!$this->is_default_display() && !empty($this->default_display) && !empty($this->display->display_options['defaults'][$option])) { return $this->default_display->get_option($option); } if (array_key_exists($option, $this->display->display_options)) { return $this->display->display_options[$option]; } } /** * Intelligently set an option either from this display or from the * default display, if directed to do so. */ function set_option($option, $value) { if (!$this->is_default_display() && !empty($this->default_display) && !empty($this->display->display_options['defaults'][$option])) { return $this->default_display->set_option($option, $value); } return $this->display->display_options[$option] = $value; } /** * Because forms may be split up into sections, this provides * an easy URL to exactly the right section. Don't override this. */ function option_link($text, $section, $class = '') { if (!empty($class)) { $text = '' . $text . ''; } return l($text, 'admin/build/views/nojs/display/' . $this->view->name . '/' . $this->display->id . '/' . $section, array('attributes' => array('class' => 'views-ajax-link ' . $class), 'html' => TRUE)); } /** * Provide the default summary for options in the views UI. * * This output is returned as an array. */ function options_summary(&$categories, &$options) { $categories['basic'] = array( 'title' => t('Basic settings'), ); $options['display_title'] = array( 'category' => 'basic', 'title' => t('Name'), 'value' => $this->display->display_title, ); $title = $this->get_option('title'); if (!$title) { $title = t('None'); } $options['title'] = array( 'category' => 'basic', 'title' => t('Title'), 'value' => $title, ); $style_plugin = views_fetch_plugin_data('style', $this->get_option('style_plugin')); $style_title = empty($style_plugin['title']) ? t('Missing style plugin') : $style_plugin['title']; $style = ''; $options['style_plugin'] = array( 'category' => 'basic', 'title' => t('Style'), 'value' => $style_title, ); // This adds a 'Settings' link to the style_options setting if the style has options. if (!empty($style_plugin['uses options'])) { $options['style_plugin']['links']['style_options'] = t('Settings'); } if (!empty($style_plugin['uses row plugin'])) { $row_plugin = views_fetch_plugin_data('row', $this->get_option('row_plugin')); $row_title = empty($row_plugin['title']) ? t('Missing style plugin') : $row_plugin['title']; $options['row_plugin'] = array( 'category' => 'basic', 'title' => t('Row style'), 'value' => $row_title, ); // This adds a 'Settings' link to the row_options setting if the row style has options. if (!empty($row_plugin['uses options'])) { $options['row_plugin']['links']['row_options'] = t('Settings'); } } $access = $this->get_option('access'); if (!is_array($access)) { $access = array('type' => 'none'); } switch($access['type']) { case 'none': default: $access_str = t('Unrestricted'); break; case 'perm': $access_str = $access['perm']; break; case 'role': $roles = array_keys(array_filter($access['role'])); if (count($roles) > 1) { $access_str = t('Multiple roles'); } else { $rids = views_ui_get_roles(); $rid = array_shift($roles); $access_str = $rids[$rid]; } break; } if (!empty($this->definition['use pager'])) { $options['use_pager'] = array( 'category' => 'basic', 'title' => t('Use pager'), 'value' => $this->get_option('use_pager') ? t('Yes') : t('No'), ); } $options['items_per_page'] = array( 'category' => 'basic', 'title' => $this->get_option('use_pager') ? t('Items per page') : t('Items to display'), 'value' => intval($this->get_option('items_per_page')), ); $options['access'] = array( 'category' => 'basic', 'title' => t('Access'), 'value' => $access_str, ); if (!$this->has_path()) { // Only show the 'link display' if there is more than one option. $count = 0; foreach ($this->view->display as $display_id => $display) { if ($display->handler->has_path()) { $count++; } if ($count > 1) { break; } } if ($count > 1) { $display_id = $this->get_link_display(); $link_display = empty($this->view->display[$display_id]) ? t('None') : $this->view->display[$display_id]->display_title; $options['link_display'] = array( 'category' => 'basic', 'title' => t('Link display'), 'value' => $link_display, ); } } foreach (array('header' => t('Header'), 'footer' => t('Footer'), 'empty' => t('Empty text')) as $type => $name) { if (!$this->get_option($type)) { $field = t('None'); } else { // A lot of code to get the name of the filter format. $fmt_string = $this->get_option($type . '_format'); if (empty($fmt_string)) { $fmt_string = FILTER_FORMAT_DEFAULT; } $format_val = filter_resolve_format($fmt_string); $format = filter_formats($format_val); $field = $format->name; } $output[] = t('!name: !field', array('!name' => $name, '!field' => $this->option_link($field, $type))); $options[$type] = array( 'category' => 'basic', 'title' => $name, 'value' => $field, ); } } /** * Provide the default form for setting options. */ function options_form(&$form, &$form_state) { if ($this->defaultable_sections($form_state['section'])) { $this->add_override_button($form, $form_state['section']); } $form['#title'] = check_plain($this->display->display_title) . ': '; // Set the 'section' to hilite on the form. // If it's the item we're looking at is pulling from the default display, // reflect that. if (!empty($this->display->display_options['defaults'][$form_state['section']])) { $form['#section'] = 'default-' . $form_state['section']; } else { $form['#section'] = $this->display->id . '-' . $form_state['section']; } switch ($form_state['section']) { case 'display_title': $form['#title'] .= t('The name of this display'); $form['display_title'] = array( '#type' => 'textfield', '#description' => t('This title will appear only in the administrative interface for the View.'), '#default_value' => $this->display->display_title, ); break; case 'title': $form['#title'] .= t('The title of this view'); $form['title'] = array( '#type' => 'textfield', '#description' => t('This title will be displayed with the view, wherever titles are normally displayed; i.e, as the page title, block title, etc.'), '#default_value' => $this->get_option('title'), ); break; case 'use_pager': $form['#title'] .= t('Use a pager for this view'); $form['use_pager'] = array( '#type' => 'radios', '#options' => array(1 => t('Yes'), 0 => t('No')), '#default_value' => $this->get_option('use_pager'), ); $form['pager_element'] = array( '#type' => 'textfield', '#title' => t('Pager element'), '#description' => t('Which element of the ?page= array to use, if the pager is enabled. 0 is recommended, but if there are other pagers on the page, you may use a higher number so as not to conflict. Large values are bad here as they will add a lot of commas to the URLs.'), '#default_value' => intval($this->get_option('pager_element')), ); break; case 'items_per_page': if ($this->get_option('use_pager')) { $form['#title'] .= t('Items per page'); } else { $form['#title'] .= t('Items to display'); } $form['items_per_page'] = array( '#type' => 'textfield', '#description' => t('The number of items to display per page. Enter 0 for no limit.'), '#default_value' => intval($this->get_option('items_per_page')), ); $form['offset'] = array( '#type' => 'textfield', '#title' => t('Offset'), '#description' => t('The number of items to skip. For example, if this field is 3, the first 3 items will be skipped and not displayed.'), '#default_value' => intval($this->get_option('offset')), ); break; case 'access': $form['#title'] .= t('Access restrictions'); $form['access'] = array( '#prefix' => '
', '#suffix' => '
', '#tree' => TRUE, ); $access = $this->get_option('access'); if (empty($access)) { $access = array('type' => 'none', 'role' => array(), 'perm' => ''); } $form['access']['type'] = array( '#prefix' => '
', '#suffix' => '
', '#title' => t('Type'), '#type' => 'radios', '#options' => array('none' => t('Unrestricted'), 'role' => t('By role'), 'perm' => t('By perm')), '#default_value' => $access['type'], ); $form['access']['role'] = array( '#prefix' => '
', '#suffix' => '
', '#type' => 'checkboxes', '#title' => t('If by role'), '#default_value' => $access['role'], '#options' => views_ui_get_roles(), '#description' => t('Only the checked roles will be able to access this display.'), ); $perms = array(); // Get list of permissions foreach (module_list(FALSE, FALSE, TRUE) as $module) { if ($permissions = module_invoke($module, 'perm')) { $perms[$module] = drupal_map_assoc($permissions); } } $form['access']['perm'] = array( '#prefix' => '
', '#suffix' => '
', '#type' => 'select', '#options' => $perms, '#title' => t('If by perm'), '#default_value' => $access['perm'], '#description' => t('Only users with the selected permission flag will be able to access this display.'), ); break; case 'header': $form['#title'] .= t('Header'); $form['header_empty'] = array( '#type' => 'checkbox', '#title' => t('Display even if view has no result'), '#default_value' => $this->get_option('header_empty'), ); $form['header'] = array( '#type' => 'textarea', '#default_value' => $this->get_option('header'), '#rows' => 6, '#description' => t('Text to display at the top of the view. May contain an explanation or links or whatever you like. Optional.'), ); $form['header_format'] = filter_form($this->get_option('header_format'), NULL, array('header_format')); break; case 'footer': $form['#title'] .= t('Footer'); $form['footer_empty'] = array( '#type' => 'checkbox', '#title' => t('Display even if view has no result'), '#default_value' => $this->get_option('header_empty'), ); $form['footer'] = array( '#type' => 'textarea', '#default_value' => $this->get_option('footer'), '#rows' => 6, '#description' => t('Text to display beneath the view. May contain an explanation or links or whatever you like. Optional.'), ); $form['footer_format'] = filter_form($this->get_option('footer_format'), NULL, array('footer_format')); break; case 'empty': $form['#title'] .= t('Empty text'); $form['empty'] = array( '#type' => 'textarea', '#default_value' => $this->get_option('empty'), '#rows' => 6, '#description' => t('Text to display if the view has no results. Optional.'), ); $form['empty_format'] = filter_form($this->get_option('empty_format'), NULL, array('empty_format')); break; case 'style_plugin': $form['#title'] .= t('How should this view be styled'); $form['style_plugin'] = array( '#type' => 'radios', '#options' => views_fetch_plugin_names('style', 'summary', FALSE), '#default_value' => $this->get_option('style_plugin'), ); break; case 'style_options': $form['#title'] .= t('Style options'); $style = TRUE; $type = 'style_plugin'; case 'row_options': // if row, $style will be empty. if (empty($style)) { $form['#title'] .= t('Row style options'); $type = 'row_plugin'; } $plugin = views_get_plugin(empty($style) ? 'row' : 'style', $this->get_option($type)); if ($plugin) { $form[$form_state['section']] = array( '#tree' => TRUE, ); $plugin->init($this->view, $this->display); $plugin->options_form($form[$form_state['section']], $form_state); } break; case 'row_plugin': $form['#title'] .= t('How should each row in this view be styled'); $form['row_plugin'] = array( '#type' => 'radios', '#options' => views_fetch_plugin_names('row', 'summary', FALSE), '#default_value' => $this->get_option('row_plugin'), ); break; case 'link_display': $form['#title'] .= t('Which display to use for path'); foreach ($this->view->display as $display_id => $display) { if ($display->handler->has_path()) { $options[$display_id] = $display->display_title; } } $form['link_display'] = array( '#type' => 'radios', '#options' => $options, '#description' => t('Which display to use to get this display\'s path for things like summary links, rss feed links, more links, etc.'), '#default_value' => $this->get_link_display(), ); break; } } /** * Validate the options form. */ function options_validate($form, &$form_state) { switch ($form_state['section']) { case 'style_options': $style = TRUE; case 'row_options': // if row, $style will be empty. $plugin = views_get_plugin(empty($style) ? 'row' : 'style', $this->get_option(empty($style) ? 'row_plugin' : 'style_plugin')); if ($plugin) { $plugin->init($this->view, $this->display); $plugin->options_validate($form[$form_state['section']], $form_state); } break; case 'access': $access = $form_state['values']['access']; if ($access['type'] == 'role' && !array_filter($access['role'])) { form_error($form, t('You must select at least one role if type is "by role"')); } } } /** * Perform any necessary changes to the form values prior to storage. * There is no need for this function to actually store the data. */ function options_submit($form, &$form_state) { $section = $form_state['section']; switch ($section) { case 'display_title': $this->display->display_title = $form_state['values']['display_title']; break; case 'title': case 'access': case 'link_display': case 'php_arg_code': $this->set_option($section, $form_state['values'][$section]); break; case 'use_pager': $this->set_option($section, $form_state['values'][$section]); $this->set_option('pager_element', intval($form_state['values']['pager_element'])); break; case 'items_per_page': $this->set_option($section, intval($form_state['values'][$section])); $this->set_option('offset', intval($form_state['values']['offset'])); break; case 'row_plugin': // This if prevents resetting options to default if they don't change // the plugin. if ($this->get_option($section) != $form_state['values'][$section]) { $this->set_option($section, $form_state['values'][$section]); $plugin = views_get_plugin('row', $form_state['values'][$section]); $this->set_option('row_options', $plugin->options($this->display)); } break; case 'style_plugin': // This if prevents resetting options to default if they don't change // the plugin. if ($this->get_option($section) != $form_state['values'][$section]) { $this->set_option($section, $form_state['values'][$section]); $plugin = views_get_plugin('style', $form_state['values'][$section]); $this->set_option('style_options', $plugin->options($this->display)); } break; case 'style_options': $style = TRUE; case 'row_options': // if row, $style will be empty. $plugin = views_get_plugin(empty($style) ? 'row' : 'style', $this->get_option('style_plugin')); if ($plugin) { $plugin->init($this->view, $this->display); $plugin->options_submit($form[$section], $form_state); } $this->set_option($section, $form_state['values'][$section]); break; case 'header': case 'footer': case 'empty': $this->set_option($section, $form_state['values'][$section]); $this->set_option($section . '_format', $form_state['values'][$section . '_format']); if ($section != 'empty') { $this->set_option($section . '_empty', $form_state['values'][$section . '_empty']); } break; } } /** * Add an override button for a given section, allowing the user to * change whether this info is stored on the default display or on * the current display. */ function add_override_button(&$form, $section) { if ($this->is_default_display()) { return; } $form['override'] = array( '#prefix' => '
', '#suffix' => '
', ); if (!empty($this->display->display_options['defaults'][$section])) { $form['override']['button'] = array( '#type' => 'submit', '#value' => t('Override'), '#submit' => array('views_ui_edit_display_form_override'), ); $form['override']['markup'] = array( '#prefix' => '
', '#value' => t('This item is currently using default values; modifying this value will modify it for all displays.'), '#suffix' => '
', ); } else { $form['override']['button'] = array( '#type' => 'submit', '#value' => t('Use default'), '#submit' => array('views_ui_edit_display_form_override'), ); $form['override']['markup'] = array( '#prefix' => '
', '#value' => t('This item is currently overriding default values; modifying this value will modify only for this display. Reverting it will remove current values and return to default values.'), '#suffix' => '
', ); } } /** * If override/revert was clicked, perform the proper toggle. */ function options_override($form, &$form_state) { $section = $form_state['section']; $options = $this->defaultable_sections($section); if (!$options) { return; } $new_state = empty($this->display->display_options['defaults'][$section]); // For each option that is part of this group, fix our settings. foreach ($options as $option) { if ($new_state) { // Revert to defaults. unset($this->display->display_options[$option]); } else { // copy existing values into our display. $this->display->display_options[$option] = $this->get_option($option); } $this->display->display_options['defaults'][$option] = $new_state; } } /** * Not all display plugins will support filtering */ function render_filters() { } /** * Not all display plugins will have a 'more' link */ function render_more_link() { } /** * Not all display plugins will have a feed icon. */ function render_feed_icon() { } /** * Render the view's title for display * @todo Necessary? Hm. */ function render_title() { } function render_textarea($area) { $value = $this->get_option($area); if ($value) { return check_markup($value, $this->get_option($area . '_format')); } } /** * Render the header of the view. */ function render_header() { return $this->render_textarea('header'); } /** * Render the footer of the view. */ function render_footer() { return $this->render_textarea('footer'); } /** * Render the empty text of the view. */ function render_empty() { return $this->render_textarea('empty'); } /** * If this display creates a block, implement one of these. */ function hook_block($op = 'list', $delta = 0, $edit = array()) { return array(); } /** * If this display creates a page with a menu item, implement it here. */ function hook_menu() { return array(); } /** * Render this display. */ function render() { $themes = array( 'views_view__' . $this->display->id . '__' . $this->view->name, 'views_view__' . $this->display->id, 'views_view__' . $this->display->display_plugin . '__' . $this->view->name, 'views_view__' . $this->display->display_plugin, 'views_view__' . $this->view->name, 'views_view', ); return theme($themes, $this->view); } /** * Determine if the user has access to this display of the view. */ function access($account) { $access = $this->get_option('access'); switch ($access['type']) { case 'role': $roles = array_keys($account->roles); $roles[] = $account->uid ? DRUPAL_AUTHENTICATED_RID : DRUPAL_ANONYMOUS_RID; return array_intersect(array_filter($access['role']), $roles); case 'perm': return user_access($access['perm'], $account); case 'none': default: return TRUE; } } /** * When used externally, this is how a view gets run and returns * data in the format required. * * The base class cannot be executed. */ function execute() { } } /** * A plugin to handle defaults on a view. */ class views_display_plugin_default extends views_display_plugin { /** * Determine if this display is the 'default' display which contains * fallback settings */ function is_default_display() { return TRUE; } function options(&$display) { // Make sure the default display has a style plugin to start with. $display->display_options['style_plugin'] = 'default'; $display->display_options['style_options'] = array(); $display->display_options['row_plugin'] = 'fields'; $display->display_options['row_options'] = array(); $display->display_options['relationships'] = array(); $display->display_options['fields'] = array(); $display->display_options['sorts'] = array(); $display->display_options['arguments'] = array(); $display->display_options['filters'] = array(); $display->display_options['items_per_page'] = 10; } /** * The default execute handler fully renders the view. * * For the simplest use: * @code * $output = $view->execute_display('default', $args); * @endcode * * For more complex usages, a view can be partially built: * @code * $view->set_arguments($args); * $view->build('default'); // Build the query * $view->execute(); // Run the query * $output = $view->render(); // Render the view * @endcode * * If short circuited at any point, look in $view->build_info for * information about the query. After execute, look in $view->result * for the object returned from db_query. * * You can also do: * @code * $view->set_arguments($args); * $output = $view->render('default'); // Render the view * @endcode * * This illustrates that render is smart enough to call build and execute * if these items have not already been accomplished. */ function execute() { return $this->view->render(); } } /** * The plugin that handles a full page. */ class views_display_plugin_page extends views_display_plugin { /** * The page display has a path. */ function has_path() { return TRUE; } function uses_breadcrumb() { return TRUE; } /** * Add this display's path information to Drupal's menu system. */ function execute_hook_menu() { $items = array(); // Replace % with the link to our standard views argument loader // views_arg_load -- which lives in views.module $path = str_replace('%', '%views_arg', $this->get_option('path')); if ($path) { // NOTE: This is the very simple 'menu normal item' version. The // tab version needs to come later. Maybe it should be its own plugin. $items[$path] = array( // default views page entry 'page callback' => 'views_page', 'page arguments' => array($this->view->name, $this->display->id), // Default access check (per display) 'access callback' => 'views_access', 'access arguments' => array(array($this->view->name, $this->display->id)), // Identify URL embedded arguments and correlate them to a handler 'load arguments' => array($this->view->name, '%index'), // Basic menu title 'title' => $this->display->handler->get_option('title'), 'type' => MENU_NORMAL_ITEM, ); } return $items; } /** * The display page handler returns a normal view, but it also does * a drupal_set_title for the page, and does a views_set_page_view * on the view. */ function execute() { // Let the world know that this is the page view we're using. views_set_page_view($this); // Prior to this being called, the $view should already be set to this // display, and arguments should be set on the view. $this->view->build(); // Now that we've built the view, extract the breadcrumb. $base = TRUE; $breadcrumb = array(); if (!empty($this->view->build_info['breadcrumb'])) { foreach ($this->view->build_info['breadcrumb'] as $path => $title) { if ($path == variable_get('site_frontpage', 'node')) { $base = FALSE; $title = t('Home'); } $breadcrumb[] = l($title, $path, array('html' => true)); } if ($base) { drupal_set_breadcrumb(array_merge(drupal_get_breadcrumb(), $breadcrumb)); } else { drupal_set_breadcrumb($breadcrumb); } } // And the title, which is much easier. drupal_set_title(filter_xss_admin($this->view->get_title())); // And now render the view. return $this->view->render(); } /** * Provide the summary for page options in the views UI. * * This output is returned as an array. */ function options_summary(&$categories, &$options) { // It is very important to call the parent function here: parent::options_summary($categories, $options); $categories['page'] = array( 'title' => t('Page settings'), ); $path = $this->get_option('path'); if (empty($path)) { $path = t('None'); } if (strlen($path) > 16) { $path = substr($path, 0, 16) . '...'; } $options['path'] = array( 'category' => 'page', 'title' => t('Path'), 'value' => $path, ); $options['menu'] = array( 'category' => 'page', 'title' => t('Menu'), 'value' => 'TODO', ); } /** * Provide the default form for setting options. */ function options_form(&$form, &$form_state) { // It is very important to call the parent function here: parent::options_form($form, $form_state); switch ($form_state['section']) { case 'path': $form['#title'] .= t('The menu path or URL of this view'); $form['path'] = array( '#type' => 'textfield', '#description' => t('This view will be displayed by visiting this path on your site.'), '#default_value' => $this->get_option('path'), ); break; } } /** * Validate the options form. */ function options_validate($form, &$form_state) { // It is very important to call the parent function here: parent::options_validate($form, $form_state); switch ($form_state['section']) { case 'path': // @todo: validate the path against other views // @todo: validate the path against aliases. break; } } /** * Perform any necessary changes to the form values prior to storage. * There is no need for this function to actually store the data. */ function options_submit($form, &$form_state) { // It is very important to call the parent function here: parent::options_submit($form, $form_state); switch ($form_state['section']) { case 'path': $this->set_option('path', $form_state['values']['path']); break; } } } /** * The plugin that handles a block. */ class views_display_plugin_block extends views_display_plugin { /** * The default block handler doesn't support configurable items, * but extended block handlers might be able to do interesting * stuff with it. */ function execute_hook_block($op = 'list', $delta = 0, $edit = array()) { if ($op == 'list') { $delta = $this->view->name . '-' . $this->display->id; $desc = $this->get_option('block_description'); if (empty($desc)) { $desc = $this->view->name; } return array($delta => array('info' => $desc)); } } /** * The display block handler returns the structure necessary for a block. */ function execute() { // Prior to this being called, the $view should already be set to this // display, and arguments should be set on the view. $info['content'] = $this->view->render(); $info['subject'] = filter_xss_admin($this->view->get_title()); return $info; } /** * Provide the summary for page options in the views UI. * * This output is returned as an array. */ function options_summary(&$categories, &$options) { // It is very important to call the parent function here: parent::options_summary($categories, $options); $categories['block'] = array( 'title' => t('Block settings'), ); $block_description = $this->get_option('block_description'); if (empty($block_description)) { $block_description = t('None'); } if (strlen($block_description) > 16) { $block_description = substr($block_description, 0, 16) . '...'; } $options['block_description'] = array( 'category' => 'block', 'title' => t('Admin'), 'value' => $block_description, ); } /** * Provide the default form for setting options. */ function options_form(&$form, &$form_state) { // It is very important to call the parent function here: parent::options_form($form, $form_state); switch ($form_state['section']) { case 'block_description': $form['#title'] .= t('Block admin description'); $form['block_description'] = array( '#type' => 'textfield', '#description' => t('This will appear as the name of this block in administer >> site building >> blocks.'), '#default_value' => $this->get_option('block_description'), ); break; } } /** * Validate the options form. */ function options_validate($form, &$form_state) { // It is very important to call the parent function here: parent::options_validate($form, $form_state); switch ($form_state['section']) { case 'block_description': // @todo: validate the block_description against other views // @todo: validate the block_description against aliases. break; } } /** * Perform any necessary changes to the form values prior to storage. * There is no need for this function to actually store the data. */ function options_submit($form, &$form_state) { // It is very important to call the parent function here: parent::options_submit($form, $form_state); switch ($form_state['section']) { case 'block_description': $this->set_option('block_description', $form_state['values']['block_description']); break; } } } /** * @} */ /** * @defgroup views_style_plugins Views' style plugins * @{ * Style plugins control how a view is rendered. For example, they * can choose to display a collection of fields, node_view() output, * table output, or any kind of crazy output they want. * * Many style plugins can have an optional 'row' plugin, that displays * a single record. Not all style plugins can utilize this, so it is * up to the plugin to set this up and call through to the row plugin. */ /** * Base class to define a style plugin handler. */ class views_style_plugin extends views_object { /** * Initialize a style plugin. * * @param $view * @param $display * @param $options * The style options might come externally as the style can be sourced * from at least two locations. If it's not included, look on the display. */ function init(&$view, &$display, $options = NULL) { $this->view = $view; $this->display = $display; if (isset($options)) { $this->options = $options; } else { $this->options = $display->handler->get_option('style_options'); } if ($this->uses_row_plugin()) { $this->row_plugin = views_get_plugin('row', $display->handler->get_option('row_plugin')); // initialize the row plugin. if ($this->row_plugin) { $this->row_plugin->init($view, $display); } } } /** * Return TRUE if this style also uses a row plugin. */ function uses_row_plugin() { return !empty($this->definition['uses row plugin']); } /** * Return TRUE if this style also uses fields. */ function uses_fields() { // If we use a row plugin, ask the row plugin. Chances are, we don't // care, it does. if ($this->uses_row_plugin()) { return $this->row_plugin->uses_fields(); } // Otherwise, maybe we do. return !empty($this->definition['uses fields']); } /** * Static member function to set default options. */ function options(&$display) { return array(); } /** * Provide a form for setting options. */ function options_form(&$form, &$form_state) { } /** * Validate the options form. */ function options_validate($form, &$form_state) { } /** * Perform any necessary changes to the form values prior to storage. * There is no need for this function to actually store the data. */ function options_submit($form, &$form_state) { } function render($rows) { } } /** * Default style plugin to render rows one after another with no * decorations. */ class views_style_plugin_default extends views_style_plugin { /** * Render the given style. */ function render() { if (empty($this->row_plugin)) { vpr('views_style_plugin_default: Missing row plugin'); return; } // @todo: This needs to be able to support either a database resource OR // an array, because our input format doesn't actually have to be from // a query. $rows = array(); while ($row = db_fetch_object($this->view->result)) { // @todo: Include separator as an option. $rows[] = $this->row_plugin->render($row); } return theme(array('views_view_unformatted__' . $this->view->name, 'views_view_unformatted'), $this->view, $this->options, $rows); } } /** * Style plugin to render each item in an ordered or unordered list */ class views_style_plugin_list extends views_style_plugin { /** * Set default options */ function options($display) { return array( 'type' => 'ul', ); } /** * Render the given style. */ function options_form(&$form, &$form_state) { $form['type'] = array( '#type' => 'radios', '#title' => t('List type'), '#options' => array('ul' => t('Unordered list'), 'ol' => t('Ordered list')), '#default_value' => $this->options['type'], ); } function render() { if (empty($this->row_plugin)) { // @todo: Log some form of error here? return; } // @todo: This needs to be able to support either a database resource OR // an array, because our input format doesn't actually have to be from // a query. $rows = array(); while ($row = db_fetch_object($this->view->result)) { $rows[] = $this->row_plugin->render($row); } return theme(array('views_view_list__' . $this->view->name, 'views_view_list'), $this->view, $this->options, $rows); } } /** * Style plugin to render each item as a row in a table. */ class views_style_plugin_table extends views_style_plugin { /** * Set default options */ function options($display) { return array( ); } /** * Render the given style. */ function options_form(&$form, &$form_state) { $form['markup'] = array( '#default_value' => 'ToDo', ); } function render() { if (empty($this->row_plugin)) { // @todo: Log some form of error here? return; } // @todo: This needs to be able to support either a database resource OR // an array, because our input format doesn't actually have to be from // a query. $rows = ''; return "Table not yet implemented"; } } /** * The default style plugin for summaries. */ class views_style_plugin_summary extends views_style_plugin { function options_form(&$form, &$form_state) { $form['count'] = array( '#type' => 'checkbox', '#default_value' => !empty($this->options['count']), '#title' => t('Display record count with link'), ); } /** * Render the given style. */ function render() { // @todo: This needs to be able to support either a database resource OR // an array, because our input format doesn't actually have to be from // a query. $rows = array(); while ($row = db_fetch_object($this->view->result)) { // @todo: Include separator as an option. $rows[] = $row; } return theme(array('views_view_summary__' . $this->view->name, 'views_view_summary'), $this->view, $this->options, $rows); } } /** * @} */ /** * @defgroup views_row_plugins Views' row plugins * @{ * * Row plugins control how Views outputs an individual record. They are * tightly coupled to style plugins, in that a style plugin is what calls * the row plugin. */ /** * Default plugin to view a single row of a table. This is really just a wrapper around * a theme function. */ class views_row_plugin extends views_object { /** * Initialize the row plugin. */ function init(&$view, &$display) { $this->view = $view; $this->display = $display; $this->options = $display->handler->get_option('row_options'); } function uses_fields() { return !empty($this->definition['uses fields']); } /** * Static member function to set default options. */ function options(&$display) { return array(); } /** * Provide a form for setting options. */ function options_form(&$form, &$form_state) { } /** * Validate the options form. */ function options_validate($form, &$form_state) { } /** * Perform any necessary changes to the form values prior to storage. * There is no need for this function to actually store the data. */ function options_submit($form, &$form_state) { } /** * Render a row object. This usually passes through to a theme template * of some form, but not always. */ function render($row) { return theme(array('views_view_fields__' . $this->view->name, 'views_view_fields'), $this->view, $this->options, $row); } } /** * @} */