summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDaniel Wehner2010-02-22 12:50:20 (GMT)
committer Daniel Wehner2010-02-22 12:50:20 (GMT)
commitf4dbdb9c79c1395c682eb8dad6a39cf9f0f14130 (patch)
treeca9ae70cd6143edce20ffb94bd0dacc88e14cade
parent5e42d36c1b2446d3f9fa73dc31a9668b891b4aec (diff)
#626704 by DamZ: Build views integration for field.module
-rw-r--r--CHANGELOG.txt1
-rw-r--r--includes/handlers.inc2
-rw-r--r--modules/field.views.inc259
-rw-r--r--modules/field/views_handler_field_field.inc118
-rw-r--r--views.info2
5 files changed, 382 insertions, 0 deletions
diff --git a/CHANGELOG.txt b/CHANGELOG.txt
index 323c55e..2c85fa0 100644
--- a/CHANGELOG.txt
+++ b/CHANGELOG.txt
@@ -100,6 +100,7 @@ Views 3.x-7.x-dev
o by dereine: Test pluggable default argument.
o by dereine: Test exposed forms ui.
o #721388 by JohnAlbin: Replace clear-block class with clearfix class
+ o #626704 by DamZ: Build views integration for field.module
Views 6.x-3.x-dev
o #396380 by merlinofchaos, dereine and dagmar: Initial support for GROUP BY queries!!!!!!!!!!!!
diff --git a/includes/handlers.inc b/includes/handlers.inc
index e36a005..25b0103 100644
--- a/includes/handlers.inc
+++ b/includes/handlers.inc
@@ -1377,6 +1377,8 @@ function comment_views_api() { return views_views_api(); }
function locale_views_api() { return views_views_api(); }
+function field_views_api() { return views_views_api(); }
+
function filter_views_api() { return views_views_api(); }
function node_views_api() { return views_views_api(); }
diff --git a/modules/field.views.inc b/modules/field.views.inc
new file mode 100644
index 0000000..459d74c
--- /dev/null
+++ b/modules/field.views.inc
@@ -0,0 +1,259 @@
+<?php
+// $Id$
+
+/**
+ * @file
+ * Provide Views data and handlers for field.module
+ */
+
+/**
+ * @defgroup views_field_module field.module handlers
+ *
+ * @{
+ */
+
+/**
+ * Implements hook_views_data()
+ */
+function field_views_data() {
+ $data = array();
+ foreach (field_info_fields() as $field) {
+ if ($field['storage']['type'] != 'field_sql_storage') {
+ continue;
+ }
+
+ $module = $field['module'];
+ $result = (array) module_invoke($module, 'field_views_data', $field);
+ drupal_alter('field_views_data', $result, $field);
+
+ if (empty($result)) {
+ $result = field_views_field_default_views_data($field);
+ }
+ if (is_array($result)) {
+ $data = array_merge($data, $result);
+ }
+ }
+
+ return $data;
+}
+
+/**
+ * Default views data implementation for a field.
+ */
+function field_views_field_default_views_data($field) {
+ $field_types = field_info_field_types();
+
+ // Check the field module is available.
+ if (!isset($field_types[$field['type']])) {
+ return;
+ }
+
+ // Note: this should be in the $field object already, see http://drupal.org/node/645926.
+ $field_storage_details = field_sql_storage_field_storage_details($field, NULL);
+ $field += $field_storage_details;
+
+ $data = array();
+
+ $current_table = _field_sql_storage_tablename($field);
+ $revision_table = _field_sql_storage_revision_tablename($field);
+
+ // The list of entity:bundle that this field is used in.
+ $bundles_names = array();
+ $supports_revisions = FALSE;
+
+ // Build the relationships between the field table and the entity tables.
+ foreach ($field['bundles'] as $entity => $bundles) {
+ $entity_info = entity_get_info($entity);
+
+ $data[$current_table]['table']['join'][$entity_info['base table']] = array(
+ 'left_field' => $entity_info['object keys']['id'],
+ 'field' => 'entity_id',
+ 'extra' => array(array('field' => 'etid', 'value' => _field_sql_storage_etid($entity), 'numeric' => TRUE)),
+ );
+
+ if (!empty($entity_info['object keys']['revision']) && !empty($entity_info['revision table'])) {
+ $data[$revision_table]['table']['join'][$entity_info['revision table']] = array(
+ 'left_field' => $entity_info['object keys']['revision'],
+ 'field' => 'revision_id',
+ 'extra' => array(array('field' => 'etid', 'value' => _field_sql_storage_etid($entity), 'numeric' => TRUE)),
+ );
+
+ $supports_revisions = TRUE;
+ }
+
+ foreach ($bundles as $bundle) {
+ $bundles_names[] = t('@entity:@bundle', array('@entity' => $entity, '@bundle' => $bundle));
+ }
+ }
+
+ $tables = array();
+ $tables[FIELD_LOAD_CURRENT] = $current_table;
+ if ($supports_revisions) {
+ $tables[FIELD_LOAD_REVISION] = $revision_table;
+ }
+
+ // Add the field handler for this field.
+ $title_short = $field['field_name'];
+ foreach ($tables as $type => $table) {
+ if ($type == FIELD_LOAD_CURRENT) {
+ $group = t('Fields');
+ $column = 'entity_id';
+ }
+ else {
+ $group = t('Fields (historical data)');
+ $column = 'revision_id';
+ }
+
+ $data[$table][$column] = array(
+ 'group' => $group,
+ 'title' => $title_short,
+ 'title short' => $title_short,
+ 'help' => t('Appears in: @bundles', array('@bundles' => implode(', ', $bundles_names))),
+ );
+ $data[$table][$column]['field'] = array(
+ 'field' => $column,
+ 'table' => $table,
+ 'handler' => 'views_handler_field_field',
+ 'click sortable' => TRUE,
+ 'field_name' => $field['field_name'],
+ 'additional fields' => array('etid'),
+ );
+ }
+
+ foreach ($field['columns'] as $column => $attributes) {
+ $sort = !empty($attributes['sortable']) ? TRUE : FALSE;
+
+ // Identify likely filters and arguments for each column based on field type.
+ switch ($attributes['type']) {
+ case 'int':
+ case 'mediumint':
+ case 'tinyint':
+ case 'bigint':
+ case 'serial':
+ $filter = 'views_handler_filter_numeric';
+ $argument = 'views_handler_argument_numeric';
+ break;
+ case 'numeric':
+ case 'float':
+ $filter = 'views_handler_filter_float';
+ $argument = 'views_handler_argument_numeric';
+ break;
+
+ case 'text':
+ case 'blob':
+ // TODO add markup handlers for these types
+ default:
+ $filter = 'views_handler_filter_string';
+ $argument = 'views_handler_argument_string';
+ break;
+ }
+
+ // Note: we don't have a label available here, because we are at the field
+ // level, not at the instance level.
+ if (count($field['columns']) == 1) {
+ $title = t('@label (!name)', array('@label' => $field['field_name'], '!name' => $field['field_name']));
+ $title_short = $field['field_name'];
+ }
+ else {
+ $title = t('@label (!name) - !column', array('@label' => $field['field_name'], '!name' => $field['field_name'], '!column' => $column));
+ $title_short = t('@label-truncated - !column', array('@label-truncated' => $field['field_name'], '!column' => $column));
+ }
+
+ foreach ($tables as $type => $table) {
+ $group = $type == FIELD_LOAD_CURRENT ? t('Fields') : t('Fields (historical data)');
+ $column_real_name = $field['sql'][$type][$table][$column];
+
+ // Load all the fields from the table by default.
+ $additional_fields = array_values($field['sql'][$type][$table]);
+
+ $data[$table][$column_real_name] = array(
+ 'group' => $group,
+ 'title' => $title,
+ 'title short' => $title_short,
+ 'help' => t('Appears in: @bundles', array('@bundles' => implode(', ', $bundles_names))),
+ );
+
+ $data[$table][$column_real_name]['argument'] = array(
+ 'field' => $column_real_name,
+ 'table' => $table,
+ 'handler' => $argument,
+ 'additional fields' => $additional_fields,
+ 'content_field_name' => $field['field_name'],
+ 'empty field name' => t('<No value>'),
+ );
+ $data[$table][$column_real_name]['filter'] = array(
+ 'field' => $column_real_name,
+ 'table' => $table,
+ 'handler' => $filter,
+ 'additional fields' => $additional_fields,
+ 'content_field_name' => $field['field_name'],
+ 'allow empty' => TRUE,
+ );
+ if (!empty($sort)) {
+ $data[$table][$column_real_name]['sort'] = array(
+ 'field' => $column_real_name,
+ 'table' => $table,
+ 'handler' => 'content_handler_sort',
+ 'additional fields' => $additional_fields,
+ 'content_field_name' => $field['field_name'],
+ );
+ }
+
+ // Expose additional delta column for multiple value fields.
+ if ($field['cardinality'] > 1 || $field['cardinality'] == FIELD_CARDINALITY_UNLIMITED) {
+ $title = t('@label (!name) - delta', array('@label' => $field['field_name'], '!name' => $field['field_name']));
+ $title_short = t('!name - delta', array('@name' => $field['field_name']));
+
+ $data[$table]['delta'] = array(
+ 'group' => $group,
+ 'title' => $title,
+ 'title short' => $title_short,
+ 'help' => t('Delta - Appears in: @bundles', array('@bundles' => implode(', ', $bundles_names))),
+ );
+ $data[$table]['delta']['argument'] = array(
+ 'field' => 'delta',
+ 'table' => $table,
+ 'handler' => 'views_handler_argument_numeric',
+ 'additional fields' => $additional_fields,
+ 'empty field name' => t('<No value>'),
+ );
+ $data[$table]['delta']['filter'] = array(
+ 'field' => 'delta',
+ 'table' => $table,
+ 'handler' => 'views_handler_filter_numeric',
+ 'additional fields' => $additional_fields,
+ 'allow empty' => TRUE,
+ );
+ $data[$table]['delta']['sort'] = array(
+ 'field' => 'delta',
+ 'table' => $table,
+ 'handler' => 'views_handler_sort',
+ 'additional fields' => $additional_fields,
+ );
+ }
+ }
+ }
+
+ return $data;
+}
+
+/**
+ * Implements hook_views_handlers().
+ */
+function field_views_handlers() {
+ return array(
+ 'info' => array(
+ 'path' => drupal_get_path('module', 'views') . '/modules/field',
+ ),
+ 'handlers' => array(
+ 'views_handler_field_field' => array(
+ 'parent' => 'views_handler_field',
+ ),
+ ),
+ );
+}
+
+
+/**
+ * @}
+ */
diff --git a/modules/field/views_handler_field_field.inc b/modules/field/views_handler_field_field.inc
new file mode 100644
index 0000000..9a8e37c
--- /dev/null
+++ b/modules/field/views_handler_field_field.inc
@@ -0,0 +1,118 @@
+<?php
+// $Id$
+
+/**
+ * Helper function: Return an array of formatter options for a field type.
+ *
+ * Borrowed from field_ui.
+ */
+function _field_view_formatter_options($field_type = NULL) {
+ $options = &drupal_static(__FUNCTION__);
+
+ if (!isset($options)) {
+ $field_types = field_info_field_types();
+ $options = array();
+ foreach (field_info_formatter_types() as $name => $formatter) {
+ foreach ($formatter['field types'] as $formatter_field_type) {
+ // Check that the field type exists.
+ if (isset($field_types[$formatter_field_type])) {
+ $options[$formatter_field_type][$name] = $formatter['label'];
+ }
+ }
+ }
+ }
+
+ if ($field_type) {
+ return !empty($options[$field_type]) ? $options[$field_type] : array();
+ }
+ return $options;
+}
+
+/**
+ * A field that displays fields.
+ */
+class views_handler_field_field extends views_handler_field {
+ /**
+ * Called to add the field to a query.
+ */
+ function query() {
+ // TODO: we should try to use the data from the join if possible first.
+ // $join = $this->get_join();
+ // That would avoid joining at all the field table.
+ return parent::query();
+ }
+
+ function option_definition() {
+ $options = parent::option_definition();
+
+ $field = field_info_field($this->definition['field_name']);
+ $field_type = field_info_field_types($field['type']);
+
+ $options['type'] = array(
+ 'default' => $field_type['default_formatter'],
+ 'translatable' => TRUE,
+ );
+
+ return $options;
+ }
+
+ function options_form(&$form, &$form_state) {
+ parent::options_form($form, $form_state);
+
+ $field = field_info_field($this->definition['field_name']);
+ $formatters = _field_view_formatter_options($field['type']);
+
+ $form['type'] = array(
+ '#type' => 'select',
+ '#title' => t('Formatter'),
+ '#options' => $formatters,
+ '#default_value' => $this->options['type'],
+ );
+ }
+
+ function pre_render(&$values) {
+ if (!empty($values) && empty($values[0]->_object)) {
+
+ // OMG, this is *ugly*.
+ $obj_type_map = db_query('SELECT etid, type FROM {field_config_entity_type}')->fetchAllKeyed();
+
+ // Load the full objects.
+ $objects_by_type = array();
+ foreach ($values as $key => $object) {
+ // Derive the entity type. For some field types, etid might be empty.
+ if (isset($object->{$this->aliases['etid']}) && isset($obj_type_map[$object->{$this->aliases['etid']}])) {
+ $obj_type = $obj_type_map[$object->{$this->aliases['etid']}];
+ $entity_id = $object->{$this->field_alias};
+ $objects_by_type[$obj_type][$key] = $entity_id;
+ }
+ }
+
+ // Load the objects.
+ foreach ($objects_by_type as $obj_type => $oids) {
+ $objects = entity_load($obj_type, $oids);
+ foreach ($oids as $key => $entity_id) {
+ if (isset($objects[$entity_id])) {
+ $values[$key]->_obj_type = $obj_type;
+ $values[$key]->_object = $objects[$entity_id];
+ }
+ }
+ }
+ }
+ }
+
+ function render($values) {
+ if (isset($values->_obj_type)) {
+ $field_name = $this->definition['field_name'];
+ $display = array(
+ 'type' => $this->options['type'],
+ 'label' => 'hidden',
+ );
+
+ return drupal_render(field_view_field($values->_obj_type, $values->_object, $field_name, $display));
+ }
+ else {
+ return '';
+ }
+ }
+}
+
diff --git a/views.info b/views.info
index 7388047..3a954bd 100644
--- a/views.info
+++ b/views.info
@@ -88,6 +88,8 @@ files[] = modules/comment/views_handler_sort_ncs_last_comment_name.inc
files[] = modules/comment/views_handler_sort_ncs_last_updated.inc
files[] = modules/comment/views_plugin_row_comment_rss.inc
files[] = modules/comment/views_plugin_row_comment_view.inc
+files[] = modules/field.views.inc
+files[] = modules/field/views_handler_field_field.inc
files[] = modules/locale.views.inc
files[] = modules/locale/views_handler_argument_locale_group.inc
files[] = modules/locale/views_handler_argument_locale_language.inc