diff --git a/lib/Drupal/views/Plugin/views/entity_controller/DefaultEntityController.php b/lib/Drupal/views/Plugin/views/entity_controller/DefaultEntityController.php new file mode 100644 index 0000000..3af8dda --- /dev/null +++ b/lib/Drupal/views/Plugin/views/entity_controller/DefaultEntityController.php @@ -0,0 +1,17 @@ +configuration = $configuration; + $this->plugin_id = $plugin_id; + $this->entity_type = $entity_type; + $this->entity_info = entity_get_info($entity_type); + } + + + /** + * Defines the result for hook_views_data(). + * + * @return array + * @todo + */ + public function viewsData() { + $data = array(); + $this->relationships = array(); + + if (!empty($this->entity_info['base table'])) { + $table = $this->entity_info['base table']; + + $table = $this->entity_info['base table']; + // Define the base group of this table. Fields that don't + // have a group defined will go into this field by default. + $data[$table]['table']['group'] = drupal_ucfirst($this->entity_info['label']); + $data[$table]['table']['entity type'] = $this->entity_type; + + // If the plural label isn't available, use the regular label. + $label = isset($this->entity_info['plural label']) ? $this->entity_info['plural label'] : $this->entity_info['label']; + $data[$table]['table']['base'] = array( + 'field' => $this->entity_info['entity keys']['id'], + 'title' => drupal_ucfirst($label), + 'help' => isset($this->entity_info['description']) ? $this->entity_info['description'] : '', + ); + $data[$table]['table']['entity type'] = $this->entity_type; + $data[$table] += $this->schemaFields(); + + // Add in any reverse-relationships which have been determined. + $data += $this->relationships; + } + + return $data; + } + + /** + * Identifies views fields based on the schema and entity property data. + * + * @return array + * @todo + */ + protected function schemaFields() { + $schema = drupal_get_schema($this->entity_info['base table']); + $storage_controller = entity_get_controller($this->entity_type); + $definition = array( + 'type' => 'entity', + 'entity type' => 'node', + ); + $properties = $storage_controller->getPropertyDefinitions($definition) + array('properties' => array()); + $data = array(); + + foreach ($properties['properties'] as $name => $property_info) { + if (isset($property_info['schema field']) && isset($schema['fields'][$property_info['schema field']])) { + if ($views_info = $this->mapFromSchemaInfo($name, $schema['fields'][$property_info['schema field']], $property_info)) { + $data[$name] = $views_info; + } + } + } + return $data; + } + + /** + * Determines views information from the schema and property information. + * + * @param string $property_name + * @todo + * @param array $schema_field_info + * @todo + * @param array $property_info + * @todo + * + * @return array + * @todo + */ + protected function mapFromSchemaInfo($property_name, $schema_field_info, $property_info) { + $type = isset($property_info['type']) ? $property_info['type'] : 'text'; + $views_field_name = $property_info['schema field']; + + $return = array(); + + if (!empty($schema_field_info['serialize'])) { + return FALSE; + } + + $description = array( + 'title' => $property_info['label'], + 'help' => isset($property_info['description']) ? $property_info['description'] : NULL, + ); + + // Add in relationships to related entities. + if (($info = entity_get_info($type)) && !empty($info['base table'])) { + + // Prepare reversed relationship data. + $label_lowercase = drupal_strtolower($this->entity_info['label'][0]) . drupal_substr($this->entity_info['label'], 1); + $property_label_lowercase = drupal_strtolower($property_info['label'][0]) . drupal_substr($property_info['label'], 1); + + // The field of the first reverse relationship is named from the base + // table for backwards compatibility. + if (!isset($this->relationships[$info['base table']][$this->entity_info['base table']])){ + $name = $this->entity_info['base table']; + } + // For subsequent relationships, append the views field name to get a + // unique name. + else { + $name = $this->entity_info['base table'] . '_' . $views_field_name; + } + + // @todo Comment here. + $this->relationships[$info['base table']][$name] = array( + 'title' => $this->entity_info['label'], + 'help' => t("Associated @label via the @label's @property.", array('@label' => $label_lowercase, '@property' => $property_label_lowercase)), + 'relationship' => array( + 'label' => $this->entity_info['label'], + 'handler' => $this->getRelationshipHandlerClass($this->entity_type, $type), + 'base' => $this->entity_info['base table'], + 'base field' => $views_field_name, + 'relationship field' => isset($info['entity keys']['name']) ? $info['entity keys']['name'] : $info['entity keys']['id'], + ), + ); + + // @todo Comment here. + $return['relationship'] = array( + 'label' => drupal_ucfirst($info['label']), + 'handler' => $this->getRelationshipHandlerClass($type, $this->entity_type), + 'base' => $info['base table'], + 'base field' => isset($info['entity keys']['name']) ? $info['entity keys']['name'] : $info['entity keys']['id'], + 'relationship field' => $views_field_name, + ); + + // Add in direct field/filters/sorts for the ID itself as well. + $type = isset($info['entity keys']['name']) ? 'token' : 'integer'; + // Append the views field name to the title if it is different from the + // property name. + if ($property_name != $views_field_name) { + $description['title'] .= ' ' . $views_field_name; + } + } + + switch ($type) { + case 'token': + case 'text': + $return += $description + array( + 'field' => array( + 'real field' => $views_field_name, + 'handler' => 'views_handler_field', + 'click sortable' => TRUE, + ), + 'sort' => array( + 'real field' => $views_field_name, + 'handler' => 'views_handler_sort', + ), + 'filter' => array( + 'real field' => $views_field_name, + 'handler' => 'views_handler_filter_string', + ), + 'argument' => array( + 'real field' => $views_field_name, + 'handler' => 'views_handler_argument_string', + ), + ); + break; + + case 'decimal': + case 'integer': + $return += $description + array( + 'field' => array( + 'real field' => $views_field_name, + 'handler' => 'views_handler_field_numeric', + 'click sortable' => TRUE, + 'float' => ($type == 'decimal'), + ), + 'sort' => array( + 'real field' => $views_field_name, + 'handler' => 'views_handler_sort', + ), + 'filter' => array( + 'real field' => $views_field_name, + 'handler' => 'views_handler_filter_numeric', + ), + 'argument' => array( + 'real field' => $views_field_name, + 'handler' => 'views_handler_argument_numeric', + ), + ); + break; + + case 'date': + $return += $description + array( + 'field' => array( + 'real field' => $views_field_name, + 'handler' => 'views_handler_field_date', + 'click sortable' => TRUE, + ), + 'sort' => array( + 'real field' => $views_field_name, + 'handler' => 'views_handler_sort_date', + ), + 'filter' => array( + 'real field' => $views_field_name, + 'handler' => 'views_handler_filter_date', + ), + 'argument' => array( + 'real field' => $views_field_name, + 'handler' => 'views_handler_argument_date', + ), + ); + break; + + case 'uri': + $return += $description + array( + 'field' => array( + 'real field' => $views_field_name, + 'handler' => 'views_handler_field_url', + 'click sortable' => TRUE, + ), + 'sort' => array( + 'real field' => $views_field_name, + 'handler' => 'views_handler_sort', + ), + 'filter' => array( + 'real field' => $views_field_name, + 'handler' => 'views_handler_filter_string', + ), + 'argument' => array( + 'real field' => $views_field_name, + 'handler' => 'views_handler_argument_string', + ), + ); + break; + + case 'boolean': + $return += $description + array( + 'field' => array( + 'real field' => $views_field_name, + 'handler' => 'views_handler_field_boolean', + 'click sortable' => TRUE, + ), + 'sort' => array( + 'real field' => $views_field_name, + 'handler' => 'views_handler_sort', + ), + 'filter' => array( + 'real field' => $views_field_name, + 'handler' => 'views_handler_filter_boolean_operator', + ), + 'argument' => array( + 'real field' => $views_field_name, + 'handler' => 'views_handler_argument_string', + ), + ); + break; + } + + // If there is an options list callback, add to the filter and field. + if (isset($return['filter']) && !empty($property_info['options list'])) { + $return['filter']['handler'] = 'views_handler_filter_in_operator'; + $return['filter']['options callback'] = array('EntityDefaultViewsController', 'optionsListCallback'); + $return['filter']['options arguments'] = array($this->type, $property_name, 'view'); + } + // @todo This class_exists is needed until views 3.2. + if (isset($return['field']) && !empty($property_info['options list']) && class_exists('views_handler_field_machine_name')) { + $return['field']['handler'] = 'views_handler_field_machine_name'; + $return['field']['options callback'] = array('EntityDefaultViewsController', 'optionsListCallback'); + $return['field']['options arguments'] = array($this->type, $property_name, 'view'); + } + return $return; + } + + /** + * Determines the handler to use for a relationship to an entity type. + * + * @param string $entity_type + * The entity type to join to. + * @param string $left_type + * The data type from which to join. + * + * @return string + * The relationship class for the field. + */ + public function getRelationshipHandlerClass($entity_type, $left_type) { + // Look for an entity type which is used as bundle for the given entity + // type. If there is one, allow filtering the relation by bundle by using + // our own handler. + foreach (entity_get_info() as $type => $info) { + // In case we already join from the bundle entity we do not need to filter + // by bundle entity any more, so we stay with the general handler. + if (!empty($info['bundle of']) && $info['bundle of'] == $entity_type && $type != $left_type) { + return 'entity_views_handler_relationship_by_bundle'; + } + } + return 'views_handler_relationship'; + } + +}