Issue #1905006: OCD-required replacement of tab characters with 2 spaces.
[project/zen.git] / template.php
1 <?php
2 /**
3 * @file
4 * File which contains theme overrides for the Zen theme.
5 *
6 * ABOUT
7 *
8 * The template.php file is one of the most useful files when creating or
9 * modifying Drupal themes. You can add new regions for block content, modify or
10 * override Drupal's theme functions, intercept or make additional variables
11 * available to your theme, and create custom PHP logic. For more information,
12 * please visit the Theme Developer's Guide on Drupal.org:
13 * http://drupal.org/theme-guide
14 *
15 * NOTE ABOUT ZEN'S TEMPLATE.PHP FILE
16 *
17 * The base Zen theme is designed to be easily extended by its sub-themes. You
18 * shouldn't modify this or any of the CSS or PHP files in the root zen/ folder.
19 * See the online documentation for more information:
20 * http://drupal.org/node/193318
21 */
22
23
24 /*
25 * To make this file easier to read, we split up the code into managable parts.
26 * Theme developers are likely to only be interested in functions that are in
27 * this main template.php file.
28 */
29
30 // Sub-theme support
31 include_once 'template-subtheme.php';
32
33 // Initialize theme settings
34 include_once 'theme-settings-init.php';
35
36 // Tabs and menu functions
37 include_once 'template-menus.php';
38
39
40 /**
41 * Declare the available regions implemented by this theme.
42 *
43 * Regions are areas in your theme where you can place blocks. The default
44 * regions used in themes are "left sidebar", "right sidebar", "header", and
45 * "footer", although you can create as many regions as you want. Once declared,
46 * they are made available to the page.tpl.php file as a variable. For instance,
47 * use <?php print $header ?> for the placement of the "header" region in
48 * page.tpl.php.
49 *
50 * By going to the administer > site building > blocks page you can choose
51 * which regions various blocks should be placed. New regions you define here
52 * will automatically show up in the drop-down list by their human readable name.
53 *
54 * @return
55 * An array of regions. The first array element will be used as the default
56 * region for themes. Each array element takes the format:
57 * variable_name => t('human readable name')
58 */
59 function zen_regions() {
60 // Allow a sub-theme to define its own regions.
61 global $theme_key;
62 if ($theme_key != 'zen') {
63 $function = str_replace('-', '_', $theme_key) .'_regions';
64 if (function_exists($function)) {
65 return $function();
66 }
67 }
68
69 return array(
70 'left' => t('left sidebar'),
71 'right' => t('right sidebar'),
72 'navbar' => t('navigation bar'),
73 'content_top' => t('content top'),
74 'content_bottom' => t('content bottom'),
75 'header' => t('header'),
76 'footer' => t('footer'),
77 'closure_region' => t('closure'),
78 );
79 }
80
81
82 /**
83 * Return a themed breadcrumb trail.
84 *
85 * @param $breadcrumb
86 * An array containing the breadcrumb links.
87 * @return
88 * A string containing the breadcrumb output.
89 */
90 function phptemplate_breadcrumb($breadcrumb) {
91 $show_breadcrumb = theme_get_setting('zen_breadcrumb');
92 $show_breadcrumb_home = theme_get_setting('zen_breadcrumb_home');
93 $breadcrumb_separator = theme_get_setting('zen_breadcrumb_separator');
94 $trailing_separator = (theme_get_setting('zen_breadcrumb_trailing') || theme_get_setting('zen_breadcrumb_title')) ? $breadcrumb_separator : '';
95
96 // Determine if we are to display the breadcrumb
97 if ($show_breadcrumb == 'yes' || $show_breadcrumb == 'admin' && arg(0) == 'admin') {
98 if (!$show_breadcrumb_home) {
99 // Optionally get rid of the homepage link
100 array_shift($breadcrumb);
101 }
102 if (!empty($breadcrumb)) {
103 // Return the breadcrumb with separators
104 return '<div class="breadcrumb">'. implode($breadcrumb_separator, $breadcrumb) ."$trailing_separator</div>";
105 }
106 }
107 // Otherwise, return an empty string
108 return '';
109 }
110
111
112 /*
113 * CREATE OR MODIFY VARIABLES FOR YOUR THEME
114 *
115 * The most powerful function available to themers is _phptemplate_variables().
116 * It allows you to pass newly created variables to different template (tpl.php)
117 * files in your theme. Or even unset ones you don't want to use.
118 *
119 * It works by switching on the hook, or name of the theme function, such as:
120 * - page
121 * - node
122 * - comment
123 * - block
124 *
125 * By switching on this hook you can send different variables to page.tpl.php
126 * file, node.tpl.php (and any other derivative node template file, like
127 * node-forum.tpl.php), comment.tpl.php, and block.tpl.php.
128 */
129
130
131 /**
132 * Intercept template variables
133 *
134 * @param $hook
135 * The name of the theme function being called (name of the .tpl.php file.)
136 * @param $vars
137 * A copy of the array containing the variables for the hook.
138 * @return
139 * The array containing additional variables to merge with $vars.
140 */
141 function _phptemplate_variables($hook, $vars = array()) {
142 global $theme_key;
143
144 // Allow modules to add or alter variables.
145 // This construct ensures that we can keep a reference through
146 // call_user_func_array.
147 $args = array(&$vars, $hook);
148 foreach (module_implements('preprocess') as $module) {
149 if ($module != 'search') { // Don't call search_preprocess().
150 $function = $module .'_preprocess';
151 call_user_func_array($function, $args);
152 }
153 }
154 foreach (module_implements('preprocess_'. $hook) as $module) {
155 $function = $module .'_preprocess_'. $hook;
156 call_user_func_array($function, $args);
157 }
158
159 // Allow the Zen base theme to add or alter variables.
160 if ($theme_key != 'zen') {
161 zen_preprocess($vars, $hook);
162 $function = 'zen_preprocess_'. $hook;
163 if (function_exists($function)) {
164 $function($vars, $hook);
165 }
166 }
167
168 // Allow a sub-theme to add or alter variables.
169 $function = $theme_key .'_preprocess';
170 if (function_exists($function)) {
171 $function($vars, $hook);
172 }
173 else {
174 $function = 'phptemplate_preprocess';
175 if (function_exists($function)) {
176 $function($vars, $hook);
177 }
178 }
179 $function = $theme_key .'_preprocess_'. $hook;
180 if (function_exists($function)) {
181 $function($vars, $hook);
182 }
183 else {
184 $function = 'phptemplate_preprocess_'. $hook;
185 if (function_exists($function)) {
186 $function($vars, $hook);
187 }
188 }
189
190 // The following is a deprecated function included for backwards compatibility
191 // with Zen 5.x-0.8 and earlier. New sub-themes should not use this function.
192 if (function_exists('zen_variables')) {
193 $vars = zen_variables($hook, $vars);
194 }
195
196 _zen_hook($hook); // Add support for sub-theme template files.
197
198 return $vars;
199 }
200
201 /**
202 * Override or insert PHPTemplate variables into all templates.
203 *
204 * @param $vars
205 * A sequential array of variables to pass to the theme template.
206 * @param $hook
207 * The name of the theme function being called (name of the .tpl.php file.)
208 */
209 function zen_preprocess(&$vars, $hook) {
210 // Get the currently logged in user
211 global $user;
212
213 // Set a new $is_admin variable. This is determined by looking at the
214 // currently logged in user and seeing if they are in the role 'admin'. The
215 // 'admin' role will need to have been created manually for this to work this
216 // variable is available to all templates.
217 $vars['is_admin'] = in_array('admin', $user->roles);
218
219 // Send a new variable, $logged_in, to tell us if the current user is
220 // logged in or out. An anonymous user has a user id of 0.
221 $vars['logged_in'] = ($user->uid > 0) ? TRUE : FALSE;
222 }
223
224 /**
225 * Override or insert PHPTemplate variables into the page templates.
226 *
227 * @param $vars
228 * A sequential array of variables to pass to the theme template.
229 */
230 function zen_preprocess_page(&$vars) {
231 global $theme, $theme_key;
232
233 // Allow sub-themes to have an ie.css or wireframes.css file.
234 $vars['subtheme_directory'] = path_to_subtheme();
235
236 // These next lines add additional CSS files and redefine
237 // the $css and $styles variables available to your page template
238 if ($theme == $theme_key) { // If we're in the main theme
239 // Load the stylesheet for a liquid layout
240 if (theme_get_setting('zen_layout') == 'border-politics-liquid') {
241 drupal_add_css($vars['directory'] .'/layout-liquid.css', 'theme', 'all');
242 }
243 // Or load the stylesheet for a fixed width layout
244 else {
245 drupal_add_css($vars['directory'] .'/layout-fixed.css', 'theme', 'all');
246 }
247 drupal_add_css($vars['directory'] .'/html-elements.css', 'theme', 'all');
248 drupal_add_css($vars['directory'] .'/tabs.css', 'theme', 'all');
249 drupal_add_css($vars['directory'] .'/zen.css', 'theme', 'all');
250 // Avoid IE5 bug that always loads @import print stylesheets
251 $vars['head'] = zen_add_print_css($vars['directory'] .'/print.css');
252 }
253 // Optionally add the block editing styles.
254 if (theme_get_setting('zen_block_editing')) {
255 drupal_add_css($vars['directory'] .'/block-editing.css', 'theme', 'all');
256 }
257 // Optionally add the wireframes style.
258 if (theme_get_setting('zen_wireframes')) {
259 if ($vars['subtheme_directory'] && file_exists($vars['subtheme_directory'] .'/wireframes.css')) {
260 drupal_add_css($vars['subtheme_directory'] .'/wireframes.css', 'theme', 'all');
261 }
262 else {
263 drupal_add_css($vars['directory'] .'/wireframes.css', 'theme', 'all');
264 }
265 }
266 $vars['css'] = drupal_add_css();
267 $vars['styles'] = drupal_get_css();
268
269 // Add an optional title to the end of the breadcrumb.
270 if (theme_get_setting('zen_breadcrumb_title') && $vars['breadcrumb']) {
271 $vars['breadcrumb'] = substr($vars['breadcrumb'], 0, -6) . $vars['title'] .'</div>';
272 }
273
274 // Don't display empty help from node_help().
275 if ($vars['help'] == "<div class=\"help\"><p></p>\n</div>") {
276 $vars['help'] = '';
277 }
278
279 // Optionally disabled the primary and secondary links.
280 if (!theme_get_setting('zen_primary_links')) {
281 $vars['primary_links'] = '';
282 }
283 if (!theme_get_setting('zen_secondary_links')) {
284 $vars['secondary_links'] = '';
285 }
286
287 // Classes for body element. Allows advanced theming based on context
288 // (home page, node of certain type, etc.)
289 $classes = array();
290 $classes[] = ($vars['is_front']) ? 'front' : 'not-front';
291 $classes[] = ($vars['logged_in']) ? 'logged-in' : 'not-logged-in';
292 if ($vars['node']->type) {
293 // If on an individual node page, put the node type in the body classes
294 $classes[] = 'node-type-'. $vars['node']->type;
295 }
296 if ($vars['sidebar_left'] && $vars['sidebar_right']) {
297 $classes[] = 'two-sidebars';
298 }
299 elseif ($vars['sidebar_left']) {
300 $classes[] = 'one-sidebar sidebar-left';
301 }
302 elseif ($vars['sidebar_right']) {
303 $classes[] = 'one-sidebar sidebar-right';
304 }
305 else {
306 $classes[] = 'no-sidebars';
307 }
308 if (!$vars['is_front']) {
309 // Add unique class for each page.
310 $path = drupal_get_path_alias($_GET['q']);
311 $classes[] = zen_id_safe('page-'. $path);
312 // Add unique class for each website section.
313 list($section, ) = explode('/', $path, 2);
314 if (arg(0) == 'node') {
315 if (arg(1) == 'add') {
316 $section = 'node-add';
317 }
318 elseif (is_numeric(arg(1)) && (arg(2) == 'edit' || arg(2) == 'delete')) {
319 $section = 'node-'. arg(2);
320 }
321 }
322 $classes[] = zen_id_safe('section-'. $section);
323 }
324 $vars['body_classes_array'] = $classes;
325 $vars['body_classes'] = implode(' ', $classes); // Concatenate with spaces.
326 }
327
328 /**
329 * Override or insert PHPTemplate variables into the node templates.
330 *
331 * @param $vars
332 * A sequential array of variables to pass to the theme template.
333 */
334 function zen_preprocess_node(&$vars) {
335 global $user;
336
337 // Special classes for nodes
338 $node_classes = array();
339 if ($vars['sticky']) {
340 $node_classes[] = 'sticky';
341 }
342 if (!$vars['node']->status) {
343 $node_classes[] = 'node-unpublished';
344 $vars['unpublished'] = TRUE;
345 }
346 else {
347 $vars['unpublished'] = FALSE;
348 }
349 if ($vars['node']->uid && $vars['node']->uid == $user->uid) {
350 // Node is authored by current user
351 $node_classes[] = 'node-mine';
352 }
353 if ($vars['teaser']) {
354 // Node is displayed as teaser
355 $node_classes[] = 'node-teaser';
356 }
357 // Class for node type: "node-type-page", "node-type-story", "node-type-my-custom-type", etc.
358 $node_classes[] = 'node-type-'. $vars['node']->type;
359 $vars['node_classes'] = implode(' ', $node_classes); // Concatenate with spaces
360 }
361
362 /**
363 * Override or insert PHPTemplate variables into the comment templates.
364 *
365 * @param $vars
366 * A sequential array of variables to pass to the theme template.
367 */
368 function zen_preprocess_comment(&$vars) {
369 global $user;
370
371 // We load the node object that the current comment is attached to
372 $node = node_load($vars['comment']->nid);
373 // If the author of this comment is equal to the author of the node, we
374 // set a variable so we can theme this comment uniquely.
375 $vars['author_comment'] = $vars['comment']->uid == $node->uid ? TRUE : FALSE;
376
377 $comment_classes = array();
378
379 // Odd/even handling
380 static $comment_odd = TRUE;
381 $comment_classes[] = $comment_odd ? 'odd' : 'even';
382 $comment_odd = !$comment_odd;
383
384 if ($vars['comment']->status == COMMENT_NOT_PUBLISHED) {
385 $comment_classes[] = 'comment-unpublished';
386 $vars['unpublished'] = TRUE;
387 }
388 else {
389 $vars['unpublished'] = FALSE;
390 }
391 if ($vars['author_comment']) {
392 // Comment is by the node author
393 $comment_classes[] = 'comment-by-author';
394 }
395 if ($vars['comment']->uid == 0) {
396 // Comment is by an anonymous user
397 $comment_classes[] = 'comment-by-anon';
398 }
399 if ($user->uid && $vars['comment']->uid == $user->uid) {
400 // Comment was posted by current user
401 $comment_classes[] = 'comment-mine';
402 }
403 $vars['comment_classes'] = implode(' ', $comment_classes);
404
405 // If comment subjects are disabled, don't display 'em
406 if (variable_get('comment_subject_field', 1) == 0) {
407 $vars['title'] = '';
408 }
409 }
410
411 /**
412 * Override or insert PHPTemplate variables into the block templates.
413 *
414 * @param $vars
415 * A sequential array of variables to pass to the theme template.
416 */
417 function zen_preprocess_block(&$vars) {
418 $block = $vars['block'];
419
420 // Special classes for blocks
421 $block_classes = array();
422 $block_classes[] = 'block-'. $block->module;
423 $block_classes[] = 'region-'. $vars['block_zebra'];
424 $block_classes[] = $vars['zebra'];
425 $block_classes[] = 'region-count-'. $vars['block_id'];
426 $block_classes[] = 'count-'. $vars['id'];
427 $vars['block_classes'] = implode(' ', $block_classes);
428
429 $vars['edit_links'] = '';
430 if (theme_get_setting('zen_block_editing') && user_access('administer blocks')) {
431 // Display 'edit block' for custom blocks
432 if ($block->module == 'block') {
433 $edit_links[] = l('<span>'. t('edit block') .'</span>', 'admin/build/block/configure/'. $block->module .'/'. $block->delta, array('title' => t('edit the content of this block'), 'class' => 'block-edit'), drupal_get_destination(), NULL, FALSE, TRUE);
434 }
435 // Display 'configure' for other blocks
436 else {
437 $edit_links[] = l('<span>'. t('configure') .'</span>', 'admin/build/block/configure/'. $block->module .'/'. $block->delta, array('title' => t('configure this block'), 'class' => 'block-config'), drupal_get_destination(), NULL, FALSE, TRUE);
438 }
439
440 // Display 'administer views' for views blocks
441 if ($block->module == 'views' && user_access('administer views')) {
442 $edit_links[] = l('<span>'. t('edit view') .'</span>', 'admin/build/views/'. $block->delta .'/edit', array('title' => t('edit the view that defines this block'), 'class' => 'block-edit-view'), drupal_get_destination(), 'edit-block', FALSE, TRUE);
443 }
444 // Display 'edit menu' for menu blocks
445 elseif (($block->module == 'menu' || ($block->module == 'user' && $block->delta == 1)) && user_access('administer menu')) {
446 $edit_links[] = l('<span>'. t('edit menu') .'</span>', 'admin/build/menu', array('title' => t('edit the menu that defines this block'), 'class' => 'block-edit-menu'), drupal_get_destination(), NULL, FALSE, TRUE);
447 }
448 $vars['edit_links_array'] = $edit_links;
449 $vars['edit_links'] = '<div class="edit">'. implode(' ', $edit_links) .'</div>';
450 }
451 }
452
453 /**
454 * Converts a string to a suitable html ID attribute.
455 *
456 * http://www.w3.org/TR/html4/struct/global.html#h-7.5.2 specifies what makes a
457 * valid ID attribute in HTML. This function:
458 *
459 * - Ensure an ID starts with an alpha character by optionally adding an 'n'.
460 * - Replaces any character except A-Z, numbers, and underscores with dashes.
461 * - Converts entire string to lowercase.
462 *
463 * @param $string
464 * The string
465 * @return
466 * The converted string
467 */
468 function zen_id_safe($string) {
469 // Replace with dashes anything that isn't A-Z, numbers, dashes, or underscores.
470 $string = strtolower(preg_replace('/[^a-zA-Z0-9_-]+/', '-', $string));
471 // If the first character is not a-z, add 'id' in front.
472 if (!ctype_lower($string{0})) { // Don't use ctype_alpha since its locale aware.
473 $string = 'id' . $string;
474 }
475 return $string;
476 }
477
478 /**
479 * Adds a print stylesheet to the page's $head variable.
480 *
481 * This is a work-around for a serious bug in IE5 in which it loads print
482 * stylesheets for screen display when using an @import method, Drupal's default
483 * method when using drupal_add_css().
484 *
485 * @param $url
486 * The URL of the print stylesheet
487 * @return
488 * All the rendered links for the $head variable
489 */
490 function zen_add_print_css($url) {
491 global $base_path;
492 return drupal_set_html_head(
493 '<link'.
494 drupal_attributes(
495 array(
496 'rel' => 'stylesheet',
497 'href' => $base_path . $url,
498 'type' => 'text/css',
499 'media' => 'print',
500 )
501 ) ." />\n"
502 );
503 }