view = &$view; $this->display = &$display; // Overlay incoming options on top of defaults $this->unpack_options($this->options, isset($options) ? $options : $display->handler->get_option('style_options')); if ($this->uses_row_plugin() && $display->handler->get_option('row_plugin')) { $this->row_plugin = $display->handler->get_plugin('row'); } $this->options += array( 'grouping' => '', ); $this->definition += array( 'uses grouping' => TRUE, ); } function destroy() { parent::destroy(); if (isset($this->row_plugin)) { $this->row_plugin->destroy(); } } /** * Return TRUE if this style also uses a row plugin. */ function uses_row_plugin() { return !empty($this->definition['uses row plugin']); } /** * Return TRUE if this style also uses fields. */ function uses_fields() { // If we use a row plugin, ask the row plugin. Chances are, we don't // care, it does. if ($this->uses_row_plugin() && !empty($this->row_plugin)) { return $this->row_plugin->uses_fields(); } // Otherwise, maybe we do. return !empty($this->definition['uses fields']); } function option_definition() { $options = parent::option_definition(); $options['grouping'] = array('default' => ''); return $options; } function options_form(&$form, &$form_state) { // Only fields-based views can handle grouping. Style plugins can also exclude // themselves from being groupable by setting their "use grouping" definiton // key to FALSE. // @TODO: Document "uses grouping" in docs.php when docs.php is written. if ($this->uses_fields() && $this->definition['uses grouping']) { $options = array('' => t('- None -')); $options += $this->display->handler->get_field_labels(); // If there are no fields, we can't group on them. if (count($options) > 1) { $form['grouping'] = array( '#type' => 'select', '#title' => t('Grouping field'), '#options' => $options, '#default_value' => $this->options['grouping'], '#description' => t('You may optionally specify a field by which to group the records. Leave blank to not group.'), ); } } } /** * Called by the view builder to see if this style handler wants to * interfere with the sorts. If so it should build; if it returns * any non-TRUE value, normal sorting will NOT be added to the query. */ function build_sort() { return TRUE; } /** * Called by the view builder to let the style build a second set of * sorts that will come after any other sorts in the view. */ function build_sort_post() { } /** * Allow the style to do stuff before each row is rendered. * * @param $result * The full array of results from the query. */ function pre_render($result) { if (!empty($this->row_plugin)) { $this->row_plugin->pre_render($result); } } /** * Render the display in this style. */ function render() { if ($this->uses_row_plugin() && empty($this->row_plugin)) { vpr('views_plugin_style_default: Missing row plugin'); return; } // Group the rows according to the grouping field, if specified. $sets = $this->render_grouping($this->view->result, $this->options['grouping']); // Render each group separately and concatenate. Plugins may override this // method if they wish some other way of handling grouping. $output = ''; foreach ($sets as $title => $records) { if ($this->uses_row_plugin()) { $rows = array(); foreach ($records as $row_index => $row) { $this->view->row_index = $row_index; $rows[] = $this->row_plugin->render($row); } } else { $rows = $records; } $output .= theme($this->theme_functions(), $this->view, $this->options, $rows, $title); } unset($this->view->row_index); return $output; } /** * Group records as needed for rendering. * * @param $records * An array of records from the view to group. * @param $grouping_field * The field id on which to group. If empty, the result set will be given * a single group with an empty string as a label. * @return * The grouped record set. */ function render_grouping($records, $grouping_field = '') { // Make sure fields are rendered $this->render_fields($this->view->result); $sets = array(); if ($grouping_field) { foreach ($records as $index => $row) { $grouping = ''; // Group on the rendered version of the field, not the raw. That way, // we can control any special formatting of the grouping field through // the admin or theme layer or anywhere else we'd like. if (isset($this->view->field[$grouping_field])) { $grouping = $this->get_field($index, $grouping_field); if ($this->view->field[$grouping_field]->options['label']) { $grouping = $this->view->field[$grouping_field]->options['label'] . ': ' . $grouping; } } $sets[$grouping][$index] = $row; } } else { // Create a single group with an empty grouping field. $sets[''] = $records; } return $sets; } /** * Render all of the fields for a given style and store them on the object. * * @param $result * The result array from $view->result */ function render_fields($result) { if (!$this->uses_fields()) { return; } if (isset($this->rendered_fields)) { return $this->rendered_fields; } $this->view->row_index = 0; $keys = array_keys($this->view->field); foreach ($result as $count => $row) { $this->view->row_index = $count; foreach ($keys as $id) { $this->rendered_fields[$count][$id] = $this->view->field[$id]->theme($row); } } unset($this->view->row_index); } /** * Get a rendered field. * * @param $index * The index count of the row. * @param $field * The id of the field. */ function get_field($index, $field) { if (!isset($this->rendered_fields)) { $this->render_fields($this->view->result); } if (isset($this->rendered_fields[$index][$field])) { return $this->rendered_fields[$index][$field]; } } function validate() { $errors = parent::validate(); if ($this->uses_row_plugin()) { $plugin = $this->display->handler->get_plugin('row'); if (empty($plugin)) { $errors[] = t('Style @style requires a row style but the row plugin is invalid.', array('@style' => $this->definition['title'])); } else { $result = $plugin->validate(); if (!empty($result) && is_array($result)) { $errors = array_merge($errors, $result); } } } return $errors; } function query() { parent::query(); if (isset($this->row_plugin)) { $this->row_plugin->query(); } } } /** * @} */