Commit b06399e3 authored by merlinofchaos's avatar merlinofchaos

Basic menu code (needs patch to core). Maybe basic block code too, but I didn't test that part.

parent 4291d390
......@@ -202,6 +202,7 @@ class views_handler_relationship extends views_handler {
* Base field handler that has no options and renders an unformatted field.
*/
class views_handler_field extends views_handler {
var $field_alias = 'unknown';
/**
* Construct a new field handler.
......@@ -218,7 +219,7 @@ class views_handler_field extends views_handler {
$this->ensure_my_table();
// Add the field.
$this->field_alias = $this->query->add_field($this->table_alias, $this->real_field);
dpr("alias: $this->field_alias");
// Add any additional fields we are given.
if (!empty($this->additional_fields) && is_array($this->additional_fields)) {
foreach ($this->additional_fields as $field) {
......@@ -591,6 +592,13 @@ class views_handler_argument extends views_handler {
function title() {
return check_plain($this->argument);
}
/**
* Validate that this argument works. By default, all arguments are valid.
*/
function validate($arg) {
return TRUE;
}
}
/**
......
......@@ -17,11 +17,13 @@ function views_views_plugins() {
'title' => t('Page'),
'help' => t('Creates a page with a URL, menu links, etc.'),
'handler' => 'views_display_plugin_page',
'uses_hook_menu' => TRUE,
),
'block' => array(
'title' => t('Block'),
'help' => t('Creates a block that can be used from the block administration page.'),
'handler' => 'views_display_plugin_block',
'title' => t('Block'),
'help' => t('Creates a block that can be used from the block administration page.'),
'handler' => 'views_display_plugin_block',
'uses_hook_block' => TRUE,
),
'embed' => array(
'title' => t('Embedded'),
......@@ -127,35 +129,51 @@ class views_display_plugin extends views_object {
function render_more_link() { }
/**
* Not all display plugins will have a feed icon, nor will they
* put it in the same place.
* 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_header() { }
function render_footer() { }
function render_empty() { return 'empty text'; }
function render_textarea($area) {
$format_string = $area . '_format';
return check_markup($this->display->$area, $this->display->$format_string);
}
/**
* 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()) {}
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() {}
function hook_menu() { return array(); }
/**
* Render this display.
*/
function render() {
// TODO: Remove this when the 'file' tag on theme registry is fixed.
include_once drupal_get_path('module', 'views') . '/theme/theme.inc';
// include_once drupal_get_path('module', 'views') . '/theme/theme.inc';
$themes = array(
'views_view__' . $this->display->id . '__' . $this->view->name,
'views_view__' . $this->display->id,
......@@ -167,6 +185,17 @@ class views_display_plugin extends views_object {
return theme($themes, $this->view);
}
/**
* Determine if the user has access to this display of the view.
*
* TODO: Implement this.
*/
function access($account) { return TRUE; }
/**
* When used externally, this is how a view gets run and returns
* data in the format required.
*/
function execute() { }
}
/**
......@@ -175,9 +204,43 @@ class views_display_plugin extends views_object {
class views_display_plugin_page extends views_display_plugin {
var $uses_hook_menu = TRUE;
function hook_menu() {}
function execute_hook_menu() {
$items = array();
// Replace % with the link to our standard views argument loader
// views_arg_load -- which lives in views.module
$url = str_replace('%', '%views_arg', $this->display->url);
function render_page() {}
// 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[$url] = 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->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() {
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();
drupal_set_title(filter_xss_admin($this->view->get_title('page')));
return $this->view->render();
}
}
/**
......@@ -185,10 +248,30 @@ class views_display_plugin_page extends views_display_plugin {
*/
class views_display_plugin_block extends views_display_plugin {
var $uses_hook_block = TRUE;
/**
* If this display creates a block, implement one of these.
* The default block handler doesn't support configurable items,
* but extended block handlers might be able to do interesting
* stuff with it.
*/
function hook_block($op = 'list', $delta = 0, $edit = array()) {
if ($op == 'list') {
$delta = $this->view->name . '-' . $this->display->id;
return array($delta => array('info' => $this->display->block_description));
}
}
/**
* The display block handler returns the structure necessary for a block.
*/
function hook_block($op = 'list', $delta = 0, $edit = array()) {}
function execute() {
drupal_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.
$info['content'] = $this->view->render();
$info['subject'] = filter_xss_admin($this->view->get_title('page'));
return $info;
}
}
/**
......@@ -230,7 +313,13 @@ class views_style_plugin_default extends views_style_plugin {
// provide an option form to select from our list of node renderers
}
/**
* 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.
$plugin = new views_row_plugin;
$rows = '';
while ($row = db_fetch_object($this->view->result)) {
......
......@@ -32,7 +32,7 @@ class view extends views_db_object {
var $page_size = 25;
var $pager_element = 0;
var $offset = 0;
var $current_page = 0;
/**
* Constructor
*/
......@@ -63,7 +63,7 @@ class view extends views_db_object {
* Set the arguments that come to this view. Usually from the URL
* but possibly from elsewhere.
*/
function set_args($args) {
function set_arguments($args) {
$this->args = $args;
}
......@@ -192,7 +192,7 @@ class view extends views_db_object {
}
// Execute the initial PHP code that a view can have.
if ($this->view_args_php) {
if (!empty($this->view_args_php)) {
ob_start();
$result = eval($this->view_args_php);
if (is_array($result)) {
......@@ -211,6 +211,7 @@ class view extends views_db_object {
$views_data = views_fetch_data($this->base_table);
$this->base_field = $views_data['table']['base']['field'];
views_include('query');
$this->query = new views_query($this->base_table, $this->base_field);
// Call a module hook and see if it wants to present us with a
......@@ -219,7 +220,7 @@ class view extends views_db_object {
// TODO: Implement this.
// Run through our handlers and ensure they have necessary information.
$this->_seed_handlers();
$this->seed_handlers();
// Build all the filters.
$this->_build('filter');
......@@ -237,7 +238,7 @@ class view extends views_db_object {
continue;
}
if (isset($this->args[$id])) {
if (isset($this->args[$id]) && $argument->handler->validate($this->args[$id])) {
// handle argument that is present.
// TODO: Do we want to put in argument placeholders here
// So that we can try to cache queries with arguments too?
......@@ -257,6 +258,9 @@ class view extends views_db_object {
}
}
// TODO: if $argument_title is set, get the argument and
// set it in the build info.
// Build our sort criteria if we were instructed to do so.
if (!empty($this->build_sort)) {
$this->_build('sort');
......@@ -290,7 +294,7 @@ class view extends views_db_object {
/**
* Acquire and attach all of the handlers.
*/
function _seed_handlers() {
function seed_handlers() {
if (empty($this->seeded)) {
foreach ($this->objects() as $key) {
$this->_seed_handler($key);
......@@ -374,6 +378,51 @@ class view extends views_db_object {
return $this->display_handler->render();
}
/**
* To be called externally. Execute the given display, with
* the given arguments.
*/
function execute_display($display_id = NULL, $args = array()) {
// Prepare the view with the information we have.
$this->set_arguments($args);
$this->set_display($display_id);
// Execute the view
if (isset($this->display_handler)) {
return $this->display_handler->execute();
}
}
/**
* Called to get hook_menu information from the view and the
* named display handler.
*/
function execute_hook_menu($display_id = NULL) {
// Prepare the view with the information we have.
$this->set_display($display_id);
// Execute the view
if (isset($this->display_handler)) {
return $this->display_handler->execute_hook_menu();
}
}
/**
* Determine if the given user has access to the view. Note that
* this sets the display handler if it hasn't been.
*/
function access($account) {
if (empty($this->display_handler) && !$this->set_display()) {
return FALSE;
}
if (!$account) {
$account = $GLOBALS['user'];
}
return $this->display_handler->access($account);
}
/**
* Get the view's current title. This can change depending upon how it
* was built.
......@@ -395,7 +444,8 @@ class view extends views_db_object {
/**
* Load a view from the database based upon either vid or name.
*/
function load($arg) {
function load($arg) {
// TODO: Move caching to here, make this a factory method.
$where = (is_numeric($arg) ? "vid = %d" : "name = '%s'");
$data = db_fetch_object(db_query("SELECT * FROM {views_view} WHERE $where", $arg));
if (empty($data)) {
......@@ -408,17 +458,52 @@ class view extends views_db_object {
foreach ($this->objects_all() as $key) {
$this->_load_row($key);
}
$view->loaded = TRUE;
$this->loaded = TRUE;
return TRUE;
}
/**
* Static factory method to load a list of views based upon a $where clause.
*/
function load_views($where, $join = '') {
// TODO: Integrate this in with caching.
$result = db_query("SELECT DISTINCT v.* FROM {views_view} v $join WHERE $where");
$views = array();
// Load all the views.
while ($data = db_fetch_object($result)) {
$view = new view;
$view->load_row($data);
$view->loaded = TRUE;
$views[$view->vid] = $view;
}
// Stop if we didn't get any views.
if (!$views) {
return array();
}
$vids = implode(', ', array_keys($views));
// Now load all the subtables:
foreach (view::objects_all() as $key) {
$object_name = "views_$key";
$result = db_query("SELECT * FROM {$object_name} WHERE vid IN ($vids) ORDER BY vid, position");
while ($data = db_fetch_object($result)) {
$object = new $object_name(FALSE);
$object->load_row($data);
array_push($views[$object->vid]->$key, $object);
}
}
return $views;
}
/**
* Load one of our sub tables.
*/
function _load_row($key) {
$object_name = "views_$key";
$table = $object_name;
$result = db_query("SELECT * FROM {$table} WHERE vid = %d ORDER BY position", $this->vid);
$result = db_query("SELECT * FROM {$object_name} WHERE vid = %d ORDER BY position", $this->vid);
while ($data = db_fetch_object($result)) {
$object = new $object_name(FALSE);
......
<?php
/**
* @file
* Provides views data and handlers for node.module
*/
function node_views_data() {
// Basic table information.
$data['node']['table'] = array(
......@@ -81,6 +85,8 @@ function node_views_data() {
/**
* Field handler to provide simple renderer that allows linking to a node.
*
* @ingroup views_field_handlers
*/
class views_handler_field_node extends views_handler_field {
/**
......@@ -124,6 +130,8 @@ class views_handler_field_node extends views_handler_field {
/**
* Field handler to translate a node type into its readable form.
*
* @ingroup views_field_handlers
*/
class views_handler_field_node_type extends views_handler_field_node {
function render($values) {
......
......@@ -14,3 +14,485 @@ function views_install() {
function views_uninstall() {
drupal_uninstall_schema('views');
}
/**
* Implementation of hook_schema
*/
function views_schema() {
$schema['views_view'] = array(
'description' => t('Stores the general data for a view.'),
'fields' => array(
'vid' => array(
'type' => 'serial',
'unsigned' => TRUE,
'not null' => TRUE,
'description' => t('The view ID of the field, defined by the database.'),
'no export' => TRUE,
),
'name' => array(
'type' => 'varchar',
'length' => '32',
'default' => '',
'not null' => TRUE,
'description' => t('The unique name of the view. This is the primary field views are loaded from, and is used so that views may be internal and not necessarily in the database. May only be alphanumeric characters plus underscores.'),
),
'description' => array(
'type' => 'varchar',
'length' => '255',
'default' => '',
'description' => t('A description of the view for the admin interface.'),
),
'view_php' => array(
'type' => 'blob',
'default' => '',
'description' => t('A chunk of PHP code that can be used to provide modifications to the view prior to building.'),
),
'base_table' => array(
'type' => 'varchar',
'length' => '32',
'default' => '',
'not null' => TRUE,
'description' => t('What table this view is based on, such as node, user, comment, or term.'),
),
'is_cacheable' => array(
'type' => 'int',
'default' => 0,
'size' => 'tiny',
'description' => t('A boolean to indicate whether or not this view may have its query cached.'),
),
),
'primary key' => array('vid'),
'unique key' => array('name' => array('name')),
);
$schema['views_display'] = array(
'description' => t('Stores information about each display attached to a view.'),
'fields' => array(
'vid' => array(
'type' => 'int',
'unsigned' => TRUE,
'not null' => TRUE,
'default' => 0,
'description' => t('The view this display is attached to.'),
'no export' => TRUE,
),
'display_plugin' => array(
'type' => 'varchar',
'length' => '64',
'default' => '',
'not null' => TRUE,
'description' => t('The type of the display. Usually page, block or embed, but is pluggable so may be other things.'),
),
'id' => array(
'type' => 'varchar',
'length' => '64',
'default' => '',
'not null' => TRUE,
'description' => t('An identifier for this display; usually generated from the display_plugin, so should be something like page or page_1 or block_2, etc.'),
),
'access' => array(
'type' => 'varchar',
'length' => 255,
'description' => t('A serialized array describing who can access this display of the view.'),
'serialize' => TRUE,
'serialized default' => 'a:0:{}',
),
'style_plugin' => array(
'type' => 'varchar',
'length' => '64',
'default' => '',
'not null' => TRUE,
'description' => t('The output type of the display. Usually list, table, teasers or nodes, but is pluggable so may be other things.'),
),
'title' => array(
'type' => 'varchar',
'length' => '255',
'default' => '',
'not null' => TRUE,
'description' => t('The title to use for this display.'),
),
'header' => array(
'type' => 'blob',
'description' => t('Text to display for the header.'),
),
'header_format' => array(
'type' => 'int',
'default' => FILTER_FORMAT_DEFAULT,
'description' => t('The output format for the header.'),
),
'header_hide_empty' => array(
'type' => 'int',
'default' => 1,
'size' => 'tiny',
'description' => t('If true, hide the header when the view has no records to display.'),
),
'footer' => array(
'type' => 'blob',
'description' => t('Text to display for the footer.'),
),
'footer_format' => array(
'type' => 'int',
'default' => FILTER_FORMAT_DEFAULT,
'description' => t('The output format for the footer.'),
),
'footer_hide_empty' => array(
'type' => 'int',
'default' => 1,
'size' => 'tiny',
'description' => t('If true, hide the footer when the view has no records to display.'),
),
'empty' => array(
'type' => 'blob',
'description' => t('Text to display if the view produces no records.'),
),
'empty_format' => array(
'type' => 'int',
'default' => FILTER_FORMAT_DEFAULT,
'description' => t('The output format for the empty text.'),
),
'use_pager' => array(
'type' => 'int',
'default' => 1,
'size' => 'tiny',
'description' => t('If true, this display will utilize the pager.'),
),
'url' => array(
'type' => 'varchar',
'length' => '255',
'description' => t('The URL of the display, if applicable.'),
),
'block' => array(
'type' => 'int',
'size' => 'tiny',
'default' => 0,
'description' => t('True if the display responds to hook_block; false otherwise.'),
),
'position' => array(
'type' => 'int',
'default' => 0,
'description' => t('The order in which this display is loaded.'),
),
'display_options' => array(
'type' => 'blob',
'description' => t('A serialized array of options for this display; it contains options that are generally only pertinent to that display plugin type.'),
'serialize' => TRUE,
'serialized default' => 'a:0:{}',
),
'style_options' => array(
'type' => 'blob',
'description' => t('A serialized array of options for this display\'s style plugin; it contains options that are generally only pertinent to that type.'),
'serialize' => TRUE,
'serialized default' => 'a:0:{}',
),
'filter_options' => array(
'type' => 'blob',
'description' => t('A serialized array of options for this display\'s exposed filters.'),
'serialize' => TRUE,
'serialized default' => 'a:0:{}',
),
),
'indexes' => array('vid' => array('vid', 'position')),
);
$schema['views_argument'] = array(
'description' => t('Stores information about each argument attached to a view.'),
'fields' => array(
'vid' => array(
'type' => 'int',
'unsigned' => TRUE,
'not null' => TRUE,
'default' => 0,
'description' => t('The view this display is attached to.'),
'no export' => TRUE,
),
'position' => array(
'type' => 'int',
'default' => 0,
'description' => t('The order in which this information is loaded.'),
),
'tablename' => array(
'type' => 'varchar',
'length' => '255',
'description' => t('The name of the table this field is attached to.'),
),
'field' => array(
'type' => 'varchar',
'length' => '255',
'description' => t('The name of the field.'),
),
'relationship' => array(
'type' => 'varchar',
'length' => '255',
'description' => t('The relationship this field belongs to.'),
),
'type' => array(
'type' => 'varchar',
'length' => '64',
'default' => '',
'not null' => TRUE,
'description' => t('The type of the display. Usually page, block or embed, but is pluggable so may be other things.'),
),
'style' => array(
'type' => 'varchar',
'length' => '64',
'default' => '',
'not null' => TRUE,
'description' => t('The type of the display. Usually page, block or embed, but is pluggable so may be other things.'),
),
'default_action' => array(
'type' => 'varchar',
'length' => '32',
'default' => '',
'not null' => TRUE,
'description' => t('What to do if this argument is not present.'),
),
'title' => array(
'type' => 'varchar',
'length' => '255',
'default' => '',
'not null' => TRUE,
'description' => t('The title to use if this argument is present.'),
),
'wildcard' => array(
'type' => 'varchar',
'length' => '32',
'default' => '*',
'description' => t('What to use for a wildcard in the URL for this argument.'),
),
'wildcard_text' => array(
'type' => 'varchar',
'length' => '64',
'default' => t('All'),
'description' => t('The textual representation of the argument, for use in titles. For example, if the argument is *, the text represnation might be "all".'),
),
'options' => array(
'type' => 'blob',
'description' => t('A serialized array of options for this field.'),
'serialize' => TRUE,
'serialized default' => 'a:0:{}',
),
),
'indexes' => array('vid' => array('vid', 'position')),
);
$schema['views_field'] = array(
'description' => t('Stores information about each field attached to a view.'),
'fields' => array(
'vid' => array(
'type' => 'int',
'unsigned' => TRUE,
'not null' => TRUE,
'default' => 0,
'description' => t('The view this display is attached to.'),