first import

This commit is contained in:
Bachir Soussi Chiadmi
2015-04-08 11:40:19 +02:00
commit 1bc61b12ad
8435 changed files with 1582817 additions and 0 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,376 @@
<?php
/**
* @file
* Handles the server side AJAX interactions of Views.
*/
/**
* @defgroup ajax Views AJAX library
* @{
* Handles the server side AJAX interactions of Views.
*/
/**
* Menu callback to load a view via AJAX.
*/
function views_ajax() {
if (isset($_REQUEST['view_name']) && isset($_REQUEST['view_display_id'])) {
$name = $_REQUEST['view_name'];
$display_id = $_REQUEST['view_display_id'];
$args = isset($_REQUEST['view_args']) && $_REQUEST['view_args'] !== '' ? explode('/', $_REQUEST['view_args']) : array();
$path = isset($_REQUEST['view_path']) ? rawurldecode($_REQUEST['view_path']) : NULL;
$dom_id = isset($_REQUEST['view_dom_id']) ? preg_replace('/[^a-zA-Z0-9_-]+/', '-', $_REQUEST['view_dom_id']) : NULL;
$pager_element = isset($_REQUEST['pager_element']) ? intval($_REQUEST['pager_element']) : NULL;
$commands = array();
// Remove all of this stuff from $_GET so it doesn't end up in pagers and tablesort URLs.
foreach (array('view_name', 'view_display_id', 'view_args', 'view_path', 'view_dom_id', 'pager_element', 'view_base_path', 'ajax_html_ids', 'ajax_page_state') as $key) {
if (isset($_GET[$key])) {
unset($_GET[$key]);
}
if (isset($_REQUEST[$key])) {
unset($_REQUEST[$key]);
}
if (isset($_POST[$key])) {
unset($_POST[$key]);
}
}
// Load the view.
$view = views_get_view($name);
if ($view && $view->access($display_id)) {
// Fix 'q' for paging.
if (!empty($path)) {
$_GET['q'] = $path;
}
// Add all $_POST data, because AJAX is always a post and many things,
// such as tablesorts, exposed filters and paging assume $_GET.
$_GET = $_POST + $_GET;
// Overwrite the destination.
// @see drupal_get_destination()
$origin_destination = $path;
$query = drupal_http_build_query($_REQUEST);
if ($query != '') {
$origin_destination .= '?' . $query;
}
$destination = &drupal_static('drupal_get_destination');
$destination = array('destination' => $origin_destination);
// Override the display's pager_element with the one actually used.
if (isset($pager_element)) {
$commands[] = views_ajax_command_scroll_top('.view-dom-id-' . $dom_id);
$view->display[$display_id]->handler->set_option('pager_element', $pager_element);
}
// Reuse the same DOM id so it matches that in Drupal.settings.
$view->dom_id = $dom_id;
$commands[] = ajax_command_replace('.view-dom-id-' . $dom_id, $view->preview($display_id, $args));
}
drupal_alter('views_ajax_data', $commands, $view);
return array('#type' => 'ajax', '#commands' => $commands);
}
}
/**
* Creates a Drupal AJAX 'viewsSetForm' command.
*
* @param $output
* The form to display in the modal.
* @param $title
* The title.
* @param $url
* An optional URL.
*
* @return
* An array suitable for use with the ajax_render() function.
*/
function views_ajax_command_set_form($output, $title, $url = NULL) {
$command = array(
'command' => 'viewsSetForm',
'output' => $output,
'title' => $title,
);
if (isset($url)) {
$command['url'] = $url;
}
return $command;
}
/**
* Creates a Drupal AJAX 'viewsDismissForm' command.
*
* @return
* An array suitable for use with the ajax_render() function.
*/
function views_ajax_command_dismiss_form() {
$command = array(
'command' => 'viewsDismissForm',
);
return $command;
}
/**
* Creates a Drupal AJAX 'viewsHilite' command.
*
* @param $selector
* The selector to highlight
*
* @return
* An array suitable for use with the ajax_render() function.
*/
function views_ajax_command_hilite($selector) {
return array(
'command' => 'viewsHilite',
'selector' => $selector,
);
}
/**
* Creates a Drupal AJAX 'addTab' command.
*
* @param $id
* The DOM ID.
* @param $title
* The title.
* @param $body
* The body.
*
* @return
* An array suitable for use with the ajax_render() function.
*/
function views_ajax_command_add_tab($id, $title, $body) {
$command = array(
'command' => 'viewsAddTab',
'id' => $id,
'title' => $title,
'body' => $body,
);
return $command;
}
/**
* Scroll to top of the current view.
*
* @return
* An array suitable for use with the ajax_render() function.
*/
function views_ajax_command_scroll_top($selector) {
$command = array(
'command' => 'viewsScrollTop',
'selector' => $selector,
);
return $command;
}
/**
* Shows Save and Cancel buttons.
*
* @return
* An array suitable for use with the ajax_render() function.
*/
function views_ajax_command_show_buttons() {
$command = array(
'command' => 'viewsShowButtons',
);
return $command;
}
/**
* Trigger the Views live preview.
*
* @return
* An array suitable for use with the ajax_render() function.
*/
function views_ajax_command_trigger_preview() {
$command = array(
'command' => 'viewsTriggerPreview',
);
return $command;
}
/**
* Replace the page title.
*
* @return
* An array suitable for use with the ajax_render() function.
*/
function views_ajax_command_replace_title($title) {
$command = array(
'command' => 'viewsReplaceTitle',
'title' => $title,
'siteName' => variable_get('site_name', 'Drupal'),
);
return $command;
}
/**
* Return an AJAX error.
*/
function views_ajax_error($message) {
$commands = array();
$commands[] = views_ajax_command_set_form($message, t('Error'));
return $commands;
}
/**
* Wrapper around drupal_build_form to handle some AJAX stuff automatically.
* This makes some assumptions about the client.
*/
function views_ajax_form_wrapper($form_id, &$form_state) {
ctools_include('dependent');
// This won't override settings already in.
$form_state += array(
'rerender' => FALSE,
'no_redirect' => !empty($form_state['ajax']),
'no_cache' => TRUE,
'build_info' => array(
'args' => array(),
),
);
$form = drupal_build_form($form_id, $form_state);
$output = drupal_render($form);
// These forms have the title built in, so set the title here:
if (empty($form_state['ajax']) && !empty($form_state['title'])) {
drupal_set_title($form_state['title']);
drupal_add_css(drupal_get_path('module', 'views_ui') . '/css/views-admin.css');
}
if (!empty($form_state['ajax']) && (empty($form_state['executed']) || !empty($form_state['rerender']))) {
// If the form didn't execute and we're using ajax, build up a
// Ajax command list to execute.
$commands = array();
$display = '';
if ($messages = theme('status_messages')) {
$display = '<div class="views-messages">' . $messages . '</div>';
}
$display .= $output;
$title = empty($form_state['title']) ? '' : $form_state['title'];
if (!empty($form_state['help_topic'])) {
$module = !empty($form_state['help_module']) ? $form_state['help_module'] : 'views';
if (module_exists('advanced_help')) {
$title = theme('advanced_help_topic', array('module' => $module, 'topic' => $form_state['help_topic'])) . $title;
}
}
$url = empty($form_state['url']) ? url($_GET['q'], array('absolute' => TRUE)) : $form_state['url'];
$commands[] = views_ajax_command_set_form($display, $title, $url);
if (!empty($form_state['#section'])) {
$commands[] = views_ajax_command_hilite('.' . drupal_clean_css_identifier($form_state['#section']));
}
return $commands;
}
// These forms have the title built in, so set the title here:
if (empty($form_state['ajax']) && !empty($form_state['title'])) {
drupal_set_title($form_state['title']);
}
return $output;
}
/**
* Page callback for views user autocomplete
*/
function views_ajax_autocomplete_user($string = '') {
// The user enters a comma-separated list of user name. We only autocomplete the last name.
$array = drupal_explode_tags($string);
// Fetch last name
$last_string = trim(array_pop($array));
$matches = array();
if ($last_string != '') {
$prefix = count($array) ? implode(', ', $array) . ', ' : '';
if (strpos('anonymous', strtolower($last_string)) !== FALSE) {
$matches[$prefix . 'Anonymous'] = 'Anonymous';
}
$result = db_select('users', 'u')
->fields('u', array('uid', 'name'))
->condition('u.name', db_like($last_string) . '%', 'LIKE')
->range(0, 10)
->execute()
->fetchAllKeyed();
foreach ($result as $account) {
$n = $account;
// Commas and quotes in terms are special cases, so encode 'em.
if (strpos($account, ',') !== FALSE || strpos($account, '"') !== FALSE) {
$n = '"' . str_replace('"', '""', $account) . '"';
}
$matches[$prefix . $n] = check_plain($account);
}
}
drupal_json_output($matches);
}
/**
* Page callback for views taxonomy autocomplete.
*
* @param $vid
* The vocabulary id of the tags which should be returned.
*
* @param $tags_typed
* The typed string of the user.
*
* @see taxonomy_autocomplete()
*/
function views_ajax_autocomplete_taxonomy($vid, $tags_typed = '') {
// The user enters a comma-separated list of tags. We only autocomplete the last tag.
$tags_typed = drupal_explode_tags($tags_typed);
$tag_last = drupal_strtolower(array_pop($tags_typed));
$matches = array();
if ($tag_last != '') {
$query = db_select('taxonomy_term_data', 't');
$query->addTag('translatable');
$query->addTag('term_access');
// Do not select already entered terms.
if (!empty($tags_typed)) {
$query->condition('t.name', $tags_typed, 'NOT IN');
}
// Select rows that match by term name.
$tags_return = $query
->fields('t', array('tid', 'name'))
->condition('t.vid', $vid)
->condition('t.name', '%' . db_like($tag_last) . '%', 'LIKE')
->range(0, 10)
->execute()
->fetchAllKeyed();
$prefix = count($tags_typed) ? drupal_implode_tags($tags_typed) . ', ' : '';
$term_matches = array();
foreach ($tags_return as $tid => $name) {
$n = $name;
// Term names containing commas or quotes must be wrapped in quotes.
if (strpos($name, ',') !== FALSE || strpos($name, '"') !== FALSE) {
$n = '"' . str_replace('"', '""', $name) . '"';
}
// Add term name to list of matches.
$term_matches[$prefix . $n] = check_plain($name);
}
}
drupal_json_output($term_matches);
}
/**
* @}
*/

View File

@@ -0,0 +1,122 @@
<?php
/**
* @file
* Contains the view analyze tool code.
*
* This tool is a small plugin manager to perform analysis on a view and
* report results to the user. This tool is meant to let modules that
* provide data to Views also help users properly use that data by
* detecting invalid configurations. Views itself comes with only a
* small amount of analysis tools, but more could easily be added either
* by modules or as patches to Views itself.
*/
/**
* Analyze a review and return the results.
*
* @return
* An array of analyze results organized into arrays keyed by 'ok',
* 'warning' and 'error'.
*/
function views_analyze_view(&$view) {
$view->init_display();
$messages = module_invoke_all('views_analyze', $view);
return $messages;
}
/**
* Format the analyze result into a message string.
*
* This is based upon the format of drupal_set_message which uses separate
* boxes for "ok", "warning" and "error".
*/
function views_analyze_format_result($view, $messages) {
if (empty($messages)) {
$messages = array(views_ui_analysis(t('View analysis can find nothing to report.'), 'ok'));
}
$types = array('ok' => array(), 'warning' => array(), 'error' => array());
foreach ($messages as $message) {
if (empty($types[$message['type']])) {
$types[$message['type']] = array();
}
$types[$message['type']][] = $message['message'];
}
$output = '';
foreach ($types as $type => $messages) {
$type .= ' messages';
$message = '';
if (count($messages) > 1) {
$message = theme('item_list', array('items' => $messages));
}
elseif ($messages) {
$message = array_shift($messages);
}
if ($message) {
$output .= "<div class=\"$type\">$message</div>";
}
}
return $output;
}
/**
* Format an analysis message.
*
* This tool should be called by any module responding to the analyze hook
* to properly format the message. It is usually used in the form:
* @code
* $ret[] = views_ui_analysis(t('This is the message'), 'ok');
* @endcode
*
* The 'ok' status should be used to provide information about things
* that are acceptable. In general analysis isn't interested in 'ok'
* messages, but instead the 'warning', which is a category for items
* that may be broken unless the user knows what he or she is doing,
* and 'error' for items that are definitely broken are much more useful.
*
* @param $messages
* The message to report.
* @param $type
* The type of message. This should be "ok", "warning" or "error". Other
* values can be used but how they are treated by the output routine
* is undefined.
*/
function views_ui_analysis($message, $type = 'error') {
return array('message' => $message, 'type' => $type);
}
/**
* Implements hook_views_analyze().
*
* This is the basic views analysis that checks for very minimal problems.
* There are other analysis tools in core specific sections, such as
* node.views.inc as well.
*/
function views_ui_views_analyze($view) {
$ret = array();
// Check for something other than the default display:
if (count($view->display) < 2) {
$ret[] = views_ui_analysis(t('This view has only a default display and therefore will not be placed anywhere on your site; perhaps you want to add a page or a block display.'), 'warning');
}
// You can give a page display the same path as an alias existing in the
// system, so the alias will not work anymore. Report this to the user,
// because he probably wanted something else.
foreach ($view->display as $id => $display) {
if (empty($display->handler)) {
continue;
}
if ($display->handler->has_path() && $path = $display->handler->get_option('path')) {
$normal_path = drupal_get_normal_path($path);
if ($path != $normal_path) {
$ret[] = views_ui_analysis(t('You have configured display %display with a path which is an path alias as well. This might lead to unwanted effects so better use an internal path.', array('%display' => $display->display_title)), 'warning');
}
}
}
return $ret;
}

View File

@@ -0,0 +1,359 @@
<?php
/**
* @file
* Provides the basic object definitions used by plugins and handlers.
*/
/**
* Basic definition for many views objects.
*/
class views_object {
/**
* Except for displays, options for the object will be held here.
*/
var $options = array();
/**
* The top object of a view.
*
* @var view
*/
var $view = NULL;
/**
* Handler's definition
*
* @var array
*/
var $definition;
/**
* Information about options for all kinds of purposes will be held here.
* @code
* 'option_name' => array(
* - 'default' => default value,
* - 'translatable' => (optional) TRUE/FALSE (wrap in t() on export if true),
* - 'contains' => (optional) array of items this contains, with its own
* defaults, etc. If contains is set, the default will be ignored and
* assumed to be array().
* - 'bool' => (optional) TRUE/FALSE Is the value a boolean value. This will
* change the export format to TRUE/FALSE instead of 1/0.
* - 'export' => (optional) FALSE or a callback for special export handling
* if necessary.
* - 'unpack_translatable' => (optional) callback for special handling for
* translating data within the option, if necessary.
* ),
*
* @return array
* Returns the options of this handler/plugin.
*
* @see views_object::export_option()
* @see views_object::export_option_always()
* @see views_object::unpack_translatable()
*/
function option_definition() { return array(); }
/**
* Views handlers use a special construct function so that we can more
* easily construct them with variable arguments.
*/
function construct() { $this->set_default_options(); }
/**
* Set default options on this object. Called by the constructor in a
* complex chain to deal with backward compatibility.
*
* @deprecated since views2
*/
function options(&$options) { }
/**
* Set default options.
* For backward compatibility, it sends the options array; this is a
* feature that will likely disappear at some point.
*/
function set_default_options() {
$this->_set_option_defaults($this->options, $this->option_definition());
// Retained for complex defaults plus backward compatibility.
$this->options($this->options);
}
function _set_option_defaults(&$storage, $options, $level = 0) {
foreach ($options as $option => $definition) {
if (isset($definition['contains']) && is_array($definition['contains'])) {
$storage[$option] = array();
$this->_set_option_defaults($storage[$option], $definition['contains'], $level++);
}
elseif (!empty($definition['translatable']) && !empty($definition['default'])) {
$storage[$option] = t($definition['default']);
}
else {
$storage[$option] = isset($definition['default']) ? $definition['default'] : NULL;
}
}
}
/**
* Unpack options over our existing defaults, drilling down into arrays
* so that defaults don't get totally blown away.
*/
function unpack_options(&$storage, $options, $definition = NULL, $all = TRUE, $check = TRUE, $localization_keys = array()) {
if ($check && !is_array($options)) {
return;
}
if (!isset($definition)) {
$definition = $this->option_definition();
}
if (!empty($this->view)) {
// Ensure we have a localization plugin.
$this->view->init_localization();
// Set up default localization keys. Handlers and such set this for us
if (empty($localization_keys) && isset($this->localization_keys)) {
$localization_keys = $this->localization_keys;
}
// but plugins don't because there isn't a common init() these days.
else if (!empty($this->is_plugin)) {
if ($this->plugin_type != 'display') {
$localization_keys = array($this->view->current_display);
$localization_keys[] = $this->plugin_type;
}
}
}
foreach ($options as $key => $value) {
if (is_array($value)) {
// Ignore arrays with no definition.
if (!$all && empty($definition[$key])) {
continue;
}
if (!isset($storage[$key]) || !is_array($storage[$key])) {
$storage[$key] = array();
}
// If we're just unpacking our known options, and we're dropping an
// unknown array (as might happen for a dependent plugin fields) go
// ahead and drop that in.
if (!$all && isset($definition[$key]) && !isset($definition[$key]['contains'])) {
$storage[$key] = $value;
continue;
}
$this->unpack_options($storage[$key], $value, isset($definition[$key]['contains']) ? $definition[$key]['contains'] : array(), $all, FALSE, array_merge($localization_keys, array($key)));
}
// Don't localize strings during editing. When editing, we need to work with
// the original data, not the translated version.
else if (empty($this->view->editing) && !empty($definition[$key]['translatable']) && !empty($value) || !empty($definition['contains'][$key]['translatable']) && !empty($value)) {
if (!empty($this->view) && $this->view->is_translatable()) {
// Allow other modules to make changes to the string before it's
// sent for translation.
// The $keys array is built from the view name, any localization keys
// sent in, and the name of the property being processed.
$format = NULL;
if (isset($definition[$key]['format_key']) && isset($options[$definition[$key]['format_key']])) {
$format = $options[$definition[$key]['format_key']];
}
$translation_data = array(
'value' => $value,
'format' => $format,
'keys' => array_merge(array($this->view->name), $localization_keys, array($key)),
);
$storage[$key] = $this->view->localization_plugin->translate($translation_data);
}
// Otherwise, this is a code-based string, so we can use t().
else {
$storage[$key] = t($value);
}
}
else if ($all || !empty($definition[$key])) {
$storage[$key] = $value;
}
}
}
/**
* Let the handler know what its full definition is.
*/
function set_definition($definition) {
$this->definition = $definition;
if (isset($definition['field'])) {
$this->real_field = $definition['field'];
}
}
function destroy() {
if (isset($this->view)) {
unset($this->view);
}
if (isset($this->display)) {
unset($this->display);
}
if (isset($this->query)) {
unset($this->query);
}
}
function export_options($indent, $prefix) {
$output = '';
foreach ($this->option_definition() as $option => $definition) {
$output .= $this->export_option($indent, $prefix, $this->options, $option, $definition, array());
}
return $output;
}
function export_option($indent, $prefix, $storage, $option, $definition, $parents) {
// Do not export options for which we have no settings.
if (!isset($storage[$option])) {
return;
}
if (isset($definition['export'])) {
if ($definition['export'] === FALSE) {
return;
}
// Special handling for some items
if (method_exists($this, $definition['export'])) {
return $this->{$definition['export']}($indent, $prefix, $storage, $option, $definition, $parents);
}
}
// Add the current option to the parents tree.
$parents[] = $option;
$output = '';
// If it has child items, export those separately.
if (isset($definition['contains'])) {
foreach ($definition['contains'] as $sub_option => $sub_definition) {
$output .= $this->export_option($indent, $prefix, $storage[$option], $sub_option, $sub_definition, $parents);
}
}
// Otherwise export just this item.
else {
$default = isset($definition['default']) ? $definition['default'] : NULL;
$value = $storage[$option];
if (isset($definition['bool'])) {
$value = (bool) $value;
}
if ($value !== $default) {
$output .= $indent . $prefix . "['" . implode("']['", $parents) . "'] = ";
if (isset($definition['bool'])) {
$output .= empty($storage[$option]) ? 'FALSE' : 'TRUE';
}
else {
$output .= views_var_export($storage[$option], $indent);
}
$output .= ";\n";
}
}
return $output;
}
/**
* Always exports the option, regardless of the default value.
*/
function export_option_always($indent, $prefix, $storage, $option, $definition, $parents) {
// If there is no default, the option will always be exported.
unset($definition['default']);
// Unset our export method to prevent recursion.
unset($definition['export']);
return $this->export_option($indent, $prefix, $storage, $option, $definition, $parents);
}
/**
* Unpacks each handler to store translatable texts.
*/
function unpack_translatables(&$translatable, $parents = array()) {
foreach ($this->option_definition() as $option => $definition) {
$this->unpack_translatable($translatable, $this->options, $option, $definition, $parents, array());
}
}
/**
* Unpack a single option definition.
*
* This function run's through all suboptions recursive.
*
* @param $translatable
* Stores all available translatable items.
* @param $storage
* @param $option
* @param $definition
* @param $parents
* @param $keys
*/
function unpack_translatable(&$translatable, $storage, $option, $definition, $parents, $keys = array()) {
// Do not export options for which we have no settings.
if (!isset($storage[$option])) {
return;
}
// Special handling for some items
if (isset($definition['unpack_translatable']) && method_exists($this, $definition['unpack_translatable'])) {
return $this->{$definition['unpack_translatable']}($translatable, $storage, $option, $definition, $parents, $keys);
}
if (isset($definition['translatable'])) {
if ($definition['translatable'] === FALSE) {
return;
}
}
// Add the current option to the parents tree.
$parents[] = $option;
// If it has child items, unpack those separately.
if (isset($definition['contains'])) {
foreach ($definition['contains'] as $sub_option => $sub_definition) {
$translation_keys = array_merge($keys, array($sub_option));
$this->unpack_translatable($translatable, $storage[$option], $sub_option, $sub_definition, $parents, $translation_keys);
}
}
// @todo Figure out this double definition stuff.
$options = $storage[$option];
if (is_array($options)) {
foreach ($options as $key => $value) {
$translation_keys = array_merge($keys, array($key));
if (is_array($value)) {
$this->unpack_translatable($translatable, $options, $key, $definition, $parents, $translation_keys);
}
else if (!empty($definition[$key]['translatable']) && !empty($value)) {
// Build source data and add to the array
$format = NULL;
if (isset($definition['format_key']) && isset($options[$definition['format_key']])) {
$format = $options[$definition['format_key']];
}
$translatable[] = array(
'value' => $value,
'keys' => $translation_keys,
'format' => $format,
);
}
}
}
else if (!empty($definition['translatable']) && !empty($options)) {
$value = $options;
// Build source data and add to the array
$format = NULL;
if (isset($definition['format_key']) && isset($options[$definition['format_key']])) {
$format = $options[$definition['format_key']];
}
$translatable[] = array(
'value' => $value,
'keys' => isset($translation_keys) ? $translation_keys : $parents,
'format' => $format,
);
}
}
}

View File

@@ -0,0 +1,193 @@
<?php
/**
* @file
* Load Views' data so that it knows what is available to build queries from.
*/
/**
* Fetch Views' data from the cache
*
* @param $move
* Under certain circumstances it makes sense to not get the moved table, but the old one.
* One example is views_get_handler.
*/
function _views_fetch_data($table = NULL, $move = TRUE, $reset = FALSE) {
$cache = &drupal_static(__FUNCTION__ . '_cache');
$recursion_protection = &drupal_static(__FUNCTION__ . '_recursion_protected');
$fully_loaded = &drupal_static(__FUNCTION__ . '_fully_loaded');
if ($reset) {
$cache = NULL;
$fully_loaded = FALSE;
}
if ($table) {
if (!isset($cache[$table])) {
$cid = 'views_data:' . $table;
$data = views_cache_get($cid, TRUE);
if (!empty($data->data)) {
$cache[$table] = $data->data;
}
else {
// No cache entry, rebuild.
$cache = _views_fetch_data_build();
$fully_loaded = TRUE;
}
}
if (isset($cache[$table])) {
if (isset($cache[$table]['moved to']) && $move) {
$moved_table = $cache[$table]['moved to'];
if (!empty($recursion_protection[$table])) {
// recursion detected!
return NULL;
}
$recursion_protection[$table] = TRUE;
$data = _views_fetch_data($moved_table);
$recursion_protection = array();
return $data;
}
return $cache[$table];
}
}
else {
if (!$fully_loaded) {
$data = views_cache_get('views_data', TRUE);
if (!empty($data->data)) {
$cache = $data->data;
}
if (empty($cache)) {
$cache = _views_fetch_data_build();
}
$fully_loaded = TRUE;
}
return $cache;
}
// Return an empty array if there is no match.
return array();
}
/**
* Build and set the views data cache if empty.
*/
function _views_fetch_data_build() {
views_include_handlers();
$cache = module_invoke_all('views_data');
foreach (module_implements('views_data_alter') as $module) {
$function = $module . '_views_data_alter';
$function($cache);
}
_views_data_process_entity_types($cache);
// Keep a record with all data.
views_cache_set('views_data', $cache, TRUE);
// Save data in seperate cache entries.
foreach ($cache as $key => $data) {
$cid = 'views_data:' . $key;
views_cache_set($cid, $data, TRUE);
}
return $cache;
}
/**
* Links tables having an 'entity type' specified to the respective generic entity-type tables.
*/
function _views_data_process_entity_types(&$data) {
foreach ($data as $table_name => $table_info) {
// Add in a join from the entity-table if an entity-type is given.
if (!empty($table_info['table']['entity type'])) {
$entity_table = 'views_entity_' . $table_info['table']['entity type'];
$data[$entity_table]['table']['join'][$table_name] = array(
'left_table' => $table_name,
);
$data[$entity_table]['table']['entity type'] = $table_info['table']['entity type'];
// Copy over the default table group if we have none yet.
if (!empty($table_info['table']['group']) && empty($data[$entity_table]['table']['group'])) {
$data[$entity_table]['table']['group'] = $table_info['table']['group'];
}
}
}
}
/**
* Fetch the plugin data from cache.
*/
function _views_fetch_plugin_data($type = NULL, $plugin = NULL, $reset = FALSE) {
static $cache = NULL;
if (!isset($cache) || $reset) {
$start = microtime(TRUE);
views_include('plugins');
views_include_handlers();
$cache = views_discover_plugins();
}
if (!$type && !$plugin) {
return $cache;
}
elseif (!$plugin) {
// Not in the if above so the else below won't run
if (isset($cache[$type])) {
return $cache[$type];
}
}
elseif (isset($cache[$type][$plugin])) {
return $cache[$type][$plugin];
}
// Return an empty array if there is no match.
return array();
}
/**
* Set a cached item in the views cache.
*
* This is just a convenience wrapper around cache_set().
*
* @param $cid
* The cache ID of the data to store.
* @param $data
* The data to store in the cache. Complex data types will be automatically serialized before insertion.
* Strings will be stored as plain text and not serialized.
* @param $use_language
* If TRUE, the data will be cached specific to the currently active language.
*/
function views_cache_set($cid, $data, $use_language = FALSE) {
global $language;
if (variable_get('views_skip_cache', FALSE)) {
return;
}
if ($use_language) {
$cid .= ':' . $language->language;
}
cache_set($cid, $data, 'cache_views');
}
/**
* Return data from the persistent views cache.
*
* This is just a convenience wrapper around cache_get().
*
* @param int $cid
* The cache ID of the data to retrieve.
* @param bool $use_language
* If TRUE, the data will be requested specific to the currently active language.
*
* @return stdClass|bool
* The cache or FALSE on failure.
*/
function views_cache_get($cid, $use_language = FALSE) {
global $language;
if (variable_get('views_skip_cache', FALSE)) {
return FALSE;
}
if ($use_language) {
$cid .= ':' . $language->language;
}
return cache_get($cid, 'cache_views');
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,584 @@
<?php
/**
* @file
* Built in plugins for Views output handling.
*/
// @todo: Remove this once update.php can use the registry
views_include('base');
/**
* Implements hook_views_plugins().
*/
function views_views_plugins() {
$js_path = drupal_get_path('module', 'ctools') . '/js';
$plugins = array(
// display, style, row, argument default, argument validator and access.
'display' => array(
// Default settings for all display plugins.
'default' => array(
'title' => t('Master'),
'help' => t('Default settings for this view.'),
'handler' => 'views_plugin_display_default',
'theme' => 'views_view',
'no ui' => TRUE,
'no remove' => TRUE,
'js' => array('misc/form.js', 'misc/collapse.js', 'misc/textarea.js', 'misc/tabledrag.js', 'misc/autocomplete.js', "$js_path/dependent.js"),
'use ajax' => TRUE,
'use pager' => TRUE,
'use more' => TRUE,
'accept attachments' => TRUE,
'help topic' => 'display-default',
),
'page' => array(
'title' => t('Page'),
'help' => t('Display the view as a page, with a URL and menu links.'),
'handler' => 'views_plugin_display_page',
'theme' => 'views_view',
'uses hook menu' => TRUE,
'contextual links locations' => array('page'),
'use ajax' => TRUE,
'use pager' => TRUE,
'use more' => TRUE,
'accept attachments' => TRUE,
'admin' => t('Page'),
'help topic' => 'display-page',
),
'block' => array(
'title' => t('Block'),
'help' => t('Display the view as a block.'),
'handler' => 'views_plugin_display_block',
'theme' => 'views_view',
'uses hook block' => TRUE,
'contextual links locations' => array('block'),
'use ajax' => TRUE,
'use pager' => TRUE,
'use more' => TRUE,
'accept attachments' => TRUE,
'admin' => t('Block'),
'help topic' => 'display-block',
),
'attachment' => array(
'title' => t('Attachment'),
'help' => t('Attachments added to other displays to achieve multiple views in the same view.'),
'handler' => 'views_plugin_display_attachment',
'theme' => 'views_view',
'contextual links locations' => array(),
'use ajax' => TRUE,
'use pager' => FALSE,
'use more' => TRUE,
'accept attachments' => FALSE,
'help topic' => 'display-attachment',
),
'feed' => array(
'title' => t('Feed'),
'help' => t('Display the view as a feed, such as an RSS feed.'),
'handler' => 'views_plugin_display_feed',
'uses hook menu' => TRUE,
'use ajax' => FALSE,
'use pager' => FALSE,
'accept attachments' => FALSE,
'admin' => t('Feed'),
'help topic' => 'display-feed',
),
'embed' => array(
'title' => t('Embed'),
'help' => t('Provide a display which can be embedded using the views api.'),
'handler' => 'views_plugin_display_embed',
'theme' => 'views_view',
'uses hook menu' => FALSE,
'use ajax' => TRUE,
'use pager' => TRUE,
'accept attachments' => FALSE,
'admin' => t('Embed'),
'no ui' => !variable_get('views_ui_display_embed', FALSE),
),
),
'display_extender' => array(
// Default settings for all display_extender plugins.
'default' => array(
'title' => t('Empty display extender'),
'help' => t('Default settings for this view.'),
'handler' => 'views_plugin_display_extender',
// You can force the plugin to be enabled
'enabled' => FALSE,
'no ui' => TRUE,
),
),
'style' => array(
// Default settings for all style plugins.
'default' => array(
'title' => t('Unformatted list'),
'help' => t('Displays rows one after another.'),
'handler' => 'views_plugin_style_default',
'theme' => 'views_view_unformatted',
'uses row plugin' => TRUE,
'uses row class' => TRUE,
'uses grouping' => TRUE,
'uses options' => TRUE,
'type' => 'normal',
'help topic' => 'style-unformatted',
),
'list' => array(
'title' => t('HTML list'),
'help' => t('Displays rows as an HTML list.'),
'handler' => 'views_plugin_style_list',
'theme' => 'views_view_list',
'uses row plugin' => TRUE,
'uses row class' => TRUE,
'uses options' => TRUE,
'type' => 'normal',
'help topic' => 'style-list',
),
'grid' => array(
'title' => t('Grid'),
'help' => t('Displays rows in a grid.'),
'handler' => 'views_plugin_style_grid',
'theme' => 'views_view_grid',
'uses fields' => FALSE,
'uses row plugin' => TRUE,
'uses row class' => TRUE,
'uses options' => TRUE,
'type' => 'normal',
'help topic' => 'style-grid',
),
'table' => array(
'title' => t('Table'),
'help' => t('Displays rows in a table.'),
'handler' => 'views_plugin_style_table',
'theme' => 'views_view_table',
'uses row plugin' => FALSE,
'uses row class' => TRUE,
'uses fields' => TRUE,
'uses options' => TRUE,
'type' => 'normal',
'help topic' => 'style-table',
),
'default_summary' => array(
'title' => t('List'),
'help' => t('Displays the default summary as a list.'),
'handler' => 'views_plugin_style_summary',
'theme' => 'views_view_summary',
'type' => 'summary', // only shows up as a summary style
'uses options' => TRUE,
'help topic' => 'style-summary',
),
'unformatted_summary' => array(
'title' => t('Unformatted'),
'help' => t('Displays the summary unformatted, with option for one after another or inline.'),
'handler' => 'views_plugin_style_summary_unformatted',
'theme' => 'views_view_summary_unformatted',
'type' => 'summary', // only shows up as a summary style
'uses options' => TRUE,
'help topic' => 'style-summary-unformatted',
),
'rss' => array(
'title' => t('RSS Feed'),
'help' => t('Generates an RSS feed from a view.'),
'handler' => 'views_plugin_style_rss',
'theme' => 'views_view_rss',
'uses row plugin' => TRUE,
'uses options' => TRUE,
'type' => 'feed',
'help topic' => 'style-rss',
),
),
'row' => array(
'fields' => array(
'title' => t('Fields'),
'help' => t('Displays the fields with an optional template.'),
'handler' => 'views_plugin_row_fields',
'theme' => 'views_view_fields',
'uses fields' => TRUE,
'uses options' => TRUE,
'type' => 'normal',
'help topic' => 'style-row-fields',
),
'rss_fields' => array(
'title' => t('Fields'),
'help' => t('Display fields as RSS items.'),
'handler' => 'views_plugin_row_rss_fields',
'theme' => 'views_view_row_rss',
'uses fields' => TRUE,
'uses options' => TRUE,
'type' => 'feed',
'help topic' => 'style-row-fields',
),
),
'argument default' => array(
'parent' => array(
'no ui' => TRUE,
'handler' => 'views_plugin_argument_default',
'parent' => '',
),
'fixed' => array(
'title' => t('Fixed value'),
'handler' => 'views_plugin_argument_default_fixed',
),
'php' => array(
'title' => t('PHP Code'),
'handler' => 'views_plugin_argument_default_php',
),
'raw' => array(
'title' => t('Raw value from URL'),
'handler' => 'views_plugin_argument_default_raw',
),
),
'argument validator' => array(
'php' => array(
'title' => t('PHP Code'),
'handler' => 'views_plugin_argument_validate_php',
),
'numeric' => array(
'title' => t('Numeric'),
'handler' => 'views_plugin_argument_validate_numeric',
),
),
'access' => array(
'none' => array(
'title' => t('None'),
'help' => t('Will be available to all users.'),
'handler' => 'views_plugin_access_none',
'help topic' => 'access-none',
),
'role' => array(
'title' => t('Role'),
'help' => t('Access will be granted to users with any of the specified roles.'),
'handler' => 'views_plugin_access_role',
'uses options' => TRUE,
'help topic' => 'access-role',
),
'perm' => array(
'title' => t('Permission'),
'help' => t('Access will be granted to users with the specified permission string.'),
'handler' => 'views_plugin_access_perm',
'uses options' => TRUE,
'help topic' => 'access-perm',
),
),
'query' => array(
'parent' => array(
'no ui' => TRUE,
'handler' => 'views_plugin_query',
'parent' => '',
),
'views_query' => array(
'title' => t('SQL Query'),
'help' => t('Query will be generated and run using the Drupal database API.'),
'handler' => 'views_plugin_query_default'
),
),
'cache' => array(
'parent' => array(
'no ui' => TRUE,
'handler' => 'views_plugin_cache',
'parent' => '',
),
'none' => array(
'title' => t('None'),
'help' => t('No caching of Views data.'),
'handler' => 'views_plugin_cache_none',
'help topic' => 'cache-none',
),
'time' => array(
'title' => t('Time-based'),
'help' => t('Simple time-based caching of data.'),
'handler' => 'views_plugin_cache_time',
'uses options' => TRUE,
'help topic' => 'cache-time',
),
),
'exposed_form' => array(
'parent' => array(
'no ui' => TRUE,
'handler' => 'views_plugin_exposed_form',
'parent' => '',
),
'basic' => array(
'title' => t('Basic'),
'help' => t('Basic exposed form'),
'handler' => 'views_plugin_exposed_form_basic',
'uses options' => TRUE,
'help topic' => 'exposed-form-basic',
),
'input_required' => array(
'title' => t('Input required'),
'help' => t('An exposed form that only renders a view if the form contains user input.'),
'handler' => 'views_plugin_exposed_form_input_required',
'uses options' => TRUE,
'help topic' => 'exposed-form-input-required',
),
),
'pager' => array(
'parent' => array(
'no ui' => TRUE,
'handler' => 'views_plugin_pager',
'parent' => '',
),
'none' => array(
'title' => t('Display all items'),
'help' => t("Display all items that this view might find"),
'handler' => 'views_plugin_pager_none',
'help topic' => 'pager-none',
'uses options' => TRUE,
'type' => 'basic',
),
'some' => array(
'title' => t('Display a specified number of items'),
'help' => t('Display a limited number items that this view might find.'),
'handler' => 'views_plugin_pager_some',
'help topic' => 'pager-some',
'uses options' => TRUE,
'type' => 'basic',
),
'full' => array(
'title' => t('Paged output, full pager'),
'short title' => t('Full'),
'help' => t('Paged output, full Drupal style'),
'handler' => 'views_plugin_pager_full',
'help topic' => 'pager-full',
'uses options' => TRUE,
),
'mini' => array(
'title' => t('Paged output, mini pager'),
'short title' => t('Mini'),
'help' => t('Use the mini pager output.'),
'handler' => 'views_plugin_pager_mini',
'help topic' => 'pager-mini',
'uses options' => TRUE,
'parent' => 'full',
),
),
'localization' => array(
'parent' => array(
'no ui' => TRUE,
'handler' => 'views_plugin_localization',
'parent' => '',
),
'none' => array(
'title' => t('None'),
'help' => t('Do not pass admin strings for translation.'),
'handler' => 'views_plugin_localization_none',
'help topic' => 'localization-none',
),
'core' => array(
'title' => t('Core'),
'help' => t("Use Drupal core t() function. Not recommended, as it doesn't support updates to existing strings."),
'handler' => 'views_plugin_localization_core',
'help topic' => 'localization-core',
),
),
);
// Add a help message pointing to the i18views module if it is not present.
if (!module_exists('i18nviews')) {
$plugins['localization']['core']['help'] .= ' ' . t('If you need to translate Views labels into other languages, consider installing the <a href="!path">Internationalization</a> package\'s Views translation module.', array('!path' => url('http://drupal.org/project/i18n', array('absolute' => TRUE))));
}
if (module_invoke('ctools', 'api_version', '1.3')) {
$plugins['style']['jump_menu_summary'] = array(
'title' => t('Jump menu'),
'help' => t('Puts all of the results into a select box and allows the user to go to a different page based upon the results.'),
'handler' => 'views_plugin_style_summary_jump_menu',
'theme' => 'views_view_summary_jump_menu',
'type' => 'summary', // only shows up as a summary style
'uses options' => TRUE,
'help topic' => 'style-summary-jump-menu',
);
$plugins['style']['jump_menu'] = array(
'title' => t('Jump menu'),
'help' => t('Puts all of the results into a select box and allows the user to go to a different page based upon the results.'),
'handler' => 'views_plugin_style_jump_menu',
'theme' => 'views_view_jump_menu',
'uses row plugin' => TRUE,
'uses fields' => TRUE,
'uses options' => TRUE,
'type' => 'normal',
'help topic' => 'style-jump-menu',
);
}
return $plugins;
}
/**
* Builds and return a list of all plugins available in the system.
*
* @return Nested array of plugins, grouped by type.
*/
function views_discover_plugins() {
$cache = array('display' => array(), 'style' => array(), 'row' => array(), 'argument default' => array(), 'argument validator' => array(), 'access' => array(), 'cache' => array(), 'exposed_form' => 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') {
$theme_path = drupal_get_path('module', $module_dir) . '/theme';
$theme_file = 'theme.inc';
$path = drupal_get_path('module', $module_dir) . '/plugins';
}
else {
$theme_path = $path = drupal_get_path('module', $module_dir);
$theme_file = "$module.views.inc";
}
foreach ($result as $type => $info) {
if ($type == 'module') {
continue;
}
foreach ($info as $plugin => $def) {
$def['module'] = $module_dir;
if (!isset($def['theme path'])) {
$def['theme path'] = $theme_path;
}
if (!isset($def['theme file'])) {
$def['theme file'] = $theme_file;
}
if (!isset($def['path'])) {
$def['path'] = $path;
}
if (!isset($def['file'])) {
$def['file'] = $def['handler'] . '.inc';
}
if (!isset($def['parent'])) {
$def['parent'] = 'parent';
}
// Set the internal name to be able to read it out later.
$def['name'] = $plugin;
// merge the new data in
$cache[$type][$plugin] = $def;
}
}
}
// Let other modules modify the plugins.
drupal_alter('views_plugins', $cache);
return $cache;
}
/**
* Abstract base class to provide interface common to all plugins.
*/
class views_plugin extends views_object {
/**
* The top object of a view.
*
* @var view
*/
var $view = NULL;
/**
* The current used views display.
*
* @var views_display
*/
var $display = NULL;
/**
* The plugin type of this plugin, for example style or query.
*/
var $plugin_type = NULL;
/**
* The plugin name of this plugin, for example table or full.
*/
var $plugin_name = NULL;
/**
* Init will be called after construct, when the plugin is attached to a
* view and a display.
*/
/**
* Provide a form to edit options for this plugin.
*/
function options_form(&$form, &$form_state) {
// Some form elements belong in a fieldset for presentation, but can't
// be moved into one because of the form_state['values'] hierarchy. Those
// elements can add a #fieldset => 'fieldset_name' property, and they'll
// be moved to their fieldset during pre_render.
$form['#pre_render'][] = 'views_ui_pre_render_add_fieldset_markup';
}
/**
* Validate the options form.
*/
function options_validate(&$form, &$form_state) { }
/**
* Handle any special handling on the validate form.
*/
function options_submit(&$form, &$form_state) { }
/**
* Add anything to the query that we might need to.
*/
function query() { }
/**
* Provide a full list of possible theme templates used by this style.
*/
function theme_functions() {
return views_theme_functions($this->definition['theme'], $this->view, $this->display);
}
/**
* Provide a list of additional theme functions for the theme information page
*/
function additional_theme_functions() {
$funcs = array();
if (!empty($this->definition['additional themes'])) {
foreach ($this->definition['additional themes'] as $theme => $type) {
$funcs[] = views_theme_functions($theme, $this->view, $this->display);
}
}
return $funcs;
}
/**
* Validate that the plugin is correct and can be saved.
*
* @return
* An array of error strings to tell the user what is wrong with this
* plugin.
*/
function validate() { return array(); }
/**
* Returns the summary of the settings in the display.
*/
function summary_title() {
return t('Settings');
}
/**
* Return the human readable name of the display.
*
* This appears on the ui beside each plugin and beside the settings link.
*/
function plugin_title() {
if (isset($this->definition['short title'])) {
return check_plain($this->definition['short title']);
}
return check_plain($this->definition['title']);
}
}
/**
* Get enabled display extenders.
*/
function views_get_enabled_display_extenders() {
$enabled = array_filter(variable_get('views_display_extenders', array()));
$options = views_fetch_plugin_names('display_extender');
foreach ($options as $name => $plugin) {
$enabled[$name] = $name;
}
return $enabled;
}

File diff suppressed because it is too large Load Diff