Package base :: Package includes :: Module theme
[hide private]

Source Code for Module base.includes.theme

   1  #!/usr/bin/env python 
   2  # $Id: theme.inc,v 1.431 2008/08/02 19:01:02 dries Exp $ 
   3   
   4  """ 
   5    The theme system, which controls the output of Drupal. 
   6    The theme system allows for nearly all output of the Drupy system to be 
   7    customized by user themes. 
   8   
   9    @package includes 
  10    @see <a href='http://drupy.net'>Drupy Homepage</a> 
  11    @see <a href='http://drupal.org'>Drupal Homepage</a> 
  12    @see <a href="http://drupal.org/node/253">Drupal Theme system</a> 
  13    @note Drupy is a port of the Drupal project. 
  14    @note This file was ported from Drupal's includes/theme.inc 
  15    @author Brendon Crawford 
  16    @copyright 2008 Brendon Crawford 
  17    @contact message144 at users dot sourceforge dot net 
  18    @created 2008-01-10 
  19    @version 0.1 
  20    @note License: 
  21   
  22      This program is free software; you can redistribute it and/or 
  23      modify it under the terms of the GNU General Public License 
  24      as published by the Free Software Foundation; either version 2 
  25      of the License, or (at your option) any later version. 
  26   
  27      This program is distributed in the hope that it will be useful, 
  28      but WITHOUT ANY WARRANTY; without even the implied warranty of 
  29      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
  30      GNU General Public License for more details. 
  31   
  32      You should have received a copy of the GNU General Public License 
  33      along with this program; if not, write to: 
  34       
  35      The Free Software Foundation, Inc., 
  36      51 Franklin Street, Fifth Floor, 
  37      Boston, MA  02110-1301, 
  38      USA 
  39  """ 
  40   
  41  __version__ = "$Revision: 1 $" 
  42   
  43  from lib.drupy import DrupyPHP as php 
  44  from lib.drupy import DrupyImport 
  45  import appglobals as lib_appglobals 
  46  import bootstrap as lib_bootstrap 
  47  import common as lib_common 
  48  import database as lib_database 
  49  import plugin as lib_plugin 
  50   
  51  # 
  52  # Markers used by theme_mark() and node_mark() to designate content. 
  53  # @see theme_mark(), node_mark() 
  54  # 
  55   
  56  # 
  57  # Mark content as read. 
  58  # 
  59  MARK_READ = 0 
  60   
  61  # 
  62  # Mark content as being new. 
  63  # 
  64  MARK_NEW = 1 
  65   
  66  # 
  67  # Mark content as being updated. 
  68  # 
  69  MARK_UPDATED = 2 
  70   
  71  # 
  72  # Internal Use 
  73  # 
  74  processors = {} 
  75   
  76   
  77   
78 -def init_theme():
79 """ 80 Initialize the theme system by loading the theme. 81 """ 82 # If theme is already set, assume the others are set, too, and do nothing 83 if (lib_appglobals.theme is not None): 84 return True; 85 lib_bootstrap.drupal_bootstrap(lib_bootstrap.DRUPAL_BOOTSTRAP_DATABASE); 86 themes = list_themes(); 87 # Only select the user selected theme if it is available in the 88 # list of enabled themes. 89 if (lib_appglobals.user is not None and \ 90 php.isset(lib_appglobals.user, 'theme') and \ 91 not php.empty(lib_appglobals.user.theme) and \ 92 not php.empty(themes[lib_bootstrap.user.theme].status)): 93 lib_appglobals.theme = lib_appglobals.user.theme 94 else: 95 lib_appglobals.theme = \ 96 lib_bootstrap.variable_get('theme_default', 'garland') 97 # Allow plugins to override the present theme... only select custom theme 98 # if it is available in the list of installed themes. 99 lib_appglobals.theme = (lib_appglobals.custom_theme if \ 100 (lib_appglobals.custom_theme and themes[custom_theme]) else \ 101 lib_appglobals.theme); 102 # Store the identifier for retrieving theme settings with. 103 lib_appglobals.theme_key = lib_appglobals.theme 104 # Find all our ancestor themes and put them in an array. 105 base_theme = []; 106 ancestor = lib_appglobals.theme; 107 while (ancestor and php.isset(themes[ancestor], 'base_theme')): 108 new_base_theme = themes[themes[ancestor].base_theme]; 109 base_theme.append(new_base_theme); 110 ancestor = themes[ancestor].base_theme; 111 _init_theme(themes[lib_appglobals.theme], php.array_reverse(base_theme));
112 113 114
115 -def _init_theme(theme_, base_theme = [], registry_callback = \ 116 '_theme_load_registry'):
117 """ 118 Initialize the theme system given already loaded information. This 119 function is useful to initialize a theme when no database is present. 120 121 @param this_theme 122 An object with the following information: 123 filename 124 The .info file for this theme. The 'path' to 125 the theme will be in this file's directory. (Required) 126 owner 127 The path to the .theme file or the .engine file to load for 128 the theme. (Required) 129 stylesheet 130 The primary stylesheet for the theme. (Optional) 131 engine 132 The name of theme engine to use. (Optional) 133 @param base_theme 134 An optional array of objects that represent the 'base theme' if the 135 theme is meant to be derivative of another theme. It requires 136 the same information as the theme object. It should be in 137 'oldest first' order, meaning the top level of the chain will 138 be first. 139 @param registry_callback 140 The callback to invoke to set the theme registry. 141 """ 142 lib_appglobals.theme_info = theme_; 143 lib_appglobals.base_theme_info = base_theme; 144 lib_appglobals.theme_path = php.dirname(theme_.filename); 145 # Prepare stylesheets from this theme as well as all ancestor themes. 146 # We work it this way so that we can have child themes override parent 147 # theme stylesheets easily. 148 final_stylesheets = {}; 149 # Grab stylesheets from base theme 150 for base in base_theme: 151 if (not php.empty(base.stylesheets)): 152 for media,stylesheets in base.stylesheets.items(): 153 final_stylesheets[media] = {} 154 for name,stylesheet in stylesheets.items(): 155 final_stylesheets[media][name] = stylesheet; 156 # Add stylesheets used by this theme. 157 if (not php.empty(theme_.stylesheets)): 158 for media,stylesheets in theme_.stylesheets.items(): 159 final_stylesheets[media] = {} 160 for name,stylesheet in stylesheets.items(): 161 final_stylesheets[media][name] = stylesheet; 162 # And now add the stylesheets properly 163 for media,stylesheets in final_stylesheets.items(): 164 for stylesheet in stylesheets: 165 lib_common.drupal_add_css(stylesheet, 'theme', media); 166 # Do basically the same as the above for scripts 167 final_scripts = {}; 168 # Grab scripts from base theme 169 for base in base_theme: 170 if (not php.empty(base.scripts)): 171 for name,script in base.scripts.items(): 172 final_scripts[name] = script; 173 # Add scripts used by this theme. 174 if (not php.empty(theme_.scripts)): 175 for name,script in theme_.scripts.items(): 176 final_scripts[name] = script; 177 # Add scripts used by this theme. 178 for script in final_scripts: 179 lib_common.drupal_add_js(script, 'theme'); 180 lib_appglobals.theme_engine = None; 181 # Initialize the theme. 182 if (php.isset(theme_, 'engine')): 183 # Include the engine. 184 processors[theme_.engine] = DrupyImport.import_file(theme_.owner) 185 lib_appglobals.theme_engine = theme_.engine; 186 if (php.function_exists('hook_init', processors[theme_.engine])): 187 this_hook = DrupyImport.getFunction(processors[theme_.engine], 188 'hook_init') 189 for base in base_theme: 190 php.call_user_func(this_hook, base); 191 php.call_user_func(this_hook, theme_); 192 else: 193 # include non-engine theme files 194 for base in base_theme: 195 # Include the theme file or the engine. 196 if (not php.empty(base.owner)): 197 processors[base.engine] = DrupyImport.import_file(base.owner) 198 # and our theme gets one too. 199 if (not php.empty(theme_.owner)): 200 processors[theme.engine] = DrupyImport.import_file(theme_.owner) 201 if (drupal_function_exists(registry_callback)): 202 registry_callback(theme_, \ 203 base_theme, lib_appglobals.theme_engine)
204 205 206
207 -def get_registry(registry = None):
208 """ 209 Retrieve the stored theme registry. If the theme registry is already 210 in memory it will be returned; otherwise it will attempt to load the 211 registry from cache. If this fails, it will construct the registry and 212 cache it. 213 """ 214 php.static(get_registry, 'theme_registry') 215 if (get_registry.theme_registry != None): 216 get_registry.theme_registry = registry; 217 return get_registry.theme_registry;
218 219 220
221 -def _set_registry(registry):
222 """ 223 Store the theme registry in memory. 224 """ 225 # Pass through for setting of static variable. 226 return theme_get_registry(registry);
227 228 229
230 -def _load_registry(theme_, base_theme = None, theme_engine = None):
231 """ 232 Get the theme_registry cache from the database; if it doesn't exist, build 233 it. 234 235 @param theme 236 The loaded theme object. 237 @param base_theme 238 An array of loaded theme objects representing the ancestor themes in 239 oldest first order. 240 @param theme_engine 241 The name of the theme engine. 242 """ 243 # Check the theme registry cache; if it exists, use it. 244 cache = cache_get("theme_registry:t%s" % theme.name, 'cache'); 245 if (php.isset(cache, 'data')): 246 registry = cache.data; 247 else: 248 # If not, build one and cache it. 249 registry = _theme_build_registry(theme_, base_theme, theme_engine); 250 _theme_save_registry(theme_, registry); 251 _theme_set_registry(registry);
252 253 254
255 -def _theme_save_registry(theme_, registry):
256 """ 257 Write the theme_registry cache into the database. 258 """ 259 cache_set("theme_registry:%s" % theme.name, registry);
260 261 262
263 -def drupal_theme_rebuild():
264 """ 265 Force the system to rebuild the theme registry; this should be called 266 when plugins are added to the system, or when a dynamic system needs 267 to add more theme hooks. 268 """ 269 cache_clear_all('theme_registry', 'cache', True);
270 271 272
273 -def _process_registry(cache, name, type_, theme_, path):
274 """ 275 Process a single invocation of the theme hook. type will be one 276 of 'plugin', 'theme_engine' or 'theme' and it tells us some 277 important information. 278 279 Because cache is a reference, the cache will be continually 280 expanded upon; new entries will replace old entries in the 281 array_merge, but we are careful to ensure some data is carried 282 forward, such as the arguments a theme hook needs. 283 284 An override flag can be set for preprocess functions. When detected the 285 cached preprocessors for the hook will not be merged with the newly set. 286 This can be useful to themes and theme engines by giving them more control 287 over how and when the preprocess functions are run. 288 """ 289 php.Reference.check(cache); 290 function = name + '_theme'; 291 if (php.function_exists(function)): 292 result = function(cache, type_, theme_, path); 293 for hook,info in result.items(): 294 result[hook]['type'] = type_; 295 result[hook]['theme path'] = path; 296 # if function and file are left out, default to standard naming 297 # conventions. 298 if (not php.isset(info, 'template') and not \ 299 php.isset(info, 'function')): 300 result[hook]['function'] = ('theme_' if (type_ == 'plugin') else \ 301 name + '_') + hook; 302 # If a path is set in the info, use what was set. Otherwise use the 303 # default path. This is mostly so system.plugin can declare theme 304 # functions on behalf of core .include files. 305 # All files are included to be safe. Conditionally included 306 # files can prevent them from getting registered. 307 if (php.isset(info, 'file') and not php.isset(info['path'])): 308 result[hook]['file'] = path + '/' + info['file']; 309 php.include_once(result[hook]['file']); 310 elif (php.isset(info, 'file') and php.isset(info, 'path')): 311 php.include_once(info['path'] + '/' + info['file']); 312 if (php.isset(info, 'template') and not php.isset(info, 'path')): 313 result[hook]['template'] = path + '/' + info['template']; 314 # If 'arguments' have been defined previously, carry them forward. 315 # This should happen if a theme overrides a Drupal defined theme 316 # function, for example. 317 if (not php.isset(info, 'arguments') and php.isset(cache, hook)): 318 result[hook]['arguments'] = cache[hook]['arguments']; 319 # Likewise with theme paths. These are used for 320 # template naming suggestions. 321 # Theme implementations can occur in multiple paths. 322 # Suggestions should follow. 323 if (not php.isset(info, 'theme paths') and php.isset(cache, hook)): 324 result[hook]['theme paths'] = cache[hook]['theme paths']; 325 # Check for sub-directories. 326 result[hook]['theme paths'].append( info['path'] if \ 327 php.isset(info, 'path') else path ); 328 # Check for default _preprocess_ functions. Ensure arrayness. 329 if (not php.isset(info, 'preprocess functions') or not \ 330 php.is_array(info['preprocess functions'])): 331 info['preprocess functions'] = []; 332 prefixes = []; 333 if (type == 'plugin'): 334 # Default preprocessor prefix. 335 prefixes.append( 'template' ); 336 # Add all plugins so they can intervene with their own 337 # preprocessors. This allows them 338 # to provide preprocess functions even if they are not 339 # the owner of the current hook. 340 prefixes += plugin_list(); 341 elif (type_ == 'theme_engine'): 342 # Theme engines get an extra set that come before the 343 # normally named preprocessors. 344 prefixes.append( name + '_engine' ); 345 # The theme engine also registers on behalf of the theme. 346 # The theme or engine name can be used. 347 prefixes.append( name ); 348 prefixes.append( theme_ ); 349 else: 350 # This applies when the theme manually registers their own 351 # preprocessors. 352 prefixes.append( name ); 353 for prefix in prefixes: 354 if (php.function_exists(prefix + '_preprocess')): 355 info['preprocess functions'].append( prefix + '_preprocess' ); 356 if (php.function_exists(prefix + '_preprocess_' + hook)): 357 info['preprocess functions'].append( prefix + \ 358 '_preprocess_' + hook ); 359 # Check for the override flag and prevent the cached preprocess 360 # functions from being used. 361 # This allows themes or theme engines to remove preprocessors 362 # set earlier in the registry build. 363 if (not php.empty(info['override preprocess functions'])): 364 # Flag not needed inside the registry. 365 del(result[hook]['override preprocess functions']); 366 elif (php.isset(cache[hook], 'preprocess functions') and \ 367 php.is_array(cache[hook]['preprocess functions'])): 368 info['preprocess functions'] = \ 369 php.array_merge(cache[hook]['preprocess functions'], \ 370 info['preprocess functions']); 371 result[hook]['preprocess functions'] = info['preprocess functions']; 372 # Merge the newly created theme hooks into the existing cache. 373 php.array_merge(cache, result, True);
374 375 376
377 -def _build_registry(theme_, base_theme, theme_engine):
378 """ 379 Rebuild the hook theme_registry cache. 380 381 @param theme 382 The loaded theme object. 383 @param base_theme 384 An array of loaded theme objects representing the ancestor themes in 385 oldest first order. 386 @param theme_engine 387 The name of the theme engine. 388 """ 389 cache = {}; 390 # First, process the theme hooks advertised by plugins. This will 391 # serve as the basic registry. 392 for plugin in plugin_implements('theme'): 393 _theme_process_registry(cache, plugin, 'plugin', plugin, \ 394 drupal_get_path('plugin', plugin)); 395 # Process each base theme. 396 for base in base_theme: 397 # If the theme uses a theme engine, process its hooks. 398 base_path = php.dirname(base.filename); 399 if (theme_engine): 400 _theme_process_registry(cache, theme_engine, 'base_theme_engine', \ 401 base.name, base_path); 402 _theme_process_registry(cache, base.name, 'base_theme', base.name, \ 403 base_path); 404 # And then the same thing, but for the theme. 405 if (theme_engine): 406 _theme_process_registry(cache, theme_engine, 'theme_engine', \ 407 theme.name, php.dirname(theme_.filename)); 408 # Finally, hooks provided by the theme itself. 409 _theme_process_registry(cache, theme_.name, 'theme', theme_.name, \ 410 php.dirname(theme_.filename)); 411 # Let plugins alter the registry 412 drupal_alter('theme_registry', cache); 413 return cache;
414 415 416
417 -def list_themes(refresh = False):
418 """ 419 Provides a list of currently available themes. 420 421 If the database is active then it will be retrieved from the database. 422 Otherwise it will retrieve a new list. 423 424 @param refresh 425 Whether to reload the list of themes from the database. 426 @return 427 An array of the currently available themes. 428 """ 429 php.static(list_themes, 'list_', {}) 430 if (refresh): 431 list_themes.list_ = {}; 432 if (php.empty(list_themes.list_)): 433 themes = []; 434 # Extract from the database only when it is available. 435 # Also check that the site is not in the middle of an install or update. 436 if (lib_database.is_active() and not lib_bootstrap.MAINTENANCE_MODE): 437 result = lib_database.query(\ 438 "SELECT * FROM {system} WHERE type = '%s'", 'theme'); 439 while True: 440 this_theme = lib_database.fetch_object(result); 441 if this_theme == False or this_theme is None: 442 break; 443 if (php.file_exists(this_theme.filename)): 444 this_theme.info = php.unserialize(this_theme.info); 445 #php.print_r( dir(theme_) ) 446 #php.flush() 447 #exit(1) 448 themes.append( this_theme ); 449 else: 450 # Scan the installation when the database should not be read. 451 themes = lib_plugin.plugins['system']._theme_data(); 452 for i_theme in themes: 453 i_theme.stylesheets = {} 454 i_theme.scripts = {} 455 for media,stylesheets in i_theme.info['stylesheets'].items(): 456 i_theme.stylesheets[media] = {} 457 for stylesheet,path in stylesheets.items(): 458 i_theme.stylesheets[media][stylesheet] = path; 459 for script,path in i_theme.info['scripts'].items(): 460 if (php.file_exists(path)): 461 i_theme.scripts[script] = path; 462 if (php.isset(i_theme.info, 'engine')): 463 i_theme.engine = i_theme.info['engine']; 464 if (php.isset(i_theme.info, 'base theme')): 465 i_theme.base_theme = i_theme.info['base theme']; 466 # Status is normally retrieved from the database. Add zero values when 467 # read from the installation directory to prevent notices. 468 if (not php.isset(i_theme, 'status')): 469 i_theme.status = 0; 470 list_themes.list_[i_theme.name] = i_theme; 471 return list_themes.list_;
472 473
474 -def theme(*args):
475 476 """ 477 Generate the themed output. 478 479 All requests for theme hooks must go through this function. It examines 480 the request and routes it to the appropriate theme function. The theme 481 registry is checked to determine which implementation to use, which may 482 be a function or a template. 483 484 If the implementation is a function, it is executed and its return value 485 passed along. 486 487 If the implementation is a template, the arguments are converted to a 488 variables array. This array is then modified by the plugin implementing 489 the hook, theme engine (if applicable) and the theme. The following 490 functions may be used to modify the variables array. They are processed in 491 this order when available: 492 493 - template_preprocess(&variables) 494 This sets a default set of variables for all template implementations. 495 496 - template_preprocess_HOOK(&variables) 497 This is the first preprocessor called specific to the hook; it should be 498 implemented by the plugin that registers it. 499 500 - MODULE_preprocess(&variables) 501 This will be called for all templates; it should only be used if there 502 is a real need. It's purpose is similar to template_preprocess(). 503 504 - MODULE_preprocess_HOOK(&variables) 505 This is for plugins that want to alter or provide extra variables for 506 theming hooks not registered to itself. For example, if a plugin named 507 "foo" wanted to alter the submitted variable for the hook "node" a 508 preprocess function of foo_preprocess_node() can be created to intercept 509 and alter the variable. 510 511 - ENGINE_engine_preprocess(&variables) 512 This function should only be implemented by theme engines and exists 513 so that it can set necessary variables for all hooks. 514 515 - ENGINE_engine_preprocess_HOOK(&variables) 516 This is the same as the previous function, but it is called for a single 517 theming hook. 518 519 - ENGINE_preprocess(&variables) 520 This is meant to be used by themes that utilize a theme engine. It is 521 provided so that the preprocessor is not locked into a specific theme. 522 This makes it easy to share and transport code but theme authors must be 523 careful to prevent fatal re-declaration errors when using sub-themes that 524 have their own preprocessor named exactly the same as its base theme. In 525 the default theme engine (PHPTemplate), sub-themes will load their own 526 template.php file in addition to the one used for its parent theme. This 527 increases the risk for these errors. A good practice is to use the engine 528 name for the base theme and the theme name for the sub-themes to minimize 529 this possibility. 530 531 - ENGINE_preprocess_HOOK(&variables) 532 The same applies from the previous function, but it is called for a 533 specific hook. 534 535 - THEME_preprocess(&variables) 536 These functions are based upon the raw theme; they should primarily be 537 used by themes that do not use an engine or by sub-themes. It serves the 538 same purpose as ENGINE_preprocess(). 539 540 - THEME_preprocess_HOOK(&variables) 541 The same applies from the previous function, but it is called for a 542 specific hook. 543 544 There are two special variables that these hooks can set: 545 'template_file' and 'template_files'. These will be merged together 546 to form a list of 'suggested' alternate template files to use, in 547 reverse order of priority. template_file will always be a higher 548 priority than items in template_files. theme() will then look for these 549 files, one at a time, and use the first one 550 that exists. 551 @param hook 552 The name of the theme function to call. May be an array, in which 553 case the first hook that actually has an implementation registered 554 will be used. This can be used to choose 'fallback' theme implementations, 555 so that if the specific theme hook isn't implemented anywhere, a more 556 generic one will be used. This can allow themes to create specific theme 557 implementations for named objects. 558 @param ... 559 Additional arguments to pass along to the theme function. 560 @return 561 An HTML string that generates the themed output. 562 """ 563 php.static(theme, 'hooks') 564 hook = php.array_shift(args); 565 if (theme.hooks == None): 566 init_theme(); 567 theme.hooks = get_registry(); 568 if (php.is_array(hook)): 569 for candidate in hook: 570 if (php.isset(hooks, candidate)): 571 break; 572 hook = candidate; 573 if (not php.isset(theme.hooks, hook)): 574 return; 575 info = theme.hooks[hook]; 576 temp = lib_appglobals.theme_path; 577 # point path_to_theme() to the currently used theme path: 578 lib_appglobals.theme_path = info['theme path']; 579 # Include a file if the theme function or preprocess function is 580 # held elsewhere. 581 if (not php.empty(info['file'])): 582 include_file = info['file']; 583 if (php.isset(info, 'path')): 584 include_file = info['path'] + '/' + include_file; 585 php.include_once(include_file); 586 if (php.isset(info, 'function')): 587 # The theme call is a function. 588 output = info['function'](*args); 589 else: 590 # The theme call is a template. 591 variables = { 592 'template_files' : [] 593 }; 594 if (not php.empty(info['arguments'])): 595 count = 0; 596 for name,default in info['arguments'].items(): 597 variables[name] = (args[count] if php.isset(args, count) else default); 598 count += 1; 599 # default render function and extension. 600 render_function = 'theme_render_template'; 601 extension = '.tpl.py'; 602 # Run through the theme engine variables, if necessary 603 if (lib_appglobals.theme_engine is not None): 604 # If theme or theme engine is implementing this, it may have 605 # a different extension and a different renderer. 606 if (info['type'] != 'plugin'): 607 if (php.function_exists(\ 608 lib_appglobals.theme_engine + '_render_template')): 609 render_function = lib_appglobals.theme_engine + '_render_template'; 610 extension_function = lib_appglobals.theme_engine + '_extension'; 611 if (php.function_exists(extension_function)): 612 extension = extension_function(); 613 if (php.isset(info, 'preprocess functions') and \ 614 php.is_array(info['preprocess functions'])): 615 # This construct ensures that we can keep a reference through 616 # call_user_func_array. 617 variables_ = php.Reference(variables); 618 args = (variables_, hook); 619 for preprocess_function in info['preprocess functions']: 620 if (drupal_function_exists(preprocess_function)): 621 preprocess_function( *args ); 622 # Get suggestions for alternate templates out of the variables 623 # that were set. This lets us dynamically choose a template 624 # from a list. The order is FILO, so this array is ordered from 625 # least appropriate first to most appropriate last. 626 suggestions = {}; 627 if (php.isset(variables_, 'template_files')): 628 suggestions = variables_['template_files']; 629 if (php.isset(variables_, 'template_file')): 630 suggestions.append( variables_['template_file'] ); 631 if (suggestions): 632 template_file = drupal_discover_template(info['theme paths'], \ 633 suggestions, extension); 634 if (php.empty(template_file)): 635 template_file = info['template'] + extension; 636 if (php.isset(info, 'path')): 637 template_file = info['path'] + '/' + template_file; 638 output = render_function(template_file, variables); 639 # restore path_to_theme() 640 lib_appglobals.theme_path = temp; 641 return output;
642 643 644
645 -def drupal_discover_template(paths, suggestions, extension = '.tpl.php'):
646 """ 647 Choose which template file to actually render. These are all suggested 648 templates from themes and plugins. Theming implementations can occur on 649 multiple levels. All paths are checked to account for this. 650 """ 651 # Loop through all paths and suggestions in FIFO order. 652 suggestions = php.array_reverse(suggestions); 653 paths = php.array_reverse(paths); 654 for suggesiton in suggestions: 655 if (not php.empty(suggestion)): 656 for path in paths: 657 file = path + '/' + suggestion + extension; 658 if (php.file_exists(file)): 659 return file;
660 661 662
663 -def path_to_theme():
664 """ 665 Return the path to the currently selected theme. 666 """ 667 if (lib_appglobals.theme_path is None): 668 init_theme(); 669 return lib_appglobals.theme_path;
670 671 672
673 -def drupal_find_theme_functions(cache, prefixes):
674 """ 675 Find overridden theme functions. Called by themes and/or theme engines to 676 easily discover theme functions. 677 678 @param cache 679 The existing cache of theme hooks to test against. 680 @param prefixes 681 An array of prefixes to test, in reverse order of importance. 682 683 @return templates 684 The functions found, suitable for returning from hook_theme; 685 """ 686 templates = []; 687 functions = get_defined_functions(); 688 for hook,info in cache.items(): 689 for prefix in prefixes: 690 if (not php.empty(info['pattern'])): 691 matches = preg_grep('/^' + prefix + '_' + info['pattern'] + '/', \ 692 functions['user']); 693 if (matches > 1): 694 for match in matches: 695 new_hook = php.str_replace(prefix + '_', '', match); 696 templates[new_hook] = { 697 'function' : match, 698 'arguments' : info['arguments'], 699 }; 700 if (php.function_exists(prefix + '_' + hook)): 701 templates[hook] = { 702 'function' : prefix + '_' + hook, 703 }; 704 return templates;
705 706
707 -def drupal_find_theme_templates(cache, extension, path):
708 """ 709 Find overridden theme templates. Called by themes and/or theme engines to 710 easily discover templates. 711 712 @param cache 713 The existing cache of theme hooks to test against. 714 @param extension 715 The extension that these templates will have. 716 @param path 717 The path to search. 718 """ 719 templates = []; 720 # Collect paths to all sub-themes grouped by base themes+ These will be 721 # used for filtering+ This allows base themes to have sub-themes in its 722 # folder hierarchy without affecting the base themes template discovery+ 723 # theme_paths = array(); 724 for theme_info in list_themes(): 725 if (not php.empty(theme_info.base_theme)): 726 theme_paths[theme_info.base_theme][theme_info.name] = \ 727 php.dirname(theme_info.filename); 728 for basetheme,subthemes in theme_paths.items(): 729 for subtheme,subtheme_path in subthemes.items(): 730 if (php.isset(theme_paths, subtheme)): 731 theme_paths[basetheme] = php.array_merge(theme_paths[basetheme], \ 732 theme_paths[subtheme]); 733 subtheme_paths = (theme_paths[theme] if \ 734 php.isset(theme_paths, lib_appglobals.theme) else []); 735 # Escape the periods in the extension+ regex = 736 # php.str_replace('.', '\.', extension) +'$'; 737 # Because drupal_system_listing works the way it does, we check for real 738 # templates separately from checking for patterns+ files = 739 # drupal_system_listing(regex, path, 'name', 0); 740 for template,file in files.items(): 741 # Ignore sub-theme templates for the current theme+ if 742 # (php.strpos(file.filename, 743 # php.str_replace(subtheme_paths, '', file.filename)) !== 0): 744 continue; 745 # Chop off the remaining extensions if there are any+ template already 746 # has the rightmost extension removed, but there might still be more, 747 # such as with +tpl.php, which still has +tpl in template at this point 748 pos = php.strpos(template, '.') 749 if (pos != False): 750 template = php.substr(template, 0, pos); 751 # Transform - in filenames to _ to match function naming scheme 752 # for the purposes of searching+ hook = php.strtr(template, '-', '_'); 753 if (php.isset(cache, hook)): 754 templates[hook] = { 755 'template' : template, 756 'path' : php.dirname(file.filename), 757 }; 758 patterns = php.array_keys(files); 759 for hook,info in cache.items(): 760 if (not php.empty(info, 'pattern')): 761 # Transform _ in pattern to - to match file naming scheme 762 # for the purposes of searching+ 763 pattern = php.strtr(info['pattern'], '_', '-'); 764 matches = preg_grep('/^'+ pattern +'/', patterns); 765 if (matches): 766 for match in matches: 767 file = php.substr(match, 0, php.strpos(match, '.')); 768 # Put the underscores back in for the hook name and 769 # register this pattern+ 770 templates[php.strtr(file, '-', '_')] = { 771 'template' : file, 772 'path' : php.dirname(files[match].filename), 773 'arguments' : info['arguments'], 774 }; 775 return templates;
776 777
778 -def get_settings(key = None):
779 """ 780 Retrieve an associative array containing the settings for a theme+ * 781 The final settings are arrived at by merging the default settings, 782 the site-wide settings, and the settings defined for the specific theme+ * 783 If no key was specified, only the site-wide theme defaults are retrieved+ * 784 The default values for each of settings are also defined in this function+ 785 To add new settings, add their default values here, and then add 786 form elements 787 to system_theme_settings() in system.plugin+ * 788 @param key 789 The template/style value for a given theme+ * 790 @return 791 An associative array containing theme settings 792 """ 793 defaults = { 794 'mission' : '', 795 'default_logo' : 1, 796 'logo_path' : '', 797 'default_favicon' : 1, 798 'favicon_path' : '', 799 'main_menu' : 1, 800 'secondary_menu' : 1, 801 'toggle_logo' : 1, 802 'toggle_favicon' : 1, 803 'toggle_name' : 1, 804 'toggle_search' : 1, 805 'toggle_slogan' : 0, 806 'toggle_mission' : 1, 807 'toggle_node_user_picture' : 0, 808 'toggle_comment_user_picture' : 0, 809 'toggle_main_menu' : 1, 810 'toggle_secondary_menu' : 1 811 }; 812 if (plugin_exists('node')): 813 for type,name in node_get_types().items(): 814 defaults['toggle_node_info_'+ type] = 1; 815 settings = php.array_merge(defaults, variable_get('theme_settings', array())) 816 if (key != None): 817 settings = php.array_merge(settings, \ 818 variable_get(php.str_replace('/', '_', 'theme_'+ key +'_settings'), [])) 819 # Only offer search box if search.plugin is enabled 820 if (not plugin_exists('search') or not user_access('search content')): 821 settings['toggle_search'] = 0; 822 return settings;
823 824 825
826 -def get_setting(setting_name, refresh = False):
827 """ 828 Retrieve a setting for the current theme+ 829 This function is designed for use from within themes & engines 830 to determine theme settings made in the admin interface+ * 831 Caches values for speed (use refresh = True to refresh cache) 832 833 @param setting_name 834 The name of the setting to be retrieved+ * 835 @param refresh 836 Whether to reload the cache of settings+ * 837 @return 838 The value of the requested setting, None if the setting does not exist 839 """ 840 php.static(theme_get_setting, 'settings') 841 if (theme_get_setting.settings == None or refresh): 842 theme_get_setting.settings = theme_get_settings(lib_appglobals.theme_key); 843 themes = list_themes(); 844 theme_object = themes[lib_appglobals.theme_key]; 845 if (theme_get_setting.settings['mission'] == ''): 846 theme_get_setting.settings['mission'] = variable_get('site_mission', ''); 847 if (not theme_get_setting.settings['toggle_mission']): 848 theme_get_setting.settings['mission'] = ''; 849 if (theme_get_setting.settings['toggle_logo']): 850 if (theme_get_setting.settings['default_logo']): 851 theme_get_setting.settings['logo'] = base_path() + \ 852 php.dirname(theme_object.filename) +'/logo.png'; 853 elif (theme_get_setting.settings['logo_path']): 854 theme_get_setting.settings['logo'] = base_path() + \ 855 theme_get_setting.settings['logo_path']; 856 if (theme_get_setting.settings['toggle_favicon']): 857 if (theme_get_setting.settings['default_favicon']): 858 favicon = (php.dirname(theme_object.filename) +'/favicon.ico'); 859 if (php.file_exists(favicon)): 860 theme_get_setting.settings['favicon'] = base_path() + favicon; 861 else: 862 theme_get_setting.settings['favicon'] = base_path() + \ 863 'misc/favicon.ico'; 864 elif (theme_get_setting.settings['favicon_path']): 865 theme_get_setting.settings['favicon'] = base_path() + \ 866 theme_get_setting.settings['favicon_path']; 867 else: 868 theme_get_setting.settings['toggle_favicon'] = False; 869 return (theme_get_setting.settings[setting_name] if \ 870 php.isset(theme_get_setting.settings, setting_name) else None);
871 872 873
874 -def render_template(file, variables):
875 """ 876 Render a system default template, which is essentially a PHP template+ * 877 @param file 878 The filename of the template to render 879 @param variables 880 A keyed array of variables that will appear in the output+ * 881 @return 882 The output generated by the template 883 """ 884 extract(variables, EXTR_SKIP); # Extract the variables to a local namespace 885 ob_start(); # Start output buffering 886 php.include( "./file" ); # Include the file 887 contents = ob_get_contents(); # Get the contents of the buffer 888 ob_end_clean(); # End buffering and discard 889 return contents; # Return the contents
890 891 892 893 # 894 # @defgroup themeable Default theme implementations 895 # @{ 896 # 897
898 -def placeholder(text):
899 """ 900 Functions and templates that present output to the user, and can be 901 implemented by themes 902 903 Drupal's presentation layer is a pluggable system known as the theme 904 layer+ Each theme can take control over most of Drupal's output, and 905 has complete control over the CSS 906 907 Inside Drupal, the theme layer is utilized by the use of the theme() 908 function, which is passed the name of a component (the theme hook) 909 and several arguments+ For example, theme('table', php.header, rows); 910 Additionally, the theme() function can take an array of theme 911 hooks, which can be used to provide 'fallback' implementations to 912 allow for more specific control of output+ For example, the function: 913 theme(array('table__foo', 'table'), php.header, rows) would look to see if 914 'table__foo' is registered anywhere; if it is not, it would 'fall back' 915 to the generic 'table' implementation+ This can be used to attach specific 916 theme functions to named objects, allowing the themer more control over 917 specific types of output 918 919 As of Drupal 6, every theme hook is required to be registered by the 920 plugin that owns it, so that Drupal can tell what to do with it and 921 to make it simple for themes to identify and override the behavior 922 for these calls 923 924 The theme hooks are registered via hook_theme(), which returns an 925 array of arrays with information about the hook+ It describes the 926 arguments the function or template will need, and provides 927 defaults for the template in case they are not filled in+ If the default 928 implementation is a function, by convention it is named theme_HOOK() 929 930 Each plugin should provide a default implementation for theme_hooks that 931 it registers 932 933 This implementation may be either a function or a template; 934 if it is a function it must be specified via hook_theme()+ By convention, 935 default implementations of theme hooks are named theme_HOOK+ Default 936 template implementations are stored in the plugin directory+ * 937 Drupal's default template renderer is a simple PHP parsing engine that 938 includes the template and stores the output+ Drupal's theme engines 939 can provide alternate template engines, such as XTemplate, Smarty and 940 PHPTal+ The most common template engine is PHPTemplate (included with 941 Drupal and implemented in phptemplate.engine, which uses Drupal's default 942 template renderer 943 944 In order to create theme-specific implementations of these hooks, 945 themes can implement their own version of theme hooks, either as functions 946 or templates+ These implementations will be used instead of the default 947 implementation+ If using a pure +theme without an engine, the +theme is 948 required to implement its own version of hook_theme() to tell Drupal what 949 it is implementing; themes utilizing an engine will have their well-named 950 theming functions automatically registered for them+ While this can vary 951 based upon the theme engine, the standard set by phptemplate is that theme 952 functions should be named either phptemplate_HOOK or THEMENAME_HOOK+ For 953 example, for Drupal's default theme (Garland) to implement the 'table' hook, 954 the phptemplate.engine would find phptemplate_table() or garland_table() 955 956 The ENGINE_HOOK() syntax is preferred, as this can be used by sub-themes 957 (which are themes that share code but use different stylesheets) 958 The theme system is described and defined in theme.inc+ * 959 @see theme() 960 @see hook_theme() 961 962 Formats text for emphasized display in a placeholder inside a sentence 963 Used automatically by t()+ * 964 @param text 965 The text to format (plain-text) 966 @return 967 The formatted text (html) 968 """ 969 return '<em>'+ check_plain(text) +'</em>';
970 971 972 973
974 -def status_messages(display = None):
975 """ 976 Return a themed set of status and/or error messages 977 The messages are grouped by type 978 979 @param display 980 (optional) Set to 'status' or 'error' to display only messages 981 of that type+ * 982 @return 983 A string containing the messages+ 984 """ 985 output = ''; 986 for type,messages in drupal_get_messages(display).items(): 987 output += "<div class=\"messages type\">\n"; 988 if (php.count(messages) > 1): 989 output += " <ul>\n"; 990 for message in messages: 991 output += ' <li>'+ message +"</li>\n"; 992 output += " </ul>\n"; 993 else: 994 output += messages[0]; 995 output += "</div>\n"; 996 return output;
997 998 999 1041 1042 1043
1044 -def image(path, alt = '', title = '', attributes = None, getsize = True):
1045 """ 1046 Return a themed image+ * 1047 @param path 1048 Either the path of the image file (relative to base_path()) 1049 or a full URL 1050 @param alt 1051 The alternative text for text-based browsers 1052 @param title 1053 The title text is displayed when the image is hovered in some 1054 popular browsers 1055 @param attributes 1056 Associative array of attributes to be placed in the img tag 1057 @param getsize 1058 If set to True, the image's dimension are fetched and added as 1059 width/height attributes 1060 @return A string containing the image tag+ 1061 """ 1062 imageSize = php.getimagesize(path); 1063 if (not getsize or (php.is_file(path) and (imageSize != False))): 1064 width, height, type, image_attributes = imageSize; 1065 attributes = drupal_attributes(attributes); 1066 url = path if (url(path) == path) else (base_path() + path); 1067 return '<img src="'+ check_url(url) +'" alt="'+ check_plain(alt) + \ 1068 '" title="'+ check_plain(title) +'" '+ (image_attributes if \ 1069 not php.empty(image_attributes) else '') + attributes +' />';
1070 1071 1072 1083 1084 1085
1086 -def help_():
1087 """ 1088 Return a themed help message+ * 1089 @return a string containing the helptext for the current page+ 1090 """ 1091 help = menu_get_active_help() 1092 if (help != False): 1093 return '<div class="help">'+ help +'</div>';
1094 1095 1096 1104 1105
1106 -def table(header_, rows, attributes = {}, caption = None, colgroups = {}):
1107 """ 1108 Return a themed table. 1109 1110 @param php.header 1111 An array containing the table headers. Each element of the array can be 1112 either a localized string or an associative array with the following keys: 1113 - "data": The localized title of the table column. 1114 - "field": The database field represented in the table column (required if 1115 user is to be able to sort on this column). 1116 - "sort": A default sort order for this column ("asc" or "desc"). 1117 - Any HTML attributes, such as "colspan", to apply to the 1118 column php.header cell. 1119 @param rows 1120 An array of table rows. Every row is an array of cells, or an associative 1121 array with the following keys: 1122 - "data": an array of cells 1123 - Any HTML attributes, such as "class", to apply to the table row. 1124 1125 Each cell can be either a string or an associative 1126 array with the following keys: 1127 - "data": The string to display in the table cell. 1128 - "php.header": Indicates this cell is a php.header. 1129 - Any HTML attributes, such as "colspan", to apply to the table cell. 1130 1131 Here's an example for rows: 1132 @verbatim 1133 rows = array( 1134 // Simple row 1135 array( 1136 'Cell 1', 'Cell 2', 'Cell 3' 1137 ), 1138 // Row with attributes on the row and some of its cells. 1139 array( 1140 'data' : array('Cell 1', array('data' : 'Cell 2', 'colspan' : 2)), \ 1141 'class' : 'funky' 1142 ) 1143 ) 1144 @endverbatim 1145 1146 @param attributes 1147 An array of HTML attributes to apply to the table tag. 1148 @param caption 1149 A localized string to use for the <caption> tag. 1150 @param colgroups 1151 An array of column groups. Each element of the array can be either: 1152 - An array of columns, each of which is an associative array of 1153 HTML attributes 1154 applied to the COL element. 1155 - An array of attributes applied to the COLGROUP element, which 1156 must include a 1157 "data" attribute. To add attributes to COL elements, set the "data" 1158 attribute 1159 with an array of columns, each of which is an associative array of 1160 HTML attributes. 1161 Here's an example for colgroup: 1162 @verbatim 1163 colgroup = array( 1164 // COLGROUP with one COL element. 1165 array( 1166 array( 1167 'class' : 'funky', // Attribute for the COL element. 1168 ), 1169 ), 1170 // Colgroup with attributes and inner COL elements. 1171 array( 1172 'data' : array( 1173 array( 1174 'class' : 'funky', // Attribute for the COL element. 1175 ), 1176 ), 1177 'class' : 'jazzy', // Attribute for the COLGROUP element. 1178 ), 1179 ) 1180 @endverbatim 1181 These optional tags are used to group and set properties on columns 1182 within a table. For example, one may easily group three columns and 1183 apply same background style to all. 1184 @return 1185 An HTML string representing the table. 1186 """ 1187 # Add sticky headers, if applicable. 1188 if (php.count(header_)): 1189 drupal_add_js('misc/tableheader.js') 1190 # Add 'sticky-enabled' class to the table to identify it for JS. 1191 # This is needed to target tables constructed by this function. 1192 attributes['class'] = ('sticky-enabled' if \ 1193 php.empty(attributes['class']) else \ 1194 (attributes['class'] + ' sticky-enabled')) 1195 output = '<table' + drupal_attributes(attributes) + ">\n" 1196 if (php.isset(caption)): 1197 output += '<caption>' + caption + "</caption>\n" 1198 # Format the table columns: 1199 if (php.count(colgroups)): 1200 for number,colgroup in colgroups.items(): 1201 attributes = {} 1202 # Check if we're dealing with a simple or complex column 1203 if (php.isset(colgroup, 'data')): 1204 for key,value in colgroup.items(): 1205 if (key == 'data'): 1206 cols = value 1207 else: 1208 attributes[key] = value 1209 else: 1210 cols = colgroup 1211 # Build colgroup 1212 if (php.is_array(cols) and php.count(cols)): 1213 output += ' <colgroup' + drupal_attributes(attributes) + '>' 1214 i = 0 1215 for col in cols: 1216 output += ' <col' + drupal_attributes(col) + ' />' 1217 output += " </colgroup>\n" 1218 else: 1219 output += ' <colgroup' + drupal_attributes(attributes) + " />\n" 1220 # Format the table php.header: 1221 if (php.count(header_) > 0): 1222 ts = tablesort_init(header_); 1223 # HTML requires that the thead tag has tr tags in it follwed by tbody 1224 # tags+ 1225 #Using ternary operator to check and see if we have any rows+ 1226 output += (' <thead><tr>' if (php.count(rows) > 1) else ' <tr>'); 1227 for cell in header_: 1228 cell = tablesort_header(cell, header_, ts); 1229 output += _theme_table_cell(cell, True); 1230 # Using ternary operator to close the tags based on 1231 # whether or not there are rows 1232 output += (" </tr></thead>\n" if (php.count(rows) > 0) else "</tr>\n"); 1233 else: 1234 ts = []; 1235 # Format the table rows: 1236 if (php.count(rows) > 0): 1237 output += "<tbody>\n"; 1238 flip = {'even' : 'odd', 'odd' : 'even'}; 1239 class_ = 'even'; 1240 for number,row in rows.items(): 1241 attributes = []; 1242 # Check if we're dealing with a simple or complex row 1243 if (php.isset(row, 'data')): 1244 for key,value in row.items(): 1245 if (key == 'data'): 1246 cells = value; 1247 else: 1248 attributes[key] = value; 1249 else: 1250 cells = row; 1251 if (php.count(cells) > 0): 1252 # Add odd/even class 1253 class_ = flip[class_]; 1254 if (php.isset(attributes, 'class')): 1255 attributes['class'] += ' '+ class_; 1256 else: 1257 attributes['class'] = class_; 1258 # Build row 1259 output += ' <tr'+ drupal_attributes(attributes) +'>'; 1260 i = 0; 1261 for cell in cells: 1262 cell = tablesort_cell(cell, header_, ts, i); 1263 i += 1; 1264 output += _theme_table_cell(cell); 1265 output += " </tr>\n"; 1266 output += "</tbody>\n"; 1267 output += "</table>\n"; 1268 return output;
1269 1270 1271 1272
1273 -def table_select_header_cell():
1274 """ 1275 Returns a php.header cell for tables that have a select all functionality+ 1276 """ 1277 drupal_add_js('misc/tableselect.js'); 1278 return {'class' : 'select-all'};
1279 1280
1281 -def tablesort_indicator(style):
1282 """ 1283 Return a themed sort icon+ * 1284 @param style 1285 Set to either asc or desc+ 1286 This sets which icon to show+ 1287 @return 1288 A themed sort icon+ 1289 """ 1290 if (style == "asc"): 1291 return theme('image', 'misc/arrow-asc.png', t('sort icon'), \ 1292 t('sort ascending')); 1293 else: 1294 return theme('image', 'misc/arrow-desc.png', t('sort icon'), \ 1295 t('sort descending'));
1296 1297 1298
1299 -def box(title, content, region = 'main'):
1300 """ 1301 Return a themed box+ * 1302 @param title 1303 The subject of the box+ 1304 @param content 1305 The content of the box+ 1306 @param region 1307 The region in which the box is displayed+ 1308 @return 1309 A string containing the box output+ 1310 """ 1311 output = '<h2 class="title">'+ title +'</h2><div>'+ content +'</div>'; 1312 return output;
1313 1314
1315 -def mark(type = MARK_NEW):
1316 """ 1317 Return a themed marker, useful for marking new or updated 1318 content+ * 1319 @param type 1320 Number representing the marker type to display 1321 @see MARK_NEW, MARK_UPDATED, MARK_READ 1322 @return 1323 A string containing the marker+ 1324 """ 1325 if (lib_appglobals.user.uid > 0): 1326 if (type == MARK_NEW): 1327 return ' <span class="marker">'+ t('new') +'</span>'; 1328 elif (type == MARK_UPDATED): 1329 return ' <span class="marker">'+ t('updated') +'</span>';
1330 1331 1332
1333 -def item_list(items = [], title = None, type = 'ul', attributes = []):
1334 """ 1335 Return a themed list of items+ * 1336 @param items 1337 An array of items to be displayed in the list+ If an item is a string, 1338 then it is used as is+ If an item is an array, then the "data" element of 1339 the array is used as the contents of the list item+ If an item is an array 1340 with a "children" element, those children are displayed in a nested list+ 1341 All other elements are treated as attributes of the list item element+ 1342 @param title 1343 The title of the list+ 1344 @param attributes 1345 The attributes applied to the list element+ 1346 @param type 1347 The type of list to return (e.g+ "ul", "ol") 1348 @return 1349 A string containing the list output+ 1350 """ 1351 output = '<div class="item-list">'; 1352 if (title != None): 1353 output += '<h3>'+ title +'</h3>'; 1354 if (not php.empty(items)): 1355 output += "<type"+ drupal_attributes(attributes) +'>'; 1356 num_items = php.count(items); 1357 for i,item in items.items(): 1358 attributes = {}; 1359 children = {}; 1360 if (php.is_array(item)): 1361 for key,value in item.items(): 1362 if (key == 'data'): 1363 data = value; 1364 elif (key == 'children'): 1365 children = value; 1366 else: 1367 attributes[key] = value; 1368 else: 1369 data = item; 1370 if (php.count(children) > 0): 1371 # Render nested list 1372 data += theme_item_list(children, None, type, attributes); 1373 if (i == 0): 1374 attributes['class'] = ('first' if \ 1375 php.empty(attributes['class']) else (attributes['class'] +' first')); 1376 if (i == num_items - 1): 1377 attributes['class'] = ('last' if \ 1378 php.empty(attributes['class']) else (attributes['class'] +' last')); 1379 output += '<li'+ drupal_attributes(attributes) +'>'+ data +"</li>\n"; 1380 output += "</type>"; 1381 output += '</div>'; 1382 return output;
1383 1384 1385 1392 1393 1394
1395 -def xml_icon(url):
1396 """ 1397 Return code that emits an XML icon+ * 1398 For most use cases, this function has been superseded by theme_feed_icon() 1399 @see theme_feed_icon() 1400 @param url 1401 The url of the feed 1402 """ 1403 image = theme('image', 'misc/xml.png', t('XML feed'), t('XML feed')); 1404 if (image): 1405 return '<a href="'+ check_url(url) +'" class="xml-icon">'+ image +'</a>';
1406 1407
1408 -def feed_icon(url, title):
1409 """ 1410 Return code that emits an feed icon+ * 1411 @param url 1412 The url of the feed+ * @param title 1413 A descriptive title of the feed+ 1414 """ 1415 image = theme('image', 'misc/feed.png', t('Syndicate content'), title) 1416 if (image): 1417 return '<a href="'+ check_url(url) +'" class="feed-icon">'+ image +'</a>';
1418 1419 1431 1432 1433
1434 -def closure(main_ = 0):
1435 """ 1436 Execute hook_footer() which is run at the end of the page right before the 1437 close of the body tag+ * 1438 @param main (optional) 1439 Whether the current page is the front page of the site+ * @return 1440 A string containing the results of the hook_footer() calls+ 1441 """ 1442 footer = plugin_invoke_all('footer', main_); 1443 return php.implode("\n", footer) + drupal_get_js('footer');
1444 1445 1446
1447 -def blocks(region):
1448 """ 1449 Return a set of blocks available for the current user+ * 1450 @param region 1451 Which set of blocks to retrieve+ * @return 1452 A string containing the themed blocks for this region+ 1453 """ 1454 output = ''; 1455 list = block_list(region); 1456 if (list): 1457 for key,block in list.items(): 1458 # key == <i>plugin</i>_<i>delta</i> 1459 output += theme('block', block); 1460 # Add any content assigned to this region through drupal_set_content() calls+ 1461 output += drupal_get_content(region); 1462 return output;
1463 1464 1465
1466 -def username(object_):
1467 """ 1468 Format a username+ * 1469 @param object 1470 The user object to format, usually returned from user_load()+ * @return 1471 A string containing an HTML link to the user's page if the passed object 1472 suggests that this is a site user+ 1473 Otherwise, only the username is returned 1474 """ 1475 nameSet = (php.isset(object_, 'name') and not php.empty(object_.name)); 1476 if (object_.uid > 0 and nameSet): 1477 # Shorten the name when it is too long or it will break many tables+ 1478 if (drupal_strlen(object_.name) > 20): 1479 name = drupal_substr(object_.name, 0, 15) +'...'; 1480 else: 1481 name = object_.name; 1482 if (user_access('access user profiles')): 1483 output = l(name, 'user/' + object_.uid, {'attributes' : \ 1484 {'title' : t('View user profile.')}}) 1485 else: 1486 output = check_plain(name); 1487 elif (nameSet): 1488 # Sometimes plugins display content composed by people who are 1489 # not registered members of the site (e.g+ mailing list or news 1490 # aggregator plugins)+ This clause enables plugins to display 1491 # the True author of the content+ 1492 if (php.isset(object_, 'homepage') and not php.empty(object_.homepage)): 1493 output = l(object_.name, object_.homepage, {'attributes' : \ 1494 {'rel' : 'nofollow'}}); 1495 else: 1496 output = check_plain(object_.name); 1497 output += ' ('+ t('not verified') +')'; 1498 else: 1499 output = variable_get('anonymous', t('Anonymous')); 1500 return output;
1501 1502 1503
1504 -def progress_bar(percent, message):
1505 """ 1506 Return a themed progress bar+ * 1507 @param percent 1508 The percentage of the progress+ 1509 @param message 1510 A string containing information to be displayed+ 1511 @return 1512 A themed HTML string representing the progress bar 1513 """ 1514 output = '<div id="progress" class="progress">'; 1515 output += '<div class="bar"><div class="filled" style="width: '+ \ 1516 percent +'%"></div></div>'; 1517 output += '<div class="percentage">'+ percent +'%</div>'; 1518 output += '<div class="message">'+ message +'</div>'; 1519 output += '</div>'; 1520 return output;
1521 1522 1523
1524 -def indentation(size = 1):
1525 """ 1526 Create a standard indentation div+ Used for drag and drop tables+ * 1527 @param size 1528 Optional+ The number of indentations to create+ 1529 @return 1530 A string containing indentations+ 1531 """ 1532 output = ''; 1533 for n in range(0, size): 1534 output += '<div class="indentation">&nbsp;</div>'; 1535 return output;
1536 1537 1538 # 1539 # @} End of "defgroup themeable"+ */ 1540 # 1541 1542
1543 -def _table_cell(cell, header_ = False):
1544 attributes = ''; 1545 if (php.is_array(cell)): 1546 data = (cell['data'] if php.isset(cell, 'data') else ''); 1547 header_ |= php.isset(cell, 'header'); 1548 del(cell['data']); 1549 del(cell['header']); 1550 attributes = drupal_attributes(cell); 1551 else: 1552 data = cell; 1553 if (header_): 1554 output = "<th %(attributes)s>%(data)s</th>" % { 'data' : data, \ 1555 'attributes' : attributes }; 1556 else: 1557 output = "<td %(attributes)s>%(data)s</td>" % { 'data' : data, \ 1558 'attributes' : attributes }; 1559 return output;
1560 1561 1562
1563 -def template_preprocess(variables_, hook):
1564 """ 1565 Adds a default set of helper variables for preprocess functions and 1566 templates+ This comes in before any other preprocess function which makes 1567 it possible to be used in default theme implementations (non-overriden 1568 theme functions)+ 1569 """ 1570 php.static(template_preprocess, 'count', {}) 1571 php.Reference.check(variables_); 1572 # Track run count for each hook to provide zebra striping+ 1573 # See "template_preprocess_block()" which provides the same 1574 # feature specific to blocks+ 1575 template_preprocess.count[hook] = \ 1576 (template_preprocess.count[hook] \ 1577 if (php.isset(template_preprocess.count, hook) and \ 1578 is_int(template_preprocess.count[hook])) else 1); 1579 variables_['zebra'] = ('odd' if \ 1580 ((template_preprocess.count[hook] % 2) > 1) else 'even'); 1581 template_preprocess.count[hook] += 1; 1582 variables_['id'] = template_preprocess.count[hook]; 1583 # Tell all templates where they are located+ 1584 variables['directory'] = path_to_theme(); 1585 # Set default variables that depend on the database+ 1586 variables['is_admin'] = False; 1587 variables_['is_front'] = False; 1588 variables_['logged_in'] = False; 1589 variables_['db_is_active'] = db_is_active() ; 1590 if (variables_['db_is_active'] and not php.defined('MAINTENANCE_MODE')): 1591 # Check for administrators+ 1592 if (user_access('access administration pages')): 1593 variables_['is_admin'] = True; 1594 # Flag front page status+ 1595 variables['is_front'] = drupal_is_front_page(); 1596 # Tell all templates by which kind of user they're viewed+ 1597 variables['logged_in'] = (lib_appglobals.user.uid > 0); 1598 # Provide user object to all templates 1599 variables_['user'] = lib_appglobals.user;
1600 1601 1602
1603 -def template_preprocess_page(variables_):
1604 """ 1605 Process variables for page.tpl.php 1606 1607 Most themes utilize their own copy of page.tpl.php+ The default is located 1608 inside "plugins/system/page.tpl.php"+ Look in there for the full list of 1609 variables+ * 1610 Uses the arg() function to generate a series of page template suggestions 1611 based on the current path+ * 1612 Any changes to variables in this preprocessor should also be changed inside 1613 template_preprocess_maintenance_page() to keep all them consistent+ * 1614 The variables array contains the following arguments: 1615 - content 1616 - show_blocks 1617 1618 @see page.tpl.php 1619 """ 1620 php.Reference.check(variables_); 1621 # Add favicon 1622 if (theme_get_setting('toggle_favicon')): 1623 drupal_set_html_head('<link rel="shortcut icon" href="'+ \ 1624 check_url(theme_get_setting('favicon')) +'" type="image/x-icon" />'); 1625 # Populate all block regions+ 1626 regions = system_region_list(lib_appglobals.theme); 1627 # Load all region content assigned via blocks+ 1628 for region in php.array_keys(regions): 1629 # Prevent left and right regions from rendering blocks when 1630 # 'show_blocks' == False+ 1631 if (not (not variables_['show_blocks'] and \ 1632 (region == 'left' or region == 'right'))): 1633 blocks = theme('blocks', region); 1634 else: 1635 blocks = ''; 1636 # Assign region to a region variable+ 1637 if (php.isset(variables_, region)): 1638 variables_[region] += blocks 1639 else: 1640 variables_[region] = blocks; 1641 # Set up layout variable+ 1642 variables_['layout'] = 'none'; 1643 if (not php.empty(variables_['left'])): 1644 variables_['layout'] = 'left'; 1645 if (not php.empty(variables_['right'])): 1646 variables_['layout'] = ('both' if \ 1647 (variables_['layout'] == 'left') else 'right'); 1648 # Set mission when viewing the frontpage+ 1649 if (drupal_is_front_page()): 1650 mission = filter_xss_admin(theme_get_setting('mission')); 1651 else: 1652 mission = None; 1653 # Construct page title 1654 if (drupal_get_title()): 1655 head_title = [strip_tags(drupal_get_title()), \ 1656 variable_get('site_name', 'Drupal')]; 1657 else: 1658 head_title = [variable_get('site_name', 'Drupal')]; 1659 if (variable_get('site_slogan', '')): 1660 head_title.append( variable_get('site_slogan', '') ); 1661 variables_['head_title'] = php.implode(' | ', head_title); 1662 variables_['base_path'] = base_path(); 1663 variables_['front_page'] = url(); 1664 variables_['breadcrumb'] = theme('breadcrumb', \ 1665 drupal_get_breadcrumb()); 1666 variables_['feed_icons'] = drupal_get_feeds(); 1667 variables_['footer_message'] = \ 1668 filter_xss_admin(variable_get('site_footer', False)); 1669 variables_['head'] = drupal_get_html_head(); 1670 variables_['help'] = theme('help'); 1671 variables_['language'] = lib_appglobals.language; 1672 variables_['language'].dir = ('rtl' if \ 1673 (php.isset(lib_appglobals.language, 'direction') and \ 1674 not php.empty(lib_appglobals.language.direction)) else 'ltr'); 1675 variables_['logo'] = theme_get_setting('logo'); 1676 variables_['messages'] = (theme('status_messages') if \ 1677 variables['show_messages'] else ''); 1678 variables_['mission'] = (mission if (mission != None) else ''); 1679 variables_['main_menu'] = (lib_menu.main_menu() if \ 1680 theme_get_setting('toggle_main_menu') else []); 1681 variables_['secondary_menu'] = (lib_menu.secondary_menu() if \ 1682 theme_get_setting('toggle_secondary_menu') else []); 1683 variables_['search_box'] = \ 1684 (drupal_get_form('search_theme_form') if \ 1685 theme_get_setting('toggle_search') else ''); 1686 variables_['site_name'] = \ 1687 (variable_get('site_name', 'Drupal') if \ 1688 theme_get_setting('toggle_name') else ''); 1689 variables_['site_slogan'] = \ 1690 (variable_get('site_slogan', '') if \ 1691 theme_get_setting('toggle_slogan') else ''); 1692 variables_['css'] = drupal_add_css(); 1693 variables_['styles'] = drupal_get_css(); 1694 variables_['scripts'] = drupal_get_js(); 1695 variables_['tabs'] = theme('menu_local_tasks'); 1696 variables_['title'] = drupal_get_title(); 1697 # Closure should be filled last+ 1698 variables_['closure'] = theme('closure'); 1699 node = lib_menu.get_object(); 1700 if (node): 1701 variables_['node'] = node; 1702 # Compile a list of classes that are going to be applied to the body element+ 1703 # This allows advanced theming based on context 1704 # (home page, node of certain type, etc.)+ 1705 body_classes = []; 1706 # Add a class that tells us whether we're on the front page or not+ 1707 body_classes.append( ('front' if \ 1708 variables_['is_front'] else 'not-front') ); 1709 # Add a class that tells us whether 1710 # the page is viewed by an authenticated user or not+ 1711 body_classes.append( ('logged-in' if \ 1712 variables_['logged_in'] else 'not-logged-in') ); 1713 # Add arg(0) to make it possible to theme 1714 # the page depending on the current page 1715 # type (e.g+ node, admin, user, etc.) 1716 # To avoid illegal characters in the class, 1717 # we're removing everything disallowed 1718 # We are not using 'a-z' as that might leave 1719 # in certain international characters (e.g+ German umlauts)+ 1720 body_classes.append(\ 1721 php.preg_replace('not [^abcdefghijklmnopqrstuvwxyz0-9-_]+not s', '', \ 1722 'page-'+ form_clean_id(drupal_strtolower(arg(0)))) ); 1723 # If on an individual node page, add the node type+ 1724 if (php.isset(variables_, 'node') and variables_['node'].type_): 1725 body_classes.append( 'node-type-'+ \ 1726 form_clean_id(variables_['node'].type_) ); 1727 # Add information about the number of sidebars+ 1728 if (variables_['layout'] == 'both'): 1729 body_classes.append( 'two-sidebars' ); 1730 elif (variables_['layout'] == 'none'): 1731 body_classes.append( 'no-sidebars' ); 1732 else: 1733 body_classes.append( 'one-sidebar sidebar-'+ variables_['layout'] ); 1734 # Implode with spaces+ 1735 variables_['body_classes'] = php.implode(' ', body_classes); 1736 # Build a list of suggested template files in order of specificity+ One 1737 # suggestion is made for every element of the current path, though 1738 # numeric elements are not carried to subsequent suggestions+ For example, 1739 # http://www.example.com/node/1/edit would result in the following 1740 # suggestions: 1741 # 1742 # page-node-edit.tpl.php 1743 # page-node-1.tpl.php 1744 # page-node.tpl.php 1745 # page.tpl.php 1746 i = 0; 1747 suggestion = 'page'; 1748 suggestions = []; 1749 while True: 1750 arg_ = arg(i); 1751 if (arg_ == False or arg_ == None): 1752 break; 1753 else: 1754 i += 1; 1755 suggestions.append( suggestion +'-'+ arg_ ); 1756 if (not php.is_numeric(arg_)): 1757 suggestion += '-'+ arg_; 1758 if (drupal_is_front_page()): 1759 suggestions.append( 'page-front' ); 1760 if (not php.empty(suggestions)): 1761 variables_['template_files'] = suggestions;
1762 1763 1764 1765
1766 -def template_preprocess_node(variables_):
1767 """ 1768 Process variables for node.tpl.php 1769 1770 Most themes utilize their own copy of node.tpl.php+ The default is located 1771 inside "plugins/node/node.tpl.php"+ Look in there for the full list of 1772 variables+ * 1773 The variables array contains the following arguments: 1774 - node 1775 - teaser 1776 - page 1777 1778 @see node.tpl.php 1779 """ 1780 php.Reference.check(variables); 1781 node = variables_['node']; 1782 if (plugin_exists('taxonomy')): 1783 variables_['taxonomy'] = taxonomy_link('taxonomy terms', node); 1784 else: 1785 variables_['taxonomy'] = {}; 1786 if (variables_['teaser'] and node.teaser): 1787 variables_['content'] = node.teaser; 1788 elif (php.isset(node, 'body')): 1789 variables_['content'] = node.body; 1790 else: 1791 variables_['content'] = ''; 1792 variables_['date'] = format_date(node.created); 1793 variables_['links'] = (theme('links', node.links, \ 1794 {'class' : 'links inline'}) if (not php.empty(node.links)) else ''); 1795 variables_['name'] = theme('username', node); 1796 variables_['node_url'] = url('node/'+ node.nid); 1797 variables_['terms'] = theme('links', variables_['taxonomy'], \ 1798 {'class' : 'links inline'}); 1799 variables_['title'] = check_plain(node.title); 1800 # Flatten the node object's member fields+ 1801 variables_ = php.array_merge(drupy_array(node), variables_); 1802 # Display info only on certain node types+ 1803 if (theme_get_setting('toggle_node_info_'+ node.type_)): 1804 variables_['submitted'] = theme('node_submitted', node); 1805 variables_['picture'] = (theme('user_picture', node) if \ 1806 theme_get_setting('toggle_node_user_picture') else ''); 1807 else: 1808 variables_['submitted'] = ''; 1809 variables_['picture'] = ''; 1810 # Clean up name so there are no underscores+ 1811 variables_['template_files'].append( 'node-' + \ 1812 php.str_replace('_', '-', node.type_) ) 1813 variables_['template_files'].append( 'node-' + node.nid )
1814 1815 1816 1817
1818 -def template_preprocess_block(variables_):
1819 """ 1820 Process variables for block.tpl.php 1821 1822 Prepare the values passed to the theme_block function to be passed 1823 into a pluggable template engine+ Uses block properties to generate a 1824 series of template file suggestions+ If none are found, the default 1825 block.tpl.php is used+ * 1826 Most themes utilize their own copy of block.tpl.php+ The default is located 1827 inside "plugins/system/block.tpl.php"+ Look in there for the full list of 1828 variables+ * 1829 The variables array contains the following arguments: 1830 - block 1831 1832 @see block.tpl.php 1833 """ 1834 php.static(template_preprocess_block, 'block_counter', {}) 1835 php.Reference.check(variables_); 1836 # All blocks get an independent counter for each region+ 1837 if (not php.isset(template_preprocess_block.block_counter, \ 1838 variables_['block'].region)): 1839 template_preprocess_block.block_counter[variables_['block'].region] = 1 1840 # Same with zebra striping+ 1841 variables_['block_zebra'] = ('odd' if \ 1842 ((template_preprocess_block.block_counter[variables_['block'].region] \ 1843 % 2) > 0) else 'even'); 1844 variables_['block_id'] = \ 1845 template_preprocess_block.block_counter[variables_['block'].region]; 1846 template_preprocess_block.block_counter[variables_['block'].region] += 1; 1847 variables_['template_files'].append( \ 1848 'block-'+ variables_['block'].region ); 1849 variables_['template_files'].append( \ 1850 'block-'+ variables_['block'].plugin ); 1851 variables_['template_files'].append( \ 1852 'block-'+ variables_['block'].plugin +'-'+ \ 1853 variables_['block'].delta );
1854