theme.inc 26.2 KB
Newer Older
Dries's avatar
   
Dries committed
1
<?php
Dries's avatar
   
Dries committed
2
3
/* $Id$ */

4
/**
Dries's avatar
   
Dries committed
5
 * @file
6
 * The theme system, which controls the output of Drupal.
Dries's avatar
   
Dries committed
7
8
9
10
 *
 * The theme system allows for nearly all output of the Drupal system to be
 * customized by user themes.
 *
Dries's avatar
   
Dries committed
11
 * @see <a href="http://drupal.org/node/253">Theme system</a>
Dries's avatar
   
Dries committed
12
13
 * @see themeable
 */
Dries's avatar
   
Dries committed
14

15
16
17
18
19
20
21
22
23
24
25
26
27
 /**
 * @name Content markers
 * @{
 * Markers used by theme_mark() and node_mark() to designate content.
 * @see theme_mark(), node_mark()
 */
define('MARK_READ',    0);
define('MARK_NEW',     1);
define('MARK_UPDATED', 2);
/**
 * @} End of "Content markers".
 */

Dries's avatar
   
Dries committed
28
/**
Dries's avatar
   
Dries committed
29
30
31
32
33
34
 * Hook Help - returns theme specific help and information.
 *
 * @param section defines the @a section of the help to be returned.
 *
 * @return a string containing the help output.
 */
Dries's avatar
   
Dries committed
35
36
function theme_help($section) {
  switch ($section) {
Dries's avatar
   
Dries committed
37
    case 'admin/themes#description':
38
      return t('The base theme');
Dries's avatar
   
Dries committed
39
40
41
42
  }
}

/**
Dries's avatar
   
Dries committed
43
 * Initialize the theme system by loading the theme.
Dries's avatar
   
Dries committed
44
 *
45
46
 * @return
 *   The name of the currently selected theme.
Dries's avatar
   
Dries committed
47
 */
Dries's avatar
   
Dries committed
48
function init_theme() {
Dries's avatar
   
Dries committed
49
  global $user, $custom_theme, $theme_engine, $theme_key;
Dries's avatar
   
Dries committed
50
51
52

  $themes = list_themes();

53
54
  // Only select the user selected theme if it is available in the
  // list of enabled themes.
Dries's avatar
   
Dries committed
55
  $theme = $user->theme && $themes[$user->theme]->status ? $user->theme : variable_get('theme_default', 'bluemarine');
Dries's avatar
   
Dries committed
56
57

  // Allow modules to override the present theme... only select custom theme
Dries's avatar
   
Dries committed
58
  // if it is available in the list of installed themes.
59
  $theme = $custom_theme && $themes[$custom_theme] ? $custom_theme : $theme;
Dries's avatar
   
Dries committed
60
61
62
63
64
65
66
67
68

  // Store the identifier for retrieving theme settings with.
  $theme_key = $theme;

  // If we're using a style, load its appropriate theme,
  // which is stored in the style's description field.
  // Also load the stylesheet using drupal_set_html_head().
  // Otherwise, load the theme.
  if (strpos($themes[$theme]->filename, '.css')) {
69
    // File is a style; loads its CSS.
Dries's avatar
   
Dries committed
70
    // Set theme to its template/theme
71
    theme_add_style($themes[$theme]->filename);
72
    $theme = basename(dirname($themes[$theme]->description));
Dries's avatar
   
Dries committed
73
74
75
  }
  else {
    // File is a template/theme
76
    // Load its CSS, if it exists
Dries's avatar
   
Dries committed
77
    if (file_exists($stylesheet = dirname($themes[$theme]->filename) .'/style.css')) {
78
      theme_add_style($stylesheet);
Dries's avatar
   
Dries committed
79
80
    }
  }
Dries's avatar
   
Dries committed
81

Dries's avatar
   
Dries committed
82
83
84
85
86
87
88
89
90
91
92
93
  if (strpos($themes[$theme]->filename, '.theme')) {
    // file is a theme; include it
    include_once($themes[$theme]->filename);
  }
  elseif (strpos($themes[$theme]->description, '.engine')) {
    // file is a template; include its engine
    include_once($themes[$theme]->description);
    $theme_engine = basename($themes[$theme]->description, '.engine');
    if (function_exists($theme_engine .'_init')) {
      call_user_func($theme_engine .'_init', $themes[$theme]);
    }
  }
94

Dries's avatar
   
Dries committed
95
96
97
98
  return $theme;
}

/**
Dries's avatar
   
Dries committed
99
100
101
 * Provides a list of currently available themes.
 *
 * @param $refresh
102
103
104
 *   Whether to reload the list of themes from the database.
 * @return
 *   An array of the currently available themes.
Dries's avatar
   
Dries committed
105
 */
106
function list_themes($refresh = FALSE) {
Dries's avatar
   
Dries committed
107
108
109
110
111
112
113
114
  static $list;

  if ($refresh) {
    unset($list);
  }

  if (!$list) {
    $list = array();
Dries's avatar
   
Dries committed
115
    $result = db_query("SELECT * FROM {system} WHERE type = 'theme' ORDER BY name");
Dries's avatar
   
Dries committed
116
117
118
119
120
121
122
123
124
125
    while ($theme = db_fetch_object($result)) {
      if (file_exists($theme->filename)) {
        $list[$theme->name] = $theme;
      }
    }
  }

  return $list;
}

Dries's avatar
   
Dries committed
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
/**
 * Provides a list of currently available theme engines
 *
 * @param $refresh
 *   Whether to reload the list of themes from the database.
 * @return
 *   An array of the currently available theme engines.
 */
function list_theme_engines($refresh = FALSE) {
  static $list;

  if ($refresh) {
    unset($list);
  }

  if (!$list) {
    $list = array();
Dries's avatar
   
Dries committed
143
    $result = db_query("SELECT * FROM {system} WHERE type = 'theme_engine' AND status = '1' ORDER BY name");
Dries's avatar
   
Dries committed
144
145
146
147
148
149
150
151
152
153
    while ($engine = db_fetch_object($result)) {
      if (file_exists($engine->filename)) {
        $list[$engine->name] = $engine;
      }
    }
  }

  return $list;
}

Dries's avatar
   
Dries committed
154
/**
155
 * Generate the themed representation of a Drupal object.
Dries's avatar
   
Dries committed
156
157
158
 *
 * All requests for themed functions must go through this function. It examines
 * the request and routes it to the appropriate theme function. If the current
Dries's avatar
   
Dries committed
159
160
161
 * theme does not implement the requested function, then the current theme
 * engine is checked. If neither the engine nor theme implement the requested
 * function, then the base theme function is called.
162
163
164
 *
 * For example, to retrieve the HTML that is output by theme_page($output), a
 * module should call theme('page', $output).
Dries's avatar
   
Dries committed
165
 *
166
167
168
169
170
171
 * @param $function
 *   The name of the theme function to call.
 * @param ...
 *   Additional arguments to pass along to the theme function.
 * @return
 *   An HTML string that generates the themed output.
Dries's avatar
   
Dries committed
172
 */
Dries's avatar
   
Dries committed
173
function theme() {
Dries's avatar
   
Dries committed
174
175
176
177
178
179
  global $theme, $theme_engine;

  if (!$theme) {
    // Initialize the enabled theme.
    $theme = init_theme();
  }
Dries's avatar
   
Dries committed
180
181
182
183

  $args = func_get_args();
  $function = array_shift($args);

Dries's avatar
   
Dries committed
184
185
  if (($theme != '') && function_exists($theme .'_'. $function)) {
    // call theme function
186
    return call_user_func_array($theme .'_'. $function, $args);
Dries's avatar
   
Dries committed
187
  }
Dries's avatar
   
Dries committed
188
189
190
191
  elseif (($theme != '') && isset($theme_engine) && function_exists($theme_engine .'_'. $function)) {
    // call engine function
    return call_user_func_array($theme_engine .'_'. $function, $args);
  }
192
  elseif (function_exists('theme_'. $function)){
Dries's avatar
   
Dries committed
193
    // call Drupal function
194
    return call_user_func_array('theme_'. $function, $args);
Dries's avatar
   
Dries committed
195
196
197
198
  }
}

/**
199
 * Return the path to the currently selected theme.
Dries's avatar
   
Dries committed
200
 */
Dries's avatar
   
Dries committed
201
202
function path_to_theme() {
  global $theme;
Dries's avatar
   
Dries committed
203
204
205
206

  $themes = list_themes();

  return dirname($themes[$theme]->filename);
Dries's avatar
   
Dries committed
207
208
}

Dries's avatar
   
Dries committed
209
210
211
/**
 * Retrieve an associative array containing the settings for a theme.
 *
212
 * The final settings are arrived at by merging the default settings,
Dries's avatar
   
Dries committed
213
214
215
216
217
218
219
220
221
222
223
224
225
 * the site-wide settings, and the settings defined for the specific theme.
 * If no $key was specified, only the site-wide theme defaults are retrieved.
 *
 * The default values for each of settings are also defined in this function.
 * To add new settings, add their default values here, and then add form elements
 * to system_theme_settings() in system.module.
 *
 * @param $key
 *  The template/style value for a given theme.
 *
 * @return
 *   An associative array containing theme settings.
 */
226
function theme_get_settings($key = NULL) {
Dries's avatar
   
Dries committed
227
  $defaults = array(
228
229
    'primary_links'                 =>  l(t('edit primary links'), 'admin/themes/settings'),
    'secondary_links'               =>  l(t('edit secondary links'), 'admin/themes/settings'),
Dries's avatar
   
Dries committed
230
231
232
233
234
235
236
237
238
239
240
241
242
243
    'mission'                       =>  '',
    'default_logo'                  =>  1,
    'logo_path'                     =>  '',
    'toggle_logo'                   =>  1,
    'toggle_name'                   =>  1,
    'toggle_search'                 =>  1,
    'toggle_slogan'                 =>  0,
    'toggle_mission'                =>  1,
    'toggle_primary_links'          =>  1,
    'toggle_secondary_links'        =>  1,
    'toggle_node_user_picture'      =>  0,
    'toggle_comment_user_picture'   =>  0,
  );

244
245
246
247
  if (module_exist('node')) {
    foreach (node_list() as $type) {
      $defaults['toggle_node_info_' . $type] = 1;
    }
Dries's avatar
   
Dries committed
248
249
250
251
252
253
254
  }
  $settings = array_merge($defaults, variable_get('theme_settings', array()));

  if ($key) {
    $settings = array_merge($settings, variable_get(str_replace('/', '_', 'theme_'. $key .'_settings'), array()));
  }

255
256
257
258
259
  // Only offer search box if search.module is enabled.
  if (!module_exist('search')) {
    $settings['toggle_search'] = 0;
  }

Dries's avatar
   
Dries committed
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
  return $settings;
}

/**
 * Retrieve a setting for the current theme.
 * This function is designed for use from within themes & engines
 * to determine theme settings made in the admin interface.
 *
 * Caches values for speed (use $refresh = TRUE to refresh cache)
 *
 * @param $setting_name
 *  The name of the setting to be retrieved.
 *
 * @param $refresh
 *  Whether to reload the cache of settings.
 *
 * @return
 *   The value of the requested setting, NULL if the setting does not exist.
 */
279
function theme_get_setting($setting_name, $refresh = FALSE) {
Dries's avatar
   
Dries committed
280
281
282
283
  global $theme_key;
  static $settings;

  if (empty($settings) || $refresh) {
284
    $settings = theme_get_settings($theme_key);
Dries's avatar
   
Dries committed
285
286
287
288
289
290
291
292
293
294
295
296
297
298

    $themes = list_themes();
    $theme_object = $themes[$theme_key];

    if ($settings['mission'] == '') {
      $settings['mission'] = variable_get('site_mission', '');
    }

    if (!$settings['toggle_mission']) {
      $settings['mission'] = '';
    }

    if ($settings['toggle_logo']) {
      if ($settings['default_logo']) {
299
        $settings['logo'] = dirname($theme_object->filename) .'/logo.png';
Dries's avatar
   
Dries committed
300
301
302
303
304
305
      }
      elseif ($settings['logo_path']) {
        $settings['logo'] = $settings['logo_path'];
      }
    }

306
    if (!$settings['toggle_primary_links']) {
Dries's avatar
   
Dries committed
307
308
309
310
311
312
313
314
315
316
317
      $settings['primary_links'] = '';
    }

    if (!$settings['toggle_secondary_links']) {
      $settings['secondary_links'] = '';
    }
  }

  return isset($settings[$setting_name]) ? $settings[$setting_name] : NULL;
}

318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
/**
 * Add a theme stylesheet to be included later. This is handled separately from
 * drupal_set_html_head() to enforce the correct CSS cascading order.
 */
function theme_add_style($style = '') {
  static $styles = array();
  if ($style) {
    $styles[] = $style;
  }
  return $styles;
}

/**
 * Return the HTML for a theme's stylesheets.
 */
function theme_get_styles() {
  $output = '';
  foreach (theme_add_style() as $style) {
    $output .= theme('stylesheet_import', $style);
  }
  return $output;
}

341
342
343
/**
 * @defgroup themeable Themeable functions
 * @{
Dries's avatar
   
Dries committed
344
 * Functions that display HTML, and which can be customized by themes.
345
 *
346
347
348
349
 * All functions that produce HTML for display should be themeable. This means
 * that they should be named with the theme_ prefix, and invoked using theme()
 * rather than being called directly. This allows themes to override the display
 * of any Drupal object.
350
 *
Dries's avatar
   
Dries committed
351
 * The theme system is described and defined in theme.inc.
352
 */
353

354
355
356
357
358
359
360
361
362
363
364
365
366
/**
 * Format a dynamic text string for emphasised display in a placeholder.
 *
 * E.g. t('Added term %term', array('%term' => theme('placeholder', $term)))
 *
 * @param $text
 *   The text to format (plain-text).
 * @return
 *   The formatted text (html).
 */
function theme_placeholder($text) {
  return '<em>'. check_plain($text) .'</em>';
}
Dries's avatar
   
Dries committed
367

Dries's avatar
   
Dries committed
368
/**
369
370
371
372
373
374
 * Return an entire Drupal page displaying the supplied content.
 *
 * @param $content
 *   A string to display in the main content area of the page.
 * @return
 *   A string containing the entire HTML page.
Dries's avatar
   
Dries committed
375
 */
376
function theme_page($content) {
Dries's avatar
   
Dries committed
377
  $output = "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n";
378
379
  $output .= '<html xmlns="http://www.w3.org/1999/xhtml">';
  $output .= '<head>';
380
  $output .= ' <title>'. (drupal_get_title() ? strip_tags(drupal_get_title()) : variable_get('site_name', 'drupal')) .'</title>';
Dries's avatar
Dries committed
381
  $output .= drupal_get_html_head();
382
  $output .= theme_get_styles();
Dries's avatar
   
Dries committed
383

384
385
386
  $output .= ' </head>';
  $output .= ' <body style="background-color: #fff; color: #000;"'. theme('onload_attribute'). '">';
  $output .= '<table border="0" cellspacing="4" cellpadding="4"><tr><td style="vertical-align: top; width: 170px;">';
Dries's avatar
   
Dries committed
387

388
389
  $output .= theme('blocks', 'all');
  $output .= '</td><td style="vertical-align: top;">';
Dries's avatar
   
Dries committed
390

391
392
  $output .= theme('breadcrumb', drupal_get_breadcrumb());
  $output .= '<h1>' . drupal_get_title() . '</h1>';
Dries's avatar
   
Dries committed
393
394
395
396
397

  if ($tabs = theme('menu_local_tasks')) {
   $output .= $tabs;
  }

Dries's avatar
   
Dries committed
398
  if ($help = menu_get_active_help()) {
399
    $output .= '<small>'. $help .'</small><hr />';
Dries's avatar
   
Dries committed
400
401
  }

Dries's avatar
   
Dries committed
402
403
  $output .= theme_status_messages();

Dries's avatar
   
Dries committed
404
  $output .= "\n<!-- begin content -->\n";
Dries's avatar
   
Dries committed
405
  $output .= $content;
Dries's avatar
   
Dries committed
406
407
  $output .= "\n<!-- end content -->\n";

408
  $output .= '</td></tr></table>';
Dries's avatar
   
Dries committed
409
  $output .= theme_closure();
410
  $output .= '</body></html>';
Dries's avatar
   
Dries committed
411
412
413
414

  return $output;
}

Dries's avatar
   
Dries committed
415
416
417
418
/**
 * Returns themed set of status and/or error messages.  The messages are grouped
 * by type.
 *
419
420
 * @return
 *   A string containing the messages.
Dries's avatar
   
Dries committed
421
422
423
424
425
426
427
428
429
 */
function theme_status_messages() {
  if ($data = drupal_get_messages()) {
    $output = '';
    foreach ($data as $type => $messages) {
      $output .= "<div class=\"messages $type\">\n";
      if (count($messages) > 1) {
        $output .= " <ul>\n";
        foreach($messages as $message) {
Dries's avatar
   
Dries committed
430
          $output .= '  <li>'. $message ."</li>\n";
Dries's avatar
   
Dries committed
431
432
433
434
        }
        $output .= " </ul>\n";
      }
      else {
Dries's avatar
   
Dries committed
435
        $output .= $messages[0];
Dries's avatar
   
Dries committed
436
437
438
439
440
441
442
443
      }
      $output .= "</div>\n";
    }

    return $output;
  }
}

Dries's avatar
   
Dries committed
444
/**
445
446
447
448
449
450
451
452
 * Return a themed set of links.
 *
 * @param $links
 *   An array of links to be themed.
 * @param $delimiter
 *   A string used to separate the links.
 * @return
 *   A string containing the themed links.
Dries's avatar
   
Dries committed
453
 */
454
function theme_links($links, $delimiter = ' | ') {
Dries's avatar
   
Dries committed
455
456
  return implode($delimiter, $links);
}
Dries's avatar
   
Dries committed
457

Dries's avatar
   
Dries committed
458
/**
459
 * Return a themed image.
Dries's avatar
   
Dries committed
460
 *
Dries's avatar
   
Dries committed
461
462
463
464
465
466
467
 * @param $path
 *   The path of the image file.
 * @param $alt
 *   The alternative text for text-based browsers.
 * @param $title
 *   The title text is displayed when the image is hovered in some popular browsers.
 * @param $attr
468
 *   Attributes placed in the img tag.
Steven Wittens's avatar
Steven Wittens committed
469
470
 * @param $getsize
 *   If set to true, the image's dimension are fetched and added as width/height attributes.
471
 * @return
Dries's avatar
   
Dries committed
472
 *   A string containing the image tag.
Dries's avatar
   
Dries committed
473
 */
Steven Wittens's avatar
Steven Wittens committed
474
475
function theme_image($path, $alt = '', $title = '', $attr = '', $getsize = true) {
  if (!$getsize || (file_exists($path) && (list($width, $height, $type, $attr) = @getimagesize($path)))) {
Dries's avatar
   
Dries committed
476
477
    return "<img src=\"$path\" $attr alt=\"$alt\" title=\"$title\" />";
  }
Dries's avatar
   
Dries committed
478
}
Dries's avatar
   
Dries committed
479

Dries's avatar
   
Dries committed
480
/**
481
 * Return a themed breadcrumb trail.
Dries's avatar
   
Dries committed
482
 *
483
484
485
 * @param $breadcrumb
 *   An array containing the breadcrumb links.
 * @return a string containing the breadcrumb output.
Dries's avatar
   
Dries committed
486
 */
Dries's avatar
   
Dries committed
487
function theme_breadcrumb($breadcrumb) {
488
  return '<div class="breadcrumb">'. implode($breadcrumb, ' &raquo; ') .'</div>';
Dries's avatar
   
Dries committed
489
}
Dries's avatar
   
Dries committed
490

Dries's avatar
   
Dries committed
491
/**
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
 * Return a themed node.
 *
 * @param $node
 *   An object providing all relevant information for displaying a node:
 *   - $node->nid: The ID of the node.
 *   - $node->type: The content type (story, blog, forum...).
 *   - $node->title: The title of the node.
 *   - $node->created: The creation date, as a UNIX timestamp.
 *   - $node->teaser: A shortened version of the node body.
 *   - $node->body: The entire node contents.
 *   - $node->changed: The last modification date, as a UNIX timestamp.
 *   - $node->uid: The ID of the author.
 *   - $node->username: The username of the author.
 * @param $teaser
 *   Whether to display the teaser only, as on the main page.
 * @param $page
 *   Whether to display the node as a standalone page. If TRUE, do not display
 *   the title because it will be provided by the menu system.
 * @return
 *   A string containing the node output.
Dries's avatar
   
Dries committed
512
 */
513
514
515
function theme_node($node, $teaser = FALSE, $page = FALSE) {
  if (module_exist('taxonomy')) {
    $terms = taxonomy_link('taxonomy terms', $node);
Dries's avatar
   
Dries committed
516
517
  }

Dries's avatar
   
Dries committed
518
  if ($page == 0) {
519
    $output = '<h2 class="title">'. check_plain($node->title) .'</h2> by '. format_name($node);
Dries's avatar
   
Dries committed
520
521
  }
  else {
522
    $output = 'by '. format_name($node);
Dries's avatar
   
Dries committed
523
  }
Dries's avatar
   
Dries committed
524
525

  if (count($terms)) {
526
    $output .= ' <small>('. theme('links', $terms) .')</small><br />';
Dries's avatar
   
Dries committed
527
528
  }

529
  if ($teaser && $node->teaser) {
Dries's avatar
   
Dries committed
530
531
532
533
    $output .= $node->teaser;
  }
  else {
    $output .= $node->body;
Dries's avatar
   
Dries committed
534
535
  }

536
537
  if ($node->links) {
    $output .= '<div class="links">'. theme('links', $node->links) .'</div>';
Dries's avatar
   
Dries committed
538
  }
Dries's avatar
   
Dries committed
539

Dries's avatar
   
Dries committed
540
  return $output;
Dries's avatar
   
Dries committed
541
542
}

Dries's avatar
   
Dries committed
543
/**
544
 * Return a themed form element.
Dries's avatar
   
Dries committed
545
546
547
548
 *
 * @param $title the form element's title
 * @param $value the form element's data
 * @param $description the form element's description or explanation
Dries's avatar
   
Dries committed
549
 * @param $id the form element's ID used by the &lt;label&gt; tag
Dries's avatar
   
Dries committed
550
551
 * @param $required a boolean to indicate whether this is a required field or not
 * @param $error a string with an error message filed against this form element
Dries's avatar
   
Dries committed
552
 *
Dries's avatar
   
Dries committed
553
 * @return a string representing the form element
Dries's avatar
   
Dries committed
554
 */
Dries's avatar
   
Dries committed
555
function theme_form_element($title, $value, $description = NULL, $id = NULL, $required = FALSE, $error = FALSE) {
556
557

  $output  = "<div class=\"form-item\">\n";
558
  $required = $required ? '<span class="form-required">*</span>' : '';
Dries's avatar
   
Dries committed
559
560
561

  if ($title) {
    if ($id) {
562
      $output .= " <label for=\"$id\">$title:</label>$required<br />\n";
Dries's avatar
   
Dries committed
563
564
    }
    else {
565
      $output .= " <label>$title:</label>$required<br />\n";
Dries's avatar
   
Dries committed
566
567
568
    }
  }

569
570
  $output .= " $value\n";

Dries's avatar
   
Dries committed
571
  if ($description) {
572
    $output .= " <div class=\"description\">$description</div>\n";
Dries's avatar
   
Dries committed
573
574
  }

575
576
577
  $output .= "</div>\n";

  return $output;
Dries's avatar
   
Dries committed
578
}
Dries's avatar
   
Dries committed
579

Dries's avatar
   
Dries committed
580
581

/**
582
 * Return a themed submenu, typically displayed under the tabs.
Dries's avatar
   
Dries committed
583
 *
584
585
 * @param $links
 *   An array of links.
Dries's avatar
   
Dries committed
586
587
 */
function theme_submenu($links) {
588
  return '<div class="submenu">'. implode(' | ', $links) .'</div>';
Dries's avatar
   
Dries committed
589
590
}

Dries's avatar
   
Dries committed
591
/**
592
593
594
595
596
597
598
599
600
601
602
 * Return a themed table.
 *
 * @param $header
 *   An array containing the table headers. Each element of the array can be
 *   either a localized string or an associative array with the following keys:
 *   - "data": The localized title of the table column.
 *   - "field": The database field represented in the table column (required if
 *     user is to be able to sort on this column).
 *   - "sort": A default sort order for this column ("asc" or "desc").
 *   - Any HTML attributes, such as "colspan", to apply to the column header cell.
 * @param $rows
603
604
605
606
607
608
 *   An array of table rows. Every row is an array of cells, or an associative
 *   array with the following keys:
 *   - "data": an array of cells
 *   - Any HTML attributes, such as "class", to apply to the table row.
 *
 *   Each cell can be either a string or an associative array with the following keys:
609
610
 *   - "data": The string to display in the table cell.
 *   - Any HTML attributes, such as "colspan", to apply to the table cell.
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
 *
 *   Here's an example for $rows:
 *   @verbatim
 *   $rows = array(
 *     // Simple row
 *     array(
 *       'Cell 1', 'Cell 2', 'Cell 3'
 *     ),
 *     // Row with attributes on the row and some of its cells.
 *     array(
 *       'data' => array('Cell 1', array('data' => 'Cell 2', 'colspan' => 2)), 'class' => 'funky'
 *     )
 *   );
 *   @endverbatim
 *
626
627
628
629
 * @param $attributes
 *   An array of HTML attributes to apply to the table tag.
 * @return
 *   An HTML string representing the table.
Dries's avatar
   
Dries committed
630
 */
631
function theme_table($header, $rows, $attributes = NULL) {
Dries's avatar
   
Dries committed
632

Dries's avatar
   
Dries committed
633
  $output = '<table'. drupal_attributes($attributes) .">\n";
Dries's avatar
   
Dries committed
634

635
  // Format the table header:
Dries's avatar
   
Dries committed
636
  if (is_array($header)) {
Dries's avatar
   
Dries committed
637
    $ts = tablesort_init($header);
638
    $output .= ' <tr>';
Dries's avatar
   
Dries committed
639
    foreach ($header as $cell) {
640
      $cell = tablesort_header($cell, $header, $ts);
Dries's avatar
   
Dries committed
641
642
643
644
645
      $output .= _theme_table_cell($cell, 1);
    }
    $output .= " </tr>\n";
  }

646
  // Format the table rows:
Dries's avatar
   
Dries committed
647
648
  if (is_array($rows)) {
    foreach ($rows as $number => $row) {
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
      $attributes = array();

      // Check if we're dealing with a simple or complex row
      if (isset($row['data'])) {
        foreach ($row as $key => $value) {
          if ($key == 'data') {
            $cells = $value;
          }
          else {
            $attributes[$key] = $value;
          }
        }
      }
      else {
        $cells = $row;
      }

      // Add light/dark class
      $class = ($number % 2 == 1) ? 'light': 'dark';
      if (isset($attributes['class'])) {
        $attributes['class'] .= ' '. $class;
Dries's avatar
   
Dries committed
670
671
      }
      else {
672
        $attributes['class'] = $class;
Dries's avatar
   
Dries committed
673
674
      }

675
676
      // Build row
      $output .= ' <tr'. drupal_attributes($attributes) .'>';
677
      $i = 0;
678
679
      foreach ($cells as $cell) {
        $cell = tablesort_cell($cell, $header, $ts, $i++);
Dries's avatar
   
Dries committed
680
681
682
683
684
685
686
687
688
689
        $output .= _theme_table_cell($cell, 0);
      }
      $output .= " </tr>\n";
    }
  }

  $output .= "</table>\n";
  return $output;
}

Dries's avatar
   
Dries committed
690
/**
691
692
693
694
695
696
697
698
699
700
 * Return a themed box.
 *
 * @param $title
 *   The subject of the box.
 * @param $content
 *   The content of the box.
 * @param $region
 *   The region in which the box is displayed.
 * @return
 *   A string containing the box output.
Dries's avatar
   
Dries committed
701
 */
Dries's avatar
   
Dries committed
702
function theme_box($title, $content, $region = 'main') {
703
  $output = '<h2 class="title">'. $title .'</h2><div>'. $content .'</div>';
Dries's avatar
   
Dries committed
704
  return $output;
Dries's avatar
   
Dries committed
705
706
707
}

/**
708
 * Return a themed block.
Dries's avatar
   
Dries committed
709
710
711
712
713
714
 *
 * You can style your blocks by defining .block (all blocks),
 * .block-<i>module</i> (all blocks of module <i>module</i>), and
 * \#block-<i>module</i>-<i>delta</i> (specific block of module <i>module</i>
 * with delta <i>delta</i>) in your theme's CSS.
 *
715
716
 * @param $block
 *   An object populated with fields from the "blocks" database table
Dries's avatar
   
Dries committed
717
 *   ($block->module, $block->delta, $block->region, ...) and fields returned by
718
719
720
 *   <i>module</i>_block('view') ($block->subject, $block->content, ...).
 * @return
 *   A string containing the block output.
Dries's avatar
   
Dries committed
721
 */
Dries's avatar
   
Dries committed
722
function theme_block($block) {
Dries's avatar
   
Dries committed
723
724
725
726
  $output  = "<div class=\"block block-$block->module\" id=\"block-$block->module-$block->delta\">\n";
  $output .= " <h2 class=\"title\">$block->subject</h2>\n";
  $output .= " <div class=\"content\">$block->content</div>\n";
  $output .= "</div>\n";
Dries's avatar
   
Dries committed
727
  return $output;
Dries's avatar
   
Dries committed
728
}
Dries's avatar
   
Dries committed
729

Dries's avatar
   
Dries committed
730
/**
731
732
 * Return a themed marker, useful for marking new or updated
 * content.
Dries's avatar
   
Dries committed
733
 *
734
 * @param $type
735
736
 *   Number representing the marker type to display
 * @see MARK_NEW, MARK_UPDATED, MARK_READ
737
738
 * @return
 *   A string containing the marker.
Dries's avatar
   
Dries committed
739
 */
740
741
742
743
744
function theme_mark($type = MARK_NEW) {
  global $user;
  if ($user->uid && $type != MARK_READ) {
    return '<span class="marker">*</span>';
  }
Dries's avatar
   
Dries committed
745
746
}

Dries's avatar
   
Dries committed
747
748
/**
 * Import a stylesheet using @import.
749
 *
Dries's avatar
   
Dries committed
750
751
752
753
754
755
756
757
758
759
760
761
762
 * @param $stylesheet
 *  The filename to point the link at.
 *
 * @param $media
 *  The media type to specify for the stylesheet
 *
 * @return
 *  A string containing the HTML for the stylesheet import.
 */
function theme_stylesheet_import($stylesheet, $media = 'all') {
  return '<style type="text/css" media="'. $media .'">@import "'. $stylesheet .'";</style>';
}

Dries's avatar
   
Dries committed
763
/**
764
765
766
767
768
769
770
771
 * Return a themed list of items.
 *
 * @param $items
 *   An array of items to be displayed in the list.
 * @param $title
 *   The title of the list.
 * @return
 *   A string containing the list output.
Dries's avatar
   
Dries committed
772
 */
Dries's avatar
   
Dries committed
773
function theme_item_list($items = array(), $title = NULL) {
774
  $output = '<div class="item-list">';
Dries's avatar
   
Dries committed
775
  if (isset($title)) {
776
    $output .= '<h3>'. $title .'</h3>';
Dries's avatar
   
Dries committed
777
778
  }

Dries's avatar
   
Dries committed
779
  if (isset($items)) {
780
    $output .= '<ul>';
Dries's avatar
   
Dries committed
781
    foreach ($items as $item) {
782
      $output .= '<li>'. $item .'</li>';
Dries's avatar
   
Dries committed
783
    }
784
    $output .= '</ul>';
Dries's avatar
   
Dries committed
785
  }
786
  $output .= '</div>';
Dries's avatar
   
Dries committed
787
788
789
  return $output;
}

Dries's avatar
   
Dries committed
790
/**
791
 * Return a themed error message.
Dries's avatar
   
Dries committed
792
 * REMOVE: this function is deprecated an no longer used in core.
Dries's avatar
   
Dries committed
793
 *
794
795
 * @param $message
 *   The error message to be themed.
Dries's avatar
   
Dries committed
796
 *
797
798
 * @return
 *   A string containing the error output.
Dries's avatar
   
Dries committed
799
 */
Dries's avatar
   
Dries committed
800
function theme_error($message) {
801
  return '<div class="error">'. $message .'</div>';
Dries's avatar
   
Dries committed
802
803
}

Dries's avatar
   
Dries committed
804
function theme_more_help_link($url) {
Dries's avatar
   
Dries committed
805
  return '<div class="more-help-link">' . t('[<a href="%link">more help...</a>]', array('%link' => $url)) . '</div>';
Dries's avatar
   
Dries committed
806
807
}

Dries's avatar
   
Dries committed
808
/**
809
 * Return code that emits an XML icon.
Dries's avatar
   
Dries committed
810
811
 */
function theme_xml_icon($url) {
Dries's avatar
   
Dries committed
812
813
814
  if ($image = theme('image', 'misc/xml.png', t('XML feed'), t('XML feed'))) {
    return '<div class="xml-icon"><a href="'. $url .'">'. $image. '</a></div>';
  }
Dries's avatar
   
Dries committed
815
816
}

Dries's avatar
   
Dries committed
817
/**
818
819
 * Execute hook_footer() which is run at the end of the page right before the
 * close of the body tag.
Dries's avatar
   
Dries committed
820
821
822
 *
 * @param $main (optional)
 *
823
824
 * @return
 *   A string containing the results of the hook_footer() calls.
Dries's avatar
   
Dries committed
825
 */
Dries's avatar
   
Dries committed
826
function theme_closure($main = 0) {
Dries's avatar
   
Dries committed
827
  $footer = module_invoke_all('footer', $main);
Dries's avatar
   
Dries committed
828
829
830
  return implode($footer, "\n");
}

Dries's avatar
   
Dries committed
831
/**
832
 * Call hook_onload() in all modules to enable modules to insert JavaScript that
Dries's avatar
   
Dries committed
833
834
 * will get run once the page has been loaded by the browser.
 *
835
836
837
838
 * @param $theme_onloads
 *   Additional onload directives.
 * @return
 *   A string containing the onload attributes.
Dries's avatar
   
Dries committed
839
 */
Dries's avatar
   
Dries committed
840
841
842
function theme_onload_attribute($theme_onloads = array()) {
  if (!is_array($theme_onloads)) {
    $theme_onloads = array($theme_onloads);
Dries's avatar
   
Dries committed
843
  }
Dries's avatar
   
Dries committed
844
845
  // Merge theme onloads (javascript rollovers, image preloads, etc.)
  // with module onloads (htmlarea, etc.)
846
  $onloads = array_merge(module_invoke_all('onload'), $theme_onloads);
Dries's avatar
   
Dries committed
847
  if (count($onloads)) {
848
    return ' onload="' . implode('; ', $onloads) . '"';
Dries's avatar
   
Dries committed
849
  }
Dries's avatar
   
Dries committed
850
  return '';
Dries's avatar
   
Dries committed
851
852
}

Dries's avatar
   
Dries committed
853
/**
854
 * Return a set of blocks available for the current user.
855
 *
856
857
858
859
 * @param $region
 *   Which set of blocks to retrieve.
 * @return
 *   A string containing the themed blocks for this region.
860
861
 */
function theme_blocks($region) {
Dries's avatar
   
Dries committed
862
  $output = '';
863
864
865
866
867

  if ($list = module_invoke('block', 'list', $region)) {
    foreach ($list as $key => $block) {
      // $key == <i>module</i>_<i>delta</i>
      $output .= theme('block', $block);
Dries's avatar
   
Dries committed
868
869
    }
  }
Dries's avatar
   
Dries committed
870
  return $output;
Dries's avatar
   
Dries committed
871
}
872
873
874
875
876
877
878
879
880

/**
 * Output a confirmation form
 *
 * This function outputs a complete form for confirming an action. A link is
 * offered to go back to the item that is being changed in case the user changes
 * his/her mind.
 *
 * You should use $_POST['edit'][$name] (where $name is usually 'confirm') to
881
 * check if the confirmation was successful.
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
 *
 * @param $question
 *   The question to ask the user (e.g. "Are you sure you want to delete the
 *   block <em>foo</em>?").
 * @param $path
 *   The page to go to if the user denies the action.
 * @param $description
 *   Additional text to display (defaults to "This action cannot be undone.").
 * @param $yes
 *   A caption for the button which confirms the action (e.g. "Delete",
 *   "Replace", ...).
 * @param $no
 *   A caption for the link which denies the action (e.g. "Cancel").
 * @param $extra
 *   Additional HTML to inject into the form, for example form_hidden()s.
 * @param $name
 *   The internal name used to refer to the confirmation item.
 * @return
 *   A themed HTML string representing the form.
 */
function theme_confirm($question, $path, $description = NULL, $yes = NULL, $no = NULL, $extra = NULL, $name = 'confirm') {
  drupal_set_title($question);

  if (is_null($description)) {
    $description = t('This action cannot be undone.');
  }

  $output .= '<p>'. $description ."</p>\n";
  if (!is_null($extra)) {
    $output .= $extra;
  }
  $output .= '<div class="container-inline">';
  $output .= form_submit($yes ? $yes : t('Confirm'));
  $output .= l($no ? $no : t('Cancel'), $path);
  $output .= "</div>\n";

  $output .= form_hidden($name, 1);
  return form($output, 'post', NULL, array('class' => 'confirmation'));
}


923
/**
Dries's avatar
   
Dries committed
924
 * @} End of "defgroup themeable".
925
 */
926

Dries's avatar
   
Dries committed
927
function _theme_table_cell($cell, $header = 0) {
Dries's avatar
   
Dries committed
928
929
  $attributes = '';

Dries's avatar
   
Dries committed
930
  if (is_array($cell)) {
Dries's avatar
   
Dries committed
931
    $data = $cell['data'];
Dries's avatar
   
Dries committed
932
    foreach ($cell as $key => $value) {
Dries's avatar
   
Dries committed
933
      if ($key != 'data')  {
Dries's avatar
   
Dries committed
934
        $attributes .= " $key=\"$value\"";
Dries's avatar
   
Dries committed
935
936
      }
    }
Dries's avatar
   
Dries committed
937
  }
Dries's avatar
   
Dries committed
938
939
  else {
    $data = $cell;
Dries's avatar
   
Dries committed
940
  }
Dries's avatar
   
Dries committed
941

Dries's avatar
   
Dries committed
942
943
  if ($header) {
    $output = "<th$attributes>$data</th>";
Dries's avatar
   
Dries committed
944
  }
Dries's avatar
   
Dries committed
945
946
  else {
    $output = "<td$attributes>$data</td>";
Dries's avatar
   
Dries committed
947
  }
Dries's avatar
   
Dries committed
948

Dries's avatar
   
Dries committed
949
950
  return $output;
}
Dries's avatar
   
Dries committed
951
?>