summaryrefslogtreecommitdiffstats
path: root/plugins/views_plugin_style.inc
blob: 17d6a089e6a3f7ee172299d04cf4b5852654c8cd (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
<?php
// $Id$

/**
 * @defgroup views_style_plugins Views' style plugins
 * @{
 * Style plugins control how a view is rendered. For example, they
 * can choose to display a collection of fields, node_view() output,
 * table output, or any kind of crazy output they want.
 *
 * Many style plugins can have an optional 'row' plugin, that displays
 * a single record. Not all style plugins can utilize this, so it is
 * up to the plugin to set this up and call through to the row plugin.
 *
 * @see hook_views_plugins
 */

/**
 * Base class to define a style plugin handler.
 */
class views_plugin_style extends views_plugin {
  /**
   * Initialize a style plugin.
   *
   * @param $view
   * @param $display
   * @param $options
   *   The style options might come externally as the style can be sourced
   *   from at least two locations. If it's not included, look on the display.
   */
  function init(&$view, &$display, $options = NULL) {
    $this->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>'));
      foreach ($this->display->handler->get_handlers('field') as $field => $handler) {

        if ($label = $handler->label()) {
          $options[$field] = $label;
        }
        else {
          $options[$field] = $handler->ui_name();
        }
      }

      // 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 = '';
    $this->view->row_index = 0;
    foreach ($sets as $title => $records) {
      if ($this->uses_row_plugin()) {
        $rows = array();
        foreach ($records as $label => $row) {
          $rows[] = $this->row_plugin->render($row);
          $this->view->row_index++;
        }
      }
      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 = '') {
    $sets = array();
    if ($grouping_field) {
      $this->view->row_index = 0;
      foreach ($records as $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->view->field[$grouping_field]->theme($row);
          $this->view->row_index++;
          if ($this->view->field[$grouping_field]->options['label']) {
            $grouping = $this->view->field[$grouping_field]->options['label'] . ': ' . $grouping;
          }
        }
        $sets[$grouping][] = $row;
      }
      unset($this->view->row_index);
    }
    else {
      // Create a single group with an empty grouping field.
      $sets[''] = $records;
    }
    return $sets;
  }

  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']));
      }
    }
    return $errors;
  }

  function query() {
    parent::query();
    if (isset($this->row_plugin)) {
      $this->row_plugin->query();
    }
  }
}

/**
 * @}
 */