global change view world
[project/views.git] / plugins / views_plugin_style.inc
1 <?php
2 // $Id$
3
4 /**
5 * @defgroup views_style_plugins Views' style plugins
6 * @{
7 * Style plugins control how a view is rendered. For example, they
8 * can choose to display a collection of fields, node_view() output,
9 * table output, or any kind of crazy output they want.
10 *
11 * Many style plugins can have an optional 'row' plugin, that displays
12 * a single record. Not all style plugins can utilize this, so it is
13 * up to the plugin to set this up and call through to the row plugin.
14 *
15 * @see hook_views_plugins
16 */
17
18 /**
19 * Base class to define a style plugin handler.
20 */
21 class views_plugin_style extends views_plugin {
22 /**
23 * Initialize a style plugin.
24 *
25 * @param $view
26 * @param $display
27 * @param $options
28 * The style options might come externally as the style can be sourced
29 * from at least two locations. If it's not included, look on the display.
30 */
31 function init(&$view, &$display, $options = NULL) {
32 $this->view = &$view;
33 $this->display = &$display;
34
35 // Overlay incoming options on top of defaults
36 $this->unpack_options($this->options, isset($options) ? $options : $display->handler->get_option('style_options'));
37
38 if ($this->uses_row_plugin() && $display->handler->get_option('row_plugin')) {
39 $this->row_plugin = $display->handler->get_plugin('row');
40 }
41
42 $this->options += array(
43 'grouping' => '',
44 );
45
46 $this->definition += array(
47 'uses grouping' => TRUE,
48 );
49 }
50
51 function destroy() {
52 parent::destroy();
53
54 if (isset($this->row_plugin)) {
55 $this->row_plugin->destroy();
56 }
57 }
58
59 /**
60 * Return TRUE if this style also uses a row plugin.
61 */
62 function uses_row_plugin() {
63 return !empty($this->definition['uses row plugin']);
64 }
65
66 /**
67 * Return TRUE if this style also uses fields.
68 */
69 function uses_fields() {
70 // If we use a row plugin, ask the row plugin. Chances are, we don't
71 // care, it does.
72 if ($this->uses_row_plugin() && !empty($this->row_plugin)) {
73 return $this->row_plugin->uses_fields();
74 }
75 // Otherwise, maybe we do.
76 return !empty($this->definition['uses fields']);
77 }
78
79 function option_definition() {
80 $options = parent::option_definition();
81 $options['grouping'] = array('default' => '');
82 return $options;
83 }
84
85 function options_form(&$form, &$form_state) {
86 // Only fields-based views can handle grouping. Style plugins can also exclude
87 // themselves from being groupable by setting their "use grouping" definiton
88 // key to FALSE.
89 // @TODO: Document "uses grouping" in docs.php when docs.php is written.
90 if ($this->uses_fields() && $this->definition['uses grouping']) {
91 $options = array('' => t('<None>'));
92 foreach ($this->display->handler->get_handlers('field') as $field => $handler) {
93
94 if ($label = $handler->label()) {
95 $options[$field] = $label;
96 }
97 else {
98 $options[$field] = $handler->ui_name();
99 }
100 }
101
102 // If there are no fields, we can't group on them.
103 if (count($options) > 1) {
104 $form['grouping'] = array(
105 '#type' => 'select',
106 '#title' => t('Grouping field'),
107 '#options' => $options,
108 '#default_value' => $this->options['grouping'],
109 '#description' => t('You may optionally specify a field by which to group the records. Leave blank to not group.'),
110 );
111 }
112 }
113 }
114
115 /**
116 * Called by the view builder to see if this style handler wants to
117 * interfere with the sorts. If so it should build; if it returns
118 * any non-TRUE value, normal sorting will NOT be added to the query.
119 */
120 function build_sort() { return TRUE; }
121
122 /**
123 * Called by the view builder to let the style build a second set of
124 * sorts that will come after any other sorts in the view.
125 */
126 function build_sort_post() { }
127
128 /**
129 * Allow the style to do stuff before each row is rendered.
130 *
131 * @param $result
132 * The full array of results from the query.
133 */
134 function pre_render($result) {
135 if (!empty($this->row_plugin)) {
136 $this->row_plugin->pre_render($result);
137 }
138 }
139
140 /**
141 * Render the display in this style.
142 */
143 function render() {
144 if ($this->uses_row_plugin() && empty($this->row_plugin)) {
145 vpr('views_plugin_style_default: Missing row plugin');
146 return;
147 }
148
149 // Group the rows according to the grouping field, if specified.
150 $sets = $this->render_grouping($this->view->result, $this->options['grouping']);
151
152 // Render each group separately and concatenate. Plugins may override this
153 // method if they wish some other way of handling grouping.
154 $output = '';
155 $this->view->row_index = 0;
156 foreach ($sets as $title => $records) {
157 if ($this->uses_row_plugin()) {
158 $rows = array();
159 foreach ($records as $label => $row) {
160 $rows[] = $this->row_plugin->render($row);
161 $this->view->row_index++;
162 }
163 }
164 else {
165 $rows = $records;
166 }
167
168 $output .= theme($this->theme_functions(), $this->view, $this->options, $rows, $title);
169 }
170 unset($this->view->row_index);
171 return $output;
172 }
173
174 /**
175 * Group records as needed for rendering.
176 *
177 * @param $records
178 * An array of records from the view to group.
179 * @param $grouping_field
180 * The field id on which to group. If empty, the result set will be given
181 * a single group with an empty string as a label.
182 * @return
183 * The grouped record set.
184 */
185 function render_grouping($records, $grouping_field = '') {
186 $sets = array();
187 if ($grouping_field) {
188 $this->view->row_index = 0;
189 foreach ($records as $row) {
190 $grouping = '';
191 // Group on the rendered version of the field, not the raw. That way,
192 // we can control any special formatting of the grouping field through
193 // the admin or theme layer or anywhere else we'd like.
194 if (isset($this->view->field[$grouping_field])) {
195 $grouping = $this->view->field[$grouping_field]->theme($row);
196 $this->view->row_index++;
197 if ($this->view->field[$grouping_field]->options['label']) {
198 $grouping = $this->view->field[$grouping_field]->options['label'] . ': ' . $grouping;
199 }
200 }
201 $sets[$grouping][] = $row;
202 }
203 unset($this->view->row_index);
204 }
205 else {
206 // Create a single group with an empty grouping field.
207 $sets[''] = $records;
208 }
209 return $sets;
210 }
211
212 function validate() {
213 $errors = parent::validate();
214
215 if ($this->uses_row_plugin()) {
216 $plugin = $this->display->handler->get_plugin('row');
217 if (empty($plugin)) {
218 $errors[] = t('Style @style requires a row style but the row plugin is invalid.', array('@style' => $this->definition['title']));
219 }
220 }
221 return $errors;
222 }
223
224 function query() {
225 parent::query();
226 if (isset($this->row_plugin)) {
227 $this->row_plugin->query();
228 }
229 }
230 }
231
232 /**
233 * @}
234 */
235