Spiffy improvements to grid view
[project/views.git] / theme / theme.inc
1 <?php
2 // $Id$
3
4 /**
5 * @file theme.inc
6 *
7 * An array of preprocessors to fill variables for templates and helper
8 * functions to make theming easier.
9 */
10
11 /**
12 * Provide a full array of possible themes to try for a given hook.
13 *
14 * @param $hook
15 * The hook to use. This is the base theme/template name.
16 * @param $view
17 * The view being rendered.
18 * @param $display
19 * The display being rendered, if applicable.
20 */
21 function _views_theme_functions($hook, $view, $display = NULL) {
22 $themes = array();
23
24 if ($display) {
25 $themes[] = $hook . '__' . $view->name . '__' . $display->id;
26 $themes[] = $hook . '__' . $display->id;
27 if ($display->id != $display->display_plugin) {
28 $themes[] = $hook . '__' . $view->name . '__' . $display->display_plugin;
29 $themes[] = $hook . '__' . $display->display_plugin;
30 }
31 }
32 $themes[] = $hook . '__' . $view->name;
33 $themes[] = $hook;
34
35 return $themes;
36 }
37
38 /**
39 * Preprocess the primary theme implementation for a view.
40 */
41 function template_preprocess_views_view(&$vars) {
42 $view = $vars['view'];
43
44 $vars['rows'] = $view->style_handler->render($view->result);
45 $vars['css_name'] = views_css_safe($view->name);
46 $vars['name'] = $view->name;
47 $vars['display_id'] = $view->current_display;
48
49 if (!$vars['rows']) {
50 $vars['empty'] = $view->display_handler->render_empty();
51 if (!$view->display_handler->get_option('header_empty')) {
52 $vars['header'] = '';
53 }
54 if (!$view->display_handler->get_option('footer_empty')) {
55 $vars['footer'] = '';
56 }
57 }
58 else {
59 $vars['empty'] = '';
60 $header = TRUE;
61 }
62
63 $vars['exposed'] = !empty($view->exposed_widgets) ? $view->exposed_widgets : '';
64 if (!isset($vars['header'])) {
65 $vars['header'] = $view->display_handler->render_header();
66 }
67 if (!isset($vars['footer'])) {
68 $vars['footer'] = $view->display_handler->render_footer();
69 }
70 $vars['more'] = $view->display_handler->render_more_link();
71 $vars['feed_icon'] = !empty($view->feed_icon) ? $view->feed_icon : '';
72
73 $vars['attachment_before'] = !empty($view->attachment_before) ? $view->attachment_before : '';
74 $vars['attachment_after'] = !empty($view->attachment_after) ? $view->attachment_after : '';
75
76 $vars['pager'] = '';
77 if (!empty($view->pager['use_pager'])) {
78 $pager_type = $view->pager['use_pager'] == 'mini' ? 'views_mini_pager' : 'pager';
79 $vars['pager'] = theme($pager_type, $view->exposed_input, $view->pager['items_per_page'], $view->pager['element']);
80 }
81
82 // if administrator, add some links. These used to be tabs, but this is better.
83 if (user_access('administer views')) {
84 $vars['admin_links_raw'] = array(
85 array(
86 'title' => t('Edit'),
87 'alt' => t("Edit this view"),
88 'href' => "admin/build/views/edit/$view->name",
89 'fragment' => 'views-tab-' . $view->current_display,
90 'query' => drupal_get_destination(),
91 ),
92 array(
93 'title' => t('Export'),
94 'alt' => t("Export this view"),
95 'href' => "admin/build/views/export/$view->name",
96 ),
97 array(
98 'title' => t('Clone'),
99 'alt' => t("Create a copy of this view"),
100 'href' => "admin/build/views/clone/$view->name",
101 ),
102 );
103 $vars['admin_links'] = theme('links', $vars['admin_links_raw']);
104 }
105 else {
106 $vars['admin_links'] = '';
107 $vars['admin_links_raw'] = array();
108 }
109
110 // If using AJAX, send identifying data about this view.
111 if ($view->use_ajax) {
112 $settings = array(
113 'views' => array(
114 'ajax_path' => url('views/ajax'),
115 'ajaxViews' => array(
116 array(
117 'view_name' => $view->name,
118 'view_display_id' => $view->current_display,
119 'view_args' => implode('/', $view->args),
120 'view_path' => $_GET['q'],
121 ),
122 ),
123 ),
124 );
125
126 drupal_add_js($settings, 'setting');
127 views_add_js('ajax_view');
128 }
129 }
130
131 /**
132 * Preprocess theme function to print a single record from a row, with fields
133 */
134 function template_preprocess_views_view_fields(&$vars) {
135 $view = $vars['view'];
136
137 // Loop through the fields for this view.
138 foreach ($view->field as $id => $field) {
139 if (!empty($field['handler']) && is_object($field['handler'])) {
140 $object = new stdClass();
141
142 $object->content = $field['handler']->theme($vars['row']);
143 if (isset($field['handler']->field_alias) && isset($vars['row']->{$field['handler']->field_alias})) {
144 $object->raw = $vars['row']->{$field['handler']->field_alias};
145 }
146 else {
147 $object->raw = NULL; // make sure it exists to reduce NOTICE
148 }
149
150 $object->handler = $field['handler'];
151 $object->class = views_css_safe($id);
152 $object->label = check_plain($field['handler']->label());
153 $vars['fields'][$id] = $object;
154 }
155 }
156
157 }
158
159 /**
160 * Display a single views field.
161 *
162 * Interesting bits of info:
163 * $field->field_alias says what the raw value in $row will be. Reach it like
164 * this: @code { $row->{$field->field_alias} @endcode
165 */
166 function theme_views_view_field($view, $field, $row) {
167 return $field->render($row);
168 }
169
170 /**
171 * Preprocess theme function to print a single record from a row, with fields
172 */
173 function template_preprocess_views_view_summary(&$vars) {
174 $view = $vars['view'];
175 $argument = $view->argument[$view->build_info['summary_level']]['handler'];
176
177 foreach ($vars['rows'] as $id => $row) {
178 $vars['rows'][$id]->link = $argument->summary_name($row);
179 $vars['rows'][$id]->url = $argument->summary_link($row, $view->get_url());
180 $vars['rows'][$id]->count = intval($row->{$argument->count_alias});
181 }
182 }
183
184 /**
185 * Display a view as a table style.
186 */
187 function template_preprocess_views_view_table(&$vars) {
188 $view = $vars['view'];
189 $result = $view->result;
190 $options = $view->style_handler->options;
191 $handler = $view->style_handler;
192
193 $fields = $view->field;
194 $columns = $handler->sanitize_columns($options['columns'], $fields);
195
196 $active = !empty($handler->active) ? $handler->active : '';
197 $order = !empty($handler->order) ? $handler->order : 'asc';
198
199 $query = tablesort_get_querystring();
200 if ($query) {
201 $query = '&' . $query;
202 }
203
204 foreach ($columns as $field => $column) {
205 // render the header labels
206 if ($field == $column) {
207 $label = check_plain(!empty($fields[$field]['handler']) ? $fields[$field]['handler']->label() : '');
208 if (empty($options['info'][$field]['sortable'])) {
209 $vars['header'][$field] = $label;
210 }
211 else {
212 // @todo -- make this a setting
213 $initial = 'asc';
214
215 if ($active == $field && $order == 'asc') {
216 $initial = 'desc';
217 }
218
219 $image = theme('tablesort_indicator', $initial);
220 $title = t('sort by @s', array('@s' => $label));
221 $link_options = array(
222 'html' => true,
223 'attributes' => array('title' => $title),
224 'query' => 'order=' . urlencode($field) . '&sort=' . $initial . $query,
225 );
226 $vars['header'][$field] = l($label . $image, $_GET['q'], $link_options);
227 }
228 }
229
230 // Create a second variable so we can easily find what fields we have and what the
231 // CSS classes should be.
232 $vars['fields'][$field] = views_css_safe($field);
233 if ($active == $field) {
234 $vars['fields'][$field] .= ' active';
235 }
236
237 // Render each field into its appropriate column.
238 foreach ($result as $num => $row) {
239 if (!empty($fields[$field]['handler']) && is_object($fields[$field]['handler'])) {
240 $handler = &$fields[$field]['handler'];
241 $field_output = $handler->theme($row);
242
243 // Don't bother with separators and stuff if the field does not show up.
244 if (!$field_output) {
245 continue;
246 }
247
248 // Place the field into the column, along with an optional separator.
249 if (isset($vars['rows'][$num][$column])) {
250 if (!empty($options['info'][$column]['separator'])) {
251 $vars['rows'][$num][$column] .= $options['info'][$column]['separator'];
252 }
253 }
254 else {
255 $vars['rows'][$num][$column] = '';
256 }
257
258 $vars['rows'][$num][$column] .= $field_output;
259 }
260 }
261 }
262 }
263
264
265 /**
266 * Display a view as a grid style.
267 */
268 function template_preprocess_views_view_grid(&$vars) {
269 $view = $vars['view'];
270 $result = $view->result;
271 $options = $view->style_handler->options;
272 $handler = $view->style_handler;
273
274 $columns = $options['columns'];
275
276 $rows = array();
277
278 if ($options['alignment'] == 'horizontal') {
279 $row = array();
280 foreach ($vars['rows'] as $count => $item) {
281 $row[] = $item;
282 if (($count + 1) % $columns == 0) {
283 $rows[] = $row;
284 $row = array();
285 }
286 }
287 if ($row) {
288 $rows[] = $row;
289 }
290 }
291 else {
292 $num_rows = floor(count($vars['rows']) / $columns);
293 // The remainders are the 'odd' columns that are slightly longer.
294 $remainders = count($vars['rows']) % $columns;
295 $row = 0;
296 $col = 0;
297 foreach ($vars['rows'] as $count => $item) {
298 $rows[$row][$col] = $item;
299 $row++;
300
301 if (!$remainders && $row == $num_rows) {
302 $row = 0;
303 $col++;
304 }
305 else if ($remainders && $row == $num_rows + 1) {
306 $row = 0;
307 $col++;
308 $remainders--;
309 }
310 }
311 }
312 $vars['rows'] = $rows;
313 }
314
315 /**
316 * Preprocess an RSS feed
317 */
318 function template_preprocess_views_view_rss(&$vars) {
319 global $base_url;
320 global $language;
321
322 $view = &$vars['view'];
323 $options = &$vars['options'];
324 $items = &$vars['rows'];
325
326 $style = &$view->style_handler;
327
328 if (!empty($options['mission_description'])) {
329 $description = variable_get('site_mission', '');
330 }
331 else {
332 $description = $options['description'];
333 }
334
335 // Figure out which display which has a path we're using for this feed. If there isn't
336 // one, use the global $base_url
337 $link_display = $view->display_handler->get_link_display();
338
339
340 // Compare the link to the default home page; if it's the default home page, just use $base_url.
341 if (empty($vars['link'])) {
342 $vars['link'] = $base_url;
343 }
344
345 $vars['namespaces'] = drupal_attributes($style->namespaces);
346 $vars['channel'] = format_rss_channel($view->get_title(), $vars['link'], $description, $items, $language->language);
347
348 drupal_set_header('Content-Type: application/rss+xml; charset=utf-8');
349 }
350
351 /**
352 * Default theme function for all filter forms.
353 */
354 function template_preprocess_views_exposed_form(&$vars) {
355 views_add_css('views');
356 $form = &$vars['form'];
357
358 // Put all single checkboxes together in the last spot.
359 $checkboxes = '';
360
361 if (!empty($form['q'])) {
362 $vars['q'] = drupal_render($form['q']);
363 }
364
365 $vars['widgets'] = array();
366 foreach ($form['#info'] as $id => $info) {
367 // Set aside checkboxes.
368 if (isset($form[$info['value']]['#type']) && $form[$info['value']]['#type'] == 'checkbox') {
369 $checkboxes .= drupal_render($form[$info['value']]);
370 continue;
371 }
372 $widget = new stdClass;
373 // set up defaults so that there's always something there.
374 $widget->label = $widget->operator = $widget->widget = NULL;
375
376 if (!empty($info['label'])) {
377 $widget->label = $info['label'];
378 }
379 if (!empty($info['operator'])) {
380 $widget->operator = drupal_render($form[$info['operator']]);
381 }
382 $widget->widget = drupal_render($form[$info['value']]);
383 $vars['widgets'][$id] = $widget;
384 }
385
386 // Wrap up all the checkboxes we set aside into a widget.
387 if ($checkboxes) {
388 $widget = new stdClass;
389 // set up defaults so that there's always something there.
390 $widget->label = $widget->operator = $widget->widget = NULL;
391 $widget->widget = $checkboxes;
392 $vars['widgets']['checkboxes'] = $widget;
393 }
394
395 // Don't render these:
396 unset($form['form_id']);
397 unset($form['form_build_id']);
398 unset($form['form_token']);
399
400 // This includes the submit button.
401 $vars['button'] = drupal_render($form);
402 }
403
404 function theme_views_mini_pager($tags = array(), $limit = 10, $element = 0, $parameters = array(), $quantity = 9) {
405 global $pager_page_array, $pager_total;
406
407 // Calculate various markers within this pager piece:
408 // Middle is used to "center" pages around the current page.
409 $pager_middle = ceil($quantity / 2);
410 // current is the page we are currently paged to
411 $pager_current = $pager_page_array[$element] + 1;
412 // max is the maximum page number
413 $pager_max = $pager_total[$element];
414 // End of marker calculations.
415
416
417 $li_previous = theme('pager_previous', (isset($tags[1]) ? $tags[1] : t('‹‹')), $limit, $element, 1, $parameters);
418 $li_next = theme('pager_next', (isset($tags[3]) ? $tags[3] : t('››')), $limit, $element, 1, $parameters);
419
420 if ($pager_total[$element] > 1) {
421 $items[] = array(
422 'class' => 'pager-previous',
423 'data' => $li_previous,
424 );
425
426 $items[] = array(
427 'class' => 'pager-current',
428 'data' => t('@current of @max', array('@current' => $pager_current, '@max' => $pager_max)),
429 );
430
431 $items[] = array(
432 'class' => 'pager-next',
433 'data' => $li_next,
434 );
435 return theme('item_list', $items, NULL, 'ul', array('class' => 'pager'));
436 }
437 }
438
439 /**
440 * @defgroup views_templates Views' template files
441 * @{
442 * All views templates can be overridden with a variety of names, using
443 * the view, the display ID of the view, the display type of the view,
444 * or some combination thereof.
445 *
446 * For each view, there will be a minimum of two templates used. The first
447 * is used for all views: views-view.tpl.php.
448 *
449 * The second template is determined by the style selected for the view. Note
450 * that certain aspects of the view can also change which style is used; for
451 * example, arguments which provide a summary view might change the style to
452 * one of the special summary styles.
453 *
454 * The default style for all views is views-view-unformatted.tpl.php
455 *
456 * Many styles will then farm out the actual display of each row to a row
457 * style; the default row style is views-view-fields.tpl.php.
458 *
459 * Here is an example of all the templates that will be tried in the following
460 * case:
461 *
462 * View, named foobar. Style: unformatted. Row style: Fields. Display: Page.
463 *
464 * - views-view--page--foobar.tpl.php
465 * - views-view--page.tpl.php
466 * - views-view--foobar.tpl.php
467 * - views-view.tpl.php
468 *
469 * - views-view-unformatted--page--foobar.tpl.php
470 * - views-view-unformatted--page.tpl.php
471 * - views-view-unformatted--foobar.tpl.php
472 * - views-view-unformatted.tpl.php
473 *
474 * - views-view-fields--page--foobar.tpl.php
475 * - views-view-fields--page.tpl.php
476 * - views-view-fields--foobar.tpl.php
477 * - views-view-fields.tpl.php
478 *
479 * Important! When adding a new template to your theme, be sure to flush the
480 * theme registry cache! Simply visit admin/build/themes.
481 *
482 * @see _views_theme_functions
483 * @}
484 */