content_field; parent::init($view, $options); $this->defer_query = !empty($options['multiple']['group']) && $field['multiple']; if ($this->defer_query) { // Grouped field: ditch the existing additional_fields (field columns + delta). // In the main query we'll only need: // - vid, which will be used to retrieve the actual values in pre_render, // - node type and nid, which wil be used in the pseudo-node used when // rendering. $this->additional_fields = array( 'type' => array('table' => 'node', 'field' => 'type'), 'nid' => array('table' => 'node', 'field' => 'nid'), ); if ($view->base_table == 'node_revisions') { $this->additional_fields['vid'] = array('table' => 'node_revisions', 'field' => 'vid'); } else { $this->additional_fields['vid'] = array('table' => 'node', 'field' => 'vid'); } } } function option_definition() { $options = parent::option_definition(); $options['multiple'] = array( 'contains' => array( 'group' => array('default' => TRUE), 'multiple_number' => array('default' => ''), 'multiple_from' => array('default' => ''), 'multiple_reversed' => array('default' => FALSE), ), ); return $options; } /** * Provide 'group multiple values' option. */ function options_form(&$form, &$form_state) { parent::options_form($form, $form_state); $field = $this->content_field; $options = $this->options; $form['multiple'] = array( '#access' => $field['multiple'], '#weight' => 1, ); $form['multiple']['group'] = array( '#title' => t('Group multiple values'), '#type' => 'checkbox', '#default_value' => $options['multiple']['group'], '#description' => t('If unchecked, each item in the field will create a new row, which may appear to cause duplicates. This setting is not compatible with click-sorting in table displays.'), ); // Make the string translatable by keeping it as a whole rather than // translating prefix and suffix separately. list($prefix, $suffix) = explode('@count', t('Show @count value(s)')); $form['multiple']['multiple_number'] = array( '#type' => 'textfield', '#size' => 5, '#field_prefix' => $prefix, '#field_suffix' => $suffix, '#default_value' => $options['multiple']['multiple_number'], '#prefix' => '
', '#process' => array('views_process_dependency'), '#dependency' => array('edit-options-multiple-group' => array(TRUE)), ); list($prefix, $suffix) = explode('@count', t('starting from @count')); $form['multiple']['multiple_from'] = array( '#type' => 'textfield', '#size' => 5, '#field_prefix' => $prefix, '#field_suffix' => $suffix, '#default_value' => $options['multiple']['multiple_from'], '#process' => array('views_process_dependency'), '#dependency' => array('edit-options-multiple-group' => array(TRUE)), '#description' => t('(first item is 0)'), ); $form['multiple']['multiple_reversed'] = array( '#title' => t('Reversed'), '#type' => 'checkbox', '#default_value' => $options['multiple']['multiple_reversed'], '#suffix' => '
', '#process' => array('views_process_dependency'), '#dependency' => array('edit-options-multiple-group' => array(TRUE)), '#description' => t('(start from last values)'), ); } /** * Determine if this field is click sortable. */ function click_sortable() { $field = $this->content_field; $options = $this->options; // Grouped fields are not click-sortable. return !empty($this->definition['click sortable']) && !$this->defer_query; } function query() { // If this is not a grouped field, use the generic query(). if (!$this->defer_query) { return parent::query(); } // Grouped field: do NOT call ensure_my_table, only add additional fields. $this->add_additional_fields(); $this->field_alias = $this->aliases['vid']; } function pre_render($values) { // If there are no values to render (displaying a summary, or query returned no results), // or if this is not a grouped field, do nothing specific. if (isset($this->view->build_info['summary']) || empty($values) || !$this->defer_query) { return parent::pre_render($values); } $field = $this->content_field; $db_info = content_database_info($field); $options = $this->options; // Build the list of vids to retrieve. // TODO: try fetching from cache_content first ?? $vids = array(); $this->field_values = array(); foreach ($values as $result) { if (isset($result->{$this->field_alias})) { $vids[] = $result->{$this->field_alias}; } } // It may happend that the multiple values field is related to a non // required relation for which no node data related to the field being // processed here is available. if (empty($vids)) { return parent::pre_render($values); } // List columns to retrieve. $alias = content_views_tablename($field); // Prefix aliases with '_' to avoid clashing with field columns names. $query_columns = array( 'vid AS _vid', "delta as _delta", // nid is needed to generate the links for 'link to node' option. 'nid AS _nid', ); // The actual field columns. foreach ($db_info['columns'] as $column => $attributes) { $query_columns[] = "$attributes[column] AS $column"; } $query = 'SELECT '. implode(', ', $query_columns) . ' FROM {'. $db_info['table'] ."}". " WHERE vid IN (". implode(',', $vids) .')'. " ORDER BY _nid ASC, _delta ". ($options['multiple']['multiple_reversed'] ? 'DESC' : 'ASC'); $result = db_query($query); while ($item = db_fetch_array($result)) { // Clean up the $item from vid and delta. We keep nid for now. $vid = $item['_vid']; unset($item['_vid']); $delta = !empty($item['_delta']) ? $item['_delta'] : 0; $item['#delta'] = $item['_delta']; unset($item['_delta']); $this->field_values[$vid][$delta] = $item; } } /** * Return DIV or SPAN based upon the field's element type. * * Fields rendered with the 'group multiple' option use
markers, * and thus shouldn't be wrapped in a . */ function element_type($none_supported = FALSE, $default_empty = FALSE) { // If this is not a grouped field, use the parent method. if (!$this->defer_query) { return parent::element_type($none_supported, $default_empty); } // The 'element_type' property denotes Views 3.x ('semantic views' // functionnality). If the property is set, and not set to '' ("default"), // let the generic method handle the output. if (isset($this->options['element_type']) && $this->options['element_type'] !== '') { return parent::element_type($none_supported, $default_empty); } if ($default_empty) { return ''; } if (isset($this->definition['element type'])) { return $this->definition['element type']; } return 'div'; } function render($values) { // If this is not a grouped field, use content_handler_field::render(). if (!$this->defer_query) { return parent::render($values); } // We're down to a single node here, so we can retrieve the actual field // definition for the node type being considered. $field = content_fields($this->content_field['field_name'], $values->{$this->aliases['type']}); // If the field does not appear in the node type, then we have no value // to display, and can just return. if (empty($field)) { return ''; } $options = $this->options; $vid = $values->{$this->field_alias}; if (isset($this->field_values[$vid])) { // Gather items, respecting the 'Display n values starting from m' settings. $count_skipped = 0; $items = array(); foreach ($this->field_values[$vid] as $item) { if (empty($options['multiple']['multiple_from']) || ($count_skipped >= $options['multiple']['multiple_from'])) { if (empty($options['multiple']['multiple_number']) || (count($items) < $options['multiple']['multiple_number'])) { // Grab the nid - needed for render_link(). $nid = $item['_nid']; unset($item['_nid']); $items[] = $item; } else { break; } } $count_skipped++; } // Build a pseudo-node from the retrieved values. $node = drupal_clone($values); // content_format and formatters will need a 'type'. $node->type = $values->{$this->aliases['type']}; $node->nid = $values->{$this->aliases['nid']}; $node->vid = $values->{$this->aliases['vid']}; // Some formatters need to behave differently depending on the build_mode // (for instance: preview), so we provide one. $node->build_mode = NODE_BUILD_NORMAL; // Render items. $formatter_name = $options['format']; if ($items && ($formatter = _content_get_formatter($formatter_name, $field['type']))) { $rendered = array(); if (content_handle('formatter', 'multiple values', $formatter) == CONTENT_HANDLE_CORE) { // Single-value formatter. foreach ($items as $item) { $output = content_format($field, $item, $formatter_name, $node); if (!empty($output)) { $rendered[] = $this->render_link($output, (object) array('nid' => $nid)); } } } else { // Multiple values formatter. $output = content_format($field, $items, $formatter_name, $values); if (!empty($output)) { $rendered[] = $this->render_link($output, (object) array('nid' => $nid)); } } if (count($rendered) > 1) { // TODO: could we use generic field display ? return theme('content_view_multiple_field', $rendered, $field, $values); } elseif ($rendered) { return $rendered[0]; } } } return ''; } function render_link($data, $values) { if (!$this->defer_query) { return parent::render_link($data, $values); } if (!empty($this->options['link_to_node']) && $data !== NULL && $data !== '') { if (method_exists('render_as_link', 'views_handler_field')) { // Views 2.3+ $this->options['alter']['make_link'] = TRUE; $this->options['alter']['path'] = "node/" . $values->{$this->aliases['nid']}; } else { // Views up to 2.2 return l($data, "node/" . $values->nid, array('html' => TRUE)); } } else { return $data; } } }