diff options
Diffstat (limited to 'sites/all/modules/entity/views/handlers/entity_views_field_handler_helper.inc')
-rw-r--r-- | sites/all/modules/entity/views/handlers/entity_views_field_handler_helper.inc | 527 |
1 files changed, 527 insertions, 0 deletions
diff --git a/sites/all/modules/entity/views/handlers/entity_views_field_handler_helper.inc b/sites/all/modules/entity/views/handlers/entity_views_field_handler_helper.inc new file mode 100644 index 000000000..0077f4a3d --- /dev/null +++ b/sites/all/modules/entity/views/handlers/entity_views_field_handler_helper.inc @@ -0,0 +1,527 @@ +<?php + +/** + * @file + * Contains the EntityFieldHandlerHelper class. + */ + +/** + * Helper class containing static implementations of common field handler methods. + * + * Used by the data selection entity field handlers to avoid code duplication. + * + * @see entity_views_table_definition() + */ +class EntityFieldHandlerHelper { + + /** + * Provide appropriate default options for a handler. + */ + public static function option_definition($handler) { + if (entity_property_list_extract_type($handler->definition['type'])) { + $options['list']['contains']['mode'] = array('default' => 'collapse'); + $options['list']['contains']['separator'] = array('default' => ', '); + $options['list']['contains']['type'] = array('default' => 'ul'); + } + $options['link_to_entity'] = array('default' => FALSE); + + return $options; + } + + /** + * Provide an appropriate default option form for a handler. + */ + public static function options_form($handler, &$form, &$form_state) { + if (entity_property_list_extract_type($handler->definition['type'])) { + $form['list']['mode'] = array( + '#type' => 'select', + '#title' => t('List handling'), + '#options' => array( + 'collapse' => t('Concatenate values using a seperator'), + 'list' => t('Output values as list'), + 'first' => t('Show first (if present)'), + 'count' => t('Show item count'), + ), + '#default_value' => $handler->options['list']['mode'], + ); + $form['list']['separator'] = array( + '#type' => 'textfield', + '#title' => t('List seperator'), + '#default_value' => $handler->options['list']['separator'], + '#dependency' => array('edit-options-list-mode' => array('collapse')), + ); + $form['list']['type'] = array( + '#type' => 'select', + '#title' => t('List type'), + '#options' => array( + 'ul' => t('Unordered'), + 'ol' => t('Ordered'), + ), + '#default_value' => $handler->options['list']['type'], + '#dependency' => array('edit-options-list-mode' => array('list')), + ); + } + $form['link_to_entity'] = array( + '#type' => 'checkbox', + '#title' => t('Link this field to its entity'), + '#description' => t("When using this, you should not set any other link on the field."), + '#default_value' => $handler->options['link_to_entity'], + ); + } + + /** + * Add the field for the entity ID (if necessary). + */ + public static function query($handler) { + // Copied over from views_handler_field_entity::query(). + // Sets table_alias (entity table), base_field (entity id field) and + // field_alias (the field's alias). + $handler->table_alias = $base_table = $handler->view->base_table; + $handler->base_field = $handler->view->base_field; + + if (!empty($handler->relationship)) { + foreach ($handler->view->relationship as $relationship) { + if ($relationship->alias == $handler->relationship) { + $base_table = $relationship->definition['base']; + $handler->table_alias = $relationship->alias; + + $table_data = views_fetch_data($base_table); + $handler->base_field = empty($relationship->definition['base field']) ? $table_data['table']['base']['field'] : $relationship->definition['base field']; + } + } + } + + // Add the field if the query back-end implements an add_field() method, + // just like the default back-end. + if (method_exists($handler->query, 'add_field')) { + $handler->field_alias = $handler->query->add_field($handler->table_alias, $handler->base_field, ''); + } + else { + // To ensure there is an alias just set the field alias to the real field. + $handler->field_alias = $handler->real_field; + } + } + + /** + * Extracts the innermost field name from a data selector. + * + * @param $selector + * The data selector. + * + * @return + * The last component of the data selector. + */ + public static function get_selector_field_name($selector) { + return ltrim(substr($selector, strrpos($selector, ':')), ':'); + } + + /** + * Adds a click-sort to the query. + * + * @param $order + * Either 'ASC' or 'DESC'. + */ + public static function click_sort($handler, $order) { + // The normal orderby() method for this usually won't work here. So we need + // query plugins to provide their own method for this. + if (method_exists($handler->query, 'add_selector_orderby')) { + $selector = self::construct_property_selector($handler, TRUE); + $handler->query->add_selector_orderby($selector, $order); + } + } + + /** + * Load the entities for all rows that are about to be displayed. + * + * Automatically takes care of relationships, including data selection + * relationships. Results are written into @code $handler->wrappers @endcode + * and @code $handler->entity_type @endcode is set. + */ + public static function pre_render($handler, &$values, $load_always = FALSE) { + if (empty($values)) { + return; + } + if (!$load_always && empty($handler->options['link_to_entity'])) { + // Check whether we even need to load the entities. + $selector = self::construct_property_selector($handler, TRUE); + $load = FALSE; + foreach ($values as $row) { + if (empty($row->_entity_properties) || !array_key_exists($selector, $row->_entity_properties)) { + $load = TRUE; + break; + } + } + if (!$load) { + return; + } + } + + if (method_exists($handler->query, 'get_result_wrappers')) { + list($handler->entity_type, $handler->wrappers) = $handler->query->get_result_wrappers($values, $handler->relationship, $handler->real_field); + } + else { + list($handler->entity_type, $entities) = $handler->query->get_result_entities($values, $handler->relationship, $handler->real_field); + $handler->wrappers = array(); + foreach ($entities as $id => $entity) { + $handler->wrappers[$id] = entity_metadata_wrapper($handler->entity_type, $entity); + } + } + } + + /** + * Return an Entity API data selector for the given handler's relationship. + * + * A data selector is a concatenation of properties which should be followed + * to arrive at a desired property that may be nested in related entities or + * structures. The separate properties are herein concatenated with colons. + * + * For instance, a data selector of "author:roles" would mean to first + * access the "author" property of the given wrapper, and then for this new + * wrapper to access and return the "roles" property. + * + * Lists of entities are handled automatically by always returning only the + * first entity. + * + * @param $handler + * The handler for which to construct the selector. + * @param $complete + * If TRUE, the complete selector for the field is returned, not just the + * one for its parent. Defaults to FALSE. + * + * @return + * An Entity API data selector for the given handler's relationship. + */ + public static function construct_property_selector($handler, $complete = FALSE) { + $return = ''; + if ($handler->relationship) { + $current_handler = $handler; + $view = $current_handler->view; + $relationships = array(); + // Collect all relationships, keyed by alias. + foreach ($view->relationship as $key => $relationship) { + $key = $relationship->alias ? $relationship->alias : $key; + $relationships[$key] = $relationship; + } + while (!empty($current_handler->relationship) && !empty($relationships[$current_handler->relationship])) { + $current_handler = $relationships[$current_handler->relationship]; + $return = $current_handler->real_field . ($return ? ":$return" : ''); + } + } + + if ($complete) { + $return .= ($return ? ':' : '') . $handler->real_field; + } + elseif ($pos = strrpos($handler->real_field, ':')) { + // If we have a selector as the real_field, append this to the returned + // relationship selector. + $return .= ($return ? ':' : '') . substr($handler->real_field, 0, $pos); + } + + return $return; + } + + /** + * Extracts data from several metadata wrappers based on a data selector. + * + * All metadata wrappers passed to this function have to be based on the exact + * same property information. The data will be returned wrapped by one or more + * metadata wrappers. + * + * Can be used in query plugins for the get_result_entities() and + * get_result_wrappers() methods. + * + * @param array $wrappers + * The EntityMetadataWrapper objects from which to extract data. + * @param $selector + * The selector specifying the data to extract. + * + * @return array + * An array with numeric indices, containing the type of the extracted + * wrappers in the first element. The second element of the array contains + * the extracted property value(s) for each wrapper, keyed to the same key + * that was used for the respecive wrapper in $wrappers. All extracted + * properties are returned as metadata wrappers. + */ + public static function extract_property_multiple(array $wrappers, $selector) { + $parts = explode(':', $selector, 2); + $name = $parts[0]; + + $results = array(); + $entities = array(); + $type = ''; + foreach ($wrappers as $i => $wrapper) { + try { + $property = $wrapper->$name; + $type = $property->type(); + if ($property instanceof EntityDrupalWrapper) { + // Remember the entity IDs to later load all at once (so as to + // properly utilize multiple load functionality). + $id = $property->getIdentifier(); + // Only accept valid ids. $id can be FALSE for entity values that are + // NULL. + if ($id) { + $entities[$type][$i] = $id; + } + } + elseif ($property instanceof EntityStructureWrapper) { + $results[$i] = $property; + } + elseif ($property instanceof EntityListWrapper) { + foreach ($property as $item) { + $results[$i] = $item; + $type = $item->type(); + break; + } + } + // Do nothing in case it cannot be applied. + } + catch (EntityMetadataWrapperException $e) { + // Skip single empty properties. + } + } + + if ($entities) { + // Map back the loaded entities back to the results array. + foreach ($entities as $type => $id_map) { + $loaded = entity_load($type, $id_map); + foreach ($id_map as $i => $id) { + if (isset($loaded[$id])) { + $results[$i] = entity_metadata_wrapper($type, $loaded[$id]); + } + } + } + } + + // If there are no further parts in the selector, we are done now. + if (empty($parts[1])) { + return array($type, $results); + } + return self::extract_property_multiple($results, $parts[1]); + } + + /** + * Get the value of a certain data selector. + * + * Uses $values->_entity_properties to look for already extracted properties. + * + * @param $handler + * The field handler for which to return a value. + * @param $values + * The values for the current row retrieved from the Views query, as an + * object. + * @param $field + * The field to extract. If no value is given, the field of the given + * handler is used instead. The special "entity object" value can be used to + * get the base entity instead of a special field. + * @param $default + * The value to return if the entity or field are not present. + */ + public static function get_value($handler, $values, $field = NULL, $default = NULL) { + // There is a value cache on each handler so parent handlers rendering a + // single field value from a list will get the single value, not the whole + // list. + if (!isset($field) && isset($handler->current_value)) { + return $handler->current_value; + } + $field = isset($field) ? $field : self::get_selector_field_name($handler->real_field); + $selector = self::construct_property_selector($handler); + $selector = $selector ? "$selector:$field" : $field; + if (!isset($values->_entity_properties)) { + $values->_entity_properties = array(); + } + if (!array_key_exists($selector, $values->_entity_properties)) { + if (!isset($handler->wrappers[$handler->view->row_index])) { + $values->_entity_properties[$selector] = $default; + } + elseif (is_array($handler->wrappers[$handler->view->row_index])) { + $values->_entity_properties[$selector] = self::extract_list_wrapper_values($handler->wrappers[$handler->view->row_index], $field); + } + else { + $wrapper = $handler->wrappers[$handler->view->row_index]; + try { + if ($field === 'entity object') { + $values->_entity_properties[$selector] = $wrapper->value(); + } + else { + $values->_entity_properties[$selector] = isset($wrapper->$field) ? $wrapper->$field->value(array('identifier' => TRUE, 'sanitize' => TRUE)) : $default; + } + } + catch (EntityMetadataWrapperException $e) { + $values->_entity_properties[$selector] = $default; + } + } + } + return $values->_entity_properties[$selector]; + } + + /** + * Helper method for extracting the values from an array of wrappers. + * + * Nested arrays of wrappers are also handled, the values are returned in a + * flat (not nested) array. + */ + public static function extract_list_wrapper_values(array $wrappers, $field) { + $return = array(); + foreach ($wrappers as $wrapper) { + if (is_array($wrapper)) { + $values = self::extract_list_wrapper_values($wrapper, $field); + if ($values) { + $return = array_merge($return, $values); + } + } + else { + try { + if ($field == 'entity object') { + $return[] = $wrapper->value(); + } + elseif (isset($wrapper->$field)) { + $return[] = $wrapper->$field->value(array('identifier' => TRUE)); + } + } + catch (EntityMetadataWrapperException $e) { + // An exception probably signifies a non-present property, so we just + // ignore it. + } + } + } + return $return; + } + + /** + * Render the field. + * + * Implements the entity link functionality and list handling. Basic handling + * of the single values is delegated back to the field handler. + * + * @param $handler + * The field handler whose field should be rendered. + * @param $values + * The values for the current row retrieved from the Views query, as an + * object. + * + * @return + * The rendered value for the field. + */ + public static function render($handler, $values) { + $value = $handler->get_value($values); + if (is_array($value)) { + return self::render_list($handler, $value, $values); + } + return self::render_entity_link($handler, $value, $values); + } + + /** + * Render a list of values. + * + * @param $handler + * The field handler whose field is rendered. + * @param $list + * The list of values to render. + * @param $values + * The values for the current row retrieved from the Views query, as an + * object. + * + * @return + * The rendered value for the given list. + */ + public static function render_list($handler, $list, $values) { + // Allow easy overriding of this behaviour in the specific field handler. + if (method_exists($handler, 'render_list')) { + return $handler->render_list($list, $values); + } + $mode = isset($handler->options['list']['mode']) ? $handler->options['list']['mode'] : NULL; + switch ($mode) { + case 'first': + $list = count($list) ? array_shift($list) : NULL; + if (is_array($list)) { + return self::render_list($handler, $list, $values); + } + elseif (isset($list)) { + return self::render_entity_link($handler, $list, $values); + } + return NULL; + + case 'count': + return count($list); + + // Handles both collapse and list output. Fallback is to collapse. + default: + $inner_values = array(); + foreach ($list as $value) { + $value = is_array($value) ? self::render_list($handler, $value, $values) : self::render_entity_link($handler, $value, $values); + if ($value) { + $inner_values[] = $value; + } + } + + // Format output as list. + if ($mode == 'list') { + $type = isset($handler->options['list']['type']) ? $handler->options['list']['type'] : 'ul'; + return theme('item_list', array( + 'items' => $inner_values, + 'type' => $type, + )); + } + + $separator = isset($handler->options['list']['separator']) ? $handler->options['list']['separator'] : ', '; + return implode($separator, $inner_values); + } + } + + /** + * Render a single value as a link to the entity if applicable. + * + * @param $handler + * The field handler whose field is rendered. + * @param $value + * The single value to render. + * @param $values + * The values for the current row retrieved from the Views query, as an + * object. + * + * @return + * The rendered value. + */ + public static function render_entity_link($handler, $value, $values) { + // Allow easy overriding of this behaviour in the specific field handler. + if (method_exists($handler, 'render_entity_link')) { + return $handler->render_entity_link($value, $values); + } + $render = self::render_single_value($handler, $value, $values); + if (!$handler->options['link_to_entity']) { + return $render; + } + $entity = $handler->get_value($values, 'entity object'); + if (is_object($entity) && ($url = entity_uri($handler->entity_type, $entity))) { + return l($render, $url['path'], array('html' => TRUE) + $url['options']); + } + return $render; + } + + /** + * Render a single value. + * + * @param $handler + * The field handler whose field is rendered. + * @param $value + * The single value to render. + * @param $values + * The values for the current row retrieved from the Views query, as an + * object. + * + * @return + * The rendered value. + */ + public static function render_single_value($handler, $value, $values) { + // Try to use the method in the specific field handler. + if (method_exists($handler, 'render_single_value')) { + $handler->current_value = $value; + $return = $handler->render_single_value($value, $values); + unset($handler->current_value); + return $return; + } + // Default fallback in case the field handler doesn't provide the method. + return is_scalar($value) ? check_plain($value) : nl2br(check_plain(print_r($value, TRUE))); + } + +} |