, but I couldn't find one here to discuss well the theme, so I'm opening this thread for this.
As the name symbolizes it, it is meant to be the body structural part of a default XOOPS theme template serving as a canvas starting point for theme designers to develop their designs under specific XOOPS standards and W3C compliance.
. The process I did was to take the graphical part out and leave just the wire frame keeping the architecture done by Leo.
Despite I have continued with the 1.0.X branch on the themes I've been making, the 2.0 branch has additional code by Kris with lots of extra functions.
Kris has taken the time to define all the structural elements at a wiki he has done here:
What I wanted to show those who are interested in relation to this is something David showed me earlier which looks like the theme engine and where we see clearly the reason of being of the main properties defined in the above theme.html.
.
<?php 2 /** 3 * xos_opal_Theme component class file 4 * 5 * @copyright The XOOPS project https://xoops.org/ 6 * @license http://www.fsf.org/copyleft/gpl.html GNU public license 7 * @author Skalpa Keo <skalpa@xoops.org> 8 * @since 2.3.0 9 * @version $Id$ 10 * @package xos_opal 11 * @subpackage xos_opal_Theme 12 */ 13 14 /** 15 * xos_opal_ThemeFactory 16 * 17 * @author Skalpa Keo 18 * @package xos_opal 19 * @subpackage xos_opal_Theme 20 * @since 2.3.0 21 */ 22 class xos_opal_ThemeFactory { 23 /** 24 * Currently enabled themes (if empty, all the themes in themes/ are allowed) 25 * @var array 26 */ 27 var $allowedThemes = array(); 28 /** 29 * Default theme to instanciate if none specified 30 * @var string 31 */ 32 var $defaultTheme = 'default'; 33 /** 34 * If users are allowed to choose a custom theme 35 * @var bool 36 */ 37 var $allowUserSelection = true; 38 39 /** 40 * Instanciate the specified theme 41 */ 42 function &createInstance( $options = array(), $initArgs = array() ) { 43 // Grab the theme folder from request vars if present 44 if ( @empty( $options['folderName'] ) ) { 45 if ( $req = @$_REQUEST['_xo_theme_name'] ) { 46 if ( $this->isThemeAllowed( $req ) ) { 47 $options['folderName'] = $req; 48 if ( isset( $_SESSION ) && $this->allowUserSelection ) { 49 $_SESSION[ $this->xoBundleIdentifier ]['defaultTheme'] = $req; 50 } 51 } 52 } elseif ( isset( $_SESSION[ $this->xoBundleIdentifier ]['defaultTheme'] ) ) { 53 $options['folderName'] = $_SESSION[ $this->xoBundleIdentifier ]['defaultTheme']; 54 } elseif ( @empty( $options['folderName'] ) || !$this->isThemeAllowed( $options['folderName'] ) ) { 55 $options['folderName'] = $this->defaultTheme; 56 } 57 } 58 // Retrieve the desired content-type from the request 59 if ( @empty( $options['contentType'] ) && !empty($_REQUEST['_xo_mime_type']) ) { 60 $options['contentType'] = $_REQUEST['_xo_mime_type']; 61 } 62 63 // Read the theme bundle info file 64 global $xoops; 65 $options['path'] = $xoops->path( '/themes/' . $options['folderName'] ); 66 if ( $info = @include( $options['path'] . "/xo-info.php" ) ) { 67 $options = array_merge( $info, $options ); 68 } else { 69 $options['themeAPI'] = '2.0'; 70 $options['parentTheme'] = ( $options['folderName'] == 'xoops20' ? '' : 'xoops20' ); 71 } 72 $inst =& XOS::createInstanceOf( 'xos_opal_Theme', $options ); 73 74 return $inst; 75 } 76 77 /** 78 * Checks if the specified theme is enabled or not 79 * @param string $name 80 * @return bool 81 */ 82 function isThemeAllowed( $name ) { 83 return ( empty( $this->allowedThemes ) || in_array( $name, $this->allowedThemes ) ); 84 } 85 86 /** 87 * List the available themes 88 * 89 * @param boolean $allowed Whether to return the allowed themes, or all of them 90 * @return array 91 */ 92 function enumerate( $allowed = false ) { 93 global $xoops; 94 $themes = array(); 95 $root = $xoops->path( '/themes/' ); 96 if ( $dh = opendir( $root ) ) { 97 while ( $file = readdir($dh) ) { 98 if ( $file{0} != '.' && $file != 'CVS' && is_dir( "$root/$file" ) ) { 99 $themes[] = $file; 100 } 101 } 102 closedir( $dh ); 103 } 104 if ( !empty($this->allowedThemes) && $allowed ) { 105 return array_intersect( $themes, $this->allowedThemes ); 106 } 107 return $themes; 108 } 109 110 } 111 112 class xos_opal_Theme { 113 /** 114 * The name of this theme 115 * @var string 116 */ 117 var $folderName = ''; 118 /** 119 * Physical path of this theme folder 120 * @var string 121 */ 122 var $path = ''; 123 var $url = ''; 124 125 /** 126 * Default content-type of pages generated by this theme 127 * @var string 128 */ 129 var $contentType = ''; 130 /** 131 * Output format doctype 132 * @var string 133 */ 134 var $doctype = 'html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"'; 135 /** 136 * Document namespaces 137 * @var string 138 */ 139 var $namespaces = array(); 140 141 /** 142 * Whether or not the theme engine should include the output generated by php 143 * @var string 144 */ 145 var $bufferOutput = true; 146 /** 147 * Canvas-level template to use 148 * @var string 149 */ 150 var $canvasTemplate = ''; 151 /** 152 * Page-level template to use 153 * @var string 154 */ 155 var $pageTemplate = ''; 156 /** 157 * Content-level template to use 158 * @var string 159 */ 160 var $contentTemplate = ''; 161 /** 162 * Text content to display right after the contentTemplate output 163 * @var string 164 */ 165 var $content = ''; 166 /** 167 * The API version supported by this theme (used to achieve BC) 168 * @var string 169 */ 170 var $themeAPI = '2.3'; 171 /** 172 * Name of this theme parent (if any) 173 * @var string 174 */ 175 var $parentTheme = ''; 176 /** 177 * Array containing all this theme ancestors (parent,grand-parent,etc...) 178 * @var array 179 * @access protected 180 */ 181 var $parentInfos = array(); 182 /** 183 * Page construction plug-ins to use 184 * @var array 185 * @access public 186 */ 187 var $plugins = array( 'xos_logos_PageBuilder' ); 188 189 /** 190 * List of mime-types supported by this theme 191 * @var array 192 * @access public 193 */ 194 var $supportedMimeTypes = ''; 195 var $allowXHTML = true; 196 197 var $renderCount = 0; 198 /** 199 * Pointer to the theme template engine 200 * @var xos_opal_Smarty 201 */ 202 var $template = false; 203 204 /** 205 * Array containing the document meta-information 206 * @var array 207 */ 208 var $metas = array( 209 'http' => array( 210 'Content-Script-Type' => 'text/javascript', 211 'Content-Style-Type' => 'text/css', 212 ), 213 'meta' => array( 214 'generator' => 'XOOPS', 215 'robots' => 'index,follow', 216 'copyright' => 'Copyright 2000-2006', 217 ), 218 'link' => array(), 219 'script' => array(), 220 ); 221 222 /** 223 * Array of strings to be inserted in the head tag of HTML documents 224 * @var array 225 */ 226 var $htmlHeadStrings = array(); 227 /** 228 * Custom variables that will always be assigned to the template 229 * @var array 230 */ 231 var $templateVars = array( 232 'slogan' => 'Put a slogan if you wish', 233 'footer' => 'Powered by XOOPS, the noosphere, and caffeine', 234 ); 235 236 237 /**#@-*/ 238 239 /**#@+ @tasktype 10 Initialization*/ 240 /** 241 * Initializes this theme 242 * 243 * Upon initialization, the theme creates its template engine and instanciates the 244 * plug-ins from the specified {@link $plugins} list. If the theme is a 2.0 theme, that does not 245 * display redirection messages, the HTTP redirections system is disabled to ensure users will 246 * see the redirection screen. 247 * 248 * @param array $options 249 * @return bool 250 */ 251 function xoInit( $options = array() ) { 252 global $xoops; 253 254 if ( isset( $_SESSION ) ) { 255 $_SESSION[$xoops->services['http']->xoBundleIdentifier]['tmpDisallowRedirections'] = ( $this->themeAPI == '2.0' ); 256 } 257 if ( $this->namespaces && !is_array( $this->namespaces ) ) { 258 $this->namespaces = array( '' => $this->namespaces ); 259 } 260 if ( $this->supportedMimeTypes && !is_array( $this->supportedMimeTypes ) ) { 261 $this->supportedMimeTypes = explode( ",", $this->supportedMimeTypes ); 262 } 263 264 if ( !$this->contentType || !in_array( $this->contentType, $this->supportedMimeTypes ) ) { 265 $this->contentType = $this->supportedMimeTypes[0]; 266 } 267 if ( $this->contentType == 'application/xhtml+xml' ) { 268 if ( !$this->allowXHTML || false === strpos( $_SERVER['HTTP_ACCEPT'], 'application/xhtml+xml' ) ) { 269 $this->contentType = 'text/html'; 270 } 271 } 272 273 $this->template =& XOS::create( 'xos_opal_Smarty' ); 274 $this->template->currentTheme =& $this; 275 $this->template->compile_id = ''; 276 277 if ( $this->bufferOutput ) { 278 ob_start(); 279 } 280 // Instanciate and initialize all the theme plugins 281 foreach ( $this->plugins as $k => $bundleId ) { 282 $this->plugins[$k] =& XOS::create( $bundleId, array( 'theme' => &$this ) ); 283 } 284 return true; 285 } 286 /**#@-*/ 287 288 /**#@+ @tasktype 20 Manipulating page meta-iformation*/ 289 /** 290 * Adds script code to the document head 291 * 292 * This methods allows the insertion of an external script file (if $src is provided), or 293 * of a script snippet. The file URI is parsed to take benefit of the theme resource 294 * overloading system. 295 * 296 * The $attributes parameter allows you to specify the attributes that will be added to the 297 * inserted <script> tag. If unspecified, the <var>type</var> attribute value will default to 298 * 'text/javascript'. 299 * 300 * <code> 301 * // Add an external script using a physical path (not recommended) 302 * $theme->addScript( '/www/script.js', null, '' ); 303 * // Add an external script that belongs to a component 304 * $theme->addScript( 'xos_pyro_TreeWidget#www/script.js', null, '' ); 305 * $theme->addScript( 'mod_xoops_SiteManager#common.js', null, '' ); 306 * // Specify attributes for the <script> tag 307 * $theme->addScript( 'mod_xoops_SiteManager#common.js', array( 'type' => 'application/x-javascript' ), '' ); 308 * // Insert a code snippet 309 * $theme->addScript( null, array( 'type' => 'application/x-javascript' ), 'window.open("Hello world");' ); 310 * </code> 311 * 312 * @param string $src path to an external script file 313 * @param array $attributes hash of attributes to add to the <script> tag 314 * @param string $content Code snippet to output within the <script> tag 315 * 316 * @return void 317 **/ 318 function addScript( $src = '', $attributes = array(), $content = '' ) { 319 global $xoops; 320 if ( empty( $attributes ) ) { 321 $attributes = array(); 322 } 323 if ( !empty( $src ) ) { 324 $attributes['src'] = $xoops->url( $this->resourcePath( $src ) ); 325 } 326 if ( !empty( $content ) ) { 327 $attributes['_'] = $content; 328 } 329 if ( !isset( $attributes['type'] ) ) { 330 $attributes['type'] = 'text/javascript'; 331 } 332 $this->setMeta( 'script', $src, $attributes ); 333 } 334 335 /** 336 * Add StyleSheet or CSS code to the document head 337 * 338 * @param string $src path to .css file 339 * @param array $attributes name => value paired array of attributes such as title 340 * @param string $content CSS code to output between the <style> tags (in case $src is empty) 341 * 342 * @return void 343 **/ 344 function addStylesheet( $src = '', $attributes = array(), $content = '' ) { 345 global $xoops; 346 if ( !empty( $src ) ) { 347 $attributes['href'] = $xoops->url( $this->resourcePath( $src ) ); 348 } 349 if ( !isset($attributes['type']) ) { 350 $attributes['type'] = 'text/css'; 351 } 352 if ( !empty( $content ) ) { 353 $attributes['_'] = $content; 354 } 355 $this->setMeta( 'stylesheet', $src, $attributes ); 356 } 357 /** 358 * Add a <link> to the header 359 * 360 * @param string $rel Relationship from the current doc to the anchored one 361 * @param string $href URI of the anchored document 362 * @param array $attributes Additional attributes to add to the <link> element 363 */ 364 function addLink( $rel, $href = '', $attributes = array() ) { 365 global $xoops; 366 if ( !empty( $href ) ) { 367 $attributes['href'] = $href; 368 } 369 $this->setMeta( 'link', $rel, $attributes ); 370 } 371 /** 372 * Set a meta http-equiv value 373 */ 374 function setHttpMeta( $name, $value = null ) { 375 if ( isset($value) ) { 376 return $this->setMeta( 'http', $name, $value ); 377 } 378 unset( $this->metas['http'][$name] ); 379 } 380 381 382 /** 383 * Change output page meta-information 384 */ 385 function setMeta( $type = 'meta', $name = '', $value = '' ) { 386 if ( !isset( $this->metas[$type] ) ) { 387 $this->metas[$type] = array(); 388 } 389 if ( isset($name) ) { 390 $this->metas[$type][$name] = $value; 391 } else { 392 $this->metas[$type][] = $value; 393 } 394 return $value; 395 } 396 397 function headContent( $params, $content, &$smarty, &$repeat ) { 398 if ( !$repeat ) { 399 $this->htmlHeadStrings[] = $content; 400 } 401 } 402 403 function setNamespace( $ns, $uri ) { 404 $this->namespaces[$ns] = $uri; 405 } 406 407 /** 408 * Returns the document namespaces as an xmlns string that can be inserted in the main tag 409 */ 410 function namespacesString() { 411 $str = array(); 412 foreach ( $this->namespaces as $ns => $uri ) { 413 if ( !empty( $ns ) ) { 414 $ns = ':' . $ns; 415 } 416 $str[] = 'xmlns' . $ns . '="' . $uri . '"'; 417 } 418 return implode( ' ', $str ); 419 } 420 /**#@-*/ 421 422 /** 423 * Render the page 424 * 425 * The theme engine builds pages from 3 templates: canvas, page and content. 426 * 427 * The canvas template is the outermost one. It is the one containing the html container 428 * elements (html,head,body), the header, footer and the left and right columns. 429 * 430 * Standard themes should be delivered with the following canvas templates: 431 * - canvas-default.xotpl: The "normal" template, used by most pages on a site 432 * - canvas-dialog.xotpl: A lightweight canvas, without left and right columns, used by popups 433 * - canvas-email.xotpl: The canvas used by e-mails sent by the site 434 * 435 * The page template is the container for center blocks and the content. Themes don't have to 436 * include several page templates, but applications may have their own page template that is 437 * used instead of the default one (i.e: the XOOPS Management module). 438 * 439 * A module can call this method directly and specify what templates the theme engine must use. 440 * If render() hasn't been called before, the theme defaults will be used for the canvas and 441 * page template (and xoopsOption['template_main'] for the content). 442 * 443 * @param string $canvasTpl The canvas template, if different from the theme default 444 * @param string $pageTpl The page template, if different from the theme default 445 * @param string $contentTpl The content template 446 * @param array $vars Template variables to send to the template engine 447 */ 448 function render( $canvasTpl = null, $pageTpl = null, $contentTpl = null, $vars = array() ) { 449 global $xoops; 450 451 $this->template->compile_id = $this->folderName . '-' . substr( $this->contentType, strrpos( $this->contentType, '/' ) + 1 ); 452 453 if ( !$this->renderCount && $this->bufferOutput ) { 454 $this->content .= ob_get_contents(); 455 ob_end_clean(); 456 } 457 458 if ( $this->contentType == 'application/pdf' ) { 459 $pdfmaker =& XOS::create( 'xos_opal_PdfMaker' ); 460 $pdfmaker->startCapture(); 461 $xoops->services['logger']->activated = false; 462 } 463 464 if ( $this->themeAPI != '2.3' ) { 465 include $xoops->path( $this->xoBundleRoot . '/render-' . $this->themeAPI . '.php' ); 466 } 467 if ( !empty($canvasTpl) ) { 468 $this->canvasTemplate = $canvasTpl; 469 } 470 if ( !empty($pageTpl) ) { 471 $this->pageTemplate = $pageTpl; 472 } 473 if ( !empty($contentTpl) ) { 474 $this->contentTemplate = $contentTpl; 475 } 476 $this->template->assign_by_ref( 'xoModule', $xoops->currentModule ); 477 $this->template->register_object( 'xoModule', &$xoops->currentModule, null, false ); 478 $this->template->register_object( 'xoTheme', &$this, null, false, array( 'headContent' ) ); 479 480 /* this will be changed */ 481 $vars['xoops_dirname'] = @!empty( $GLOBALS['xoopsModule'] ) ? $GLOBALS['xoopsModule']->getVar('dirname') : 'system'; 482 $this->template->assign( $vars ); 483 484 if ( $xoops->services['http'] ) { 485 if ( $this->contentType == 'application/xhtml+xml' && !($this->allowXHTML & 2) ) { 486 $xoops->services['http']->setEntityInfo( 'text/html', 'iso-8859-1' ); 487 } else { 488 $xoops->services['http']->setEntityInfo( $this->contentType, 'iso-8859-1' ); 489 } 490 $xoops->services['http']->addVariation( 'xo-theme', $this->xoBundleIdentifier ); 491 } 492 $this->renderZone( 'canvas' ); 493 $this->renderCount++; 494 } 495 496 /** 497 * Render the specified page part 498 * @param string $zone 499 */ 500 function renderZone( $zone ) { 501 switch ( $zone ) { 502 case 'canvas': 503 $this->renderCanvas(); 504 break; 505 case 'page': 506 $this->renderPage(); 507 echo $this->pageContent; 508 break; 509 case 'content': 510 $this->renderContent(); 511 } 512 } 513 514 function renderCanvas() { 515 $this->renderPage(); 516 $this->template->display( $this->getZoneTemplate( 'canvas' ) ); 517 } 518 519 function renderPage() { 520 ob_start(); 521 if ( $tpl = $this->getZoneTemplate( 'page' ) ) { 522 $this->template->display( $tpl ); 523 } else { 524 $this->renderContent(); 525 } 526 $this->pageContent = ob_get_contents(); 527 ob_end_clean(); 528 } 529 530 function renderContent() { 531 if ( $tpl = $this->getZoneTemplate( 'content' ) ) { 532 $this->template->display( $tpl ); 533 } 534 if ( !empty($this->content) ) { 535 echo $this->content; 536 } 537 } 538 539 function getZoneTemplate( $zone ) { 540 global $xoops; 541 $zones = array( 'canvas' => 0, 'page' => 1, 'content' => 2 ); 542 $tpl = ''; 543 if ( isset( $zones[$zone] ) ) { 544 $tpl = $zone . 'Template'; 545 $tpl = $this->$tpl; 546 if ( !empty( $tpl ) ) { 547 if ( substr( $tpl, 0, 1 ) == '.' ) { 548 $tpl = $xoops->path( $this->resourcePath( substr( $tpl, 1 ) ) ); 549 } elseif ( !strpos( $tpl, ':' ) ) { 550 $tpl = 'xotpl:' . $tpl; 551 } 552 } 553 } 554 return $tpl; 555 } 556 557 function renderMetas( $type = null ) { 558 if ( !isset($type) ) { 559 foreach ( array_keys($this->metas) as $type ) { 560 $this->renderMetas( $type ); 561 } 562 echo implode( "n", $this->htmlHeadStrings ); 563 return; 564 } 565 switch ( $type ) { 566 case 'script': 567 foreach ( $this->metas[$type] as $attrs ) { 568 echo '<script' . $this->renderAttributes( $attrs ) . ">n"; 569 if ( @$attrs['_'] ) { 570 echo "n//<![CDATA[n" . $attrs['_'] . "n//]]>"; 571 } 572 echo "</script>n"; 573 } 574 break; 575 case 'link': 576 foreach ( $this->metas[$type] as $rel => $attrs ) { 577 echo '<link rel="' . $rel . '"' . $this->renderAttributes( $attrs ) . " />n"; 578 } 579 break; 580 case 'stylesheet': 581 foreach ( $this->metas[$type] as $attrs ) { 582 if ( @$attrs['_'] ) { 583 echo '<style' . $this->renderAttributes($attrs) . ">n/* <![CDATA[ */n" . $attrs['_'] . "n/* //]]> */n</style>"; 584 } else { 585 echo '<link rel="stylesheet"' . $this->renderAttributes($attrs) . " />n"; 586 } 587 } 588 break; 589 case 'http': 590 foreach ( $this->metas[$type] as $name => $content ) { 591 echo '<meta http-equiv="' . htmlspecialchars( $name, ENT_QUOTES ) . '" content="' . htmlspecialchars( $content, ENT_QUOTES) . "" />n"; 592 } 593 break; 594 default: 595 foreach ( $this->metas[$type] as $name => $content ) { 596 echo '<meta name="' . htmlspecialchars( $name, ENT_QUOTES ) . '" content="' . htmlspecialchars( $content, ENT_QUOTES) . "" />n"; 597 } 598 break; 599 } 600 } 601 602 function genElementId( $tagName = 'xos' ) { 603 static $cache = array(); 604 if ( !isset( $cache[ $tagName ] ) ) { 605 $cache[$tagName] = 1; 606 } 607 return $tagName . '-' . $cache[$tagName]++; 608 } 609 610 function renderAttributes( $coll ) { 611 $str = ''; 612 foreach ( $coll as $name => $val ) { 613 if ( $name != '_' ) { 614 $str .= ' ' . $name . '="' . htmlspecialchars( $val, ENT_QUOTES ) . '"'; 615 } 616 } 617 return $str; 618 } 619 620 621 function resourcePath( $path, $fromDocRoot = true ) { 622 global $xoops; 623 624 $parts = explode( '#', $path, 2 ); 625 if ( count( $parts ) > 1 ) { 626 list( $bundleId, $resPath ) = $parts; 627 // This is component resource: modules are in 'modules', and components in 'components' 628 $themedRoot = ( substr( $parts[0], 0, 4 ) == 'mod_' ) ? 'modules' : 'components'; 629 if ( file_exists( "$this->path/$themedRoot/$bundleId/$resPath" ) ) { 630 return "themes/$this->folderName/$themedRoot/$bundleId/$resPath"; 631 } else { 632 return XOS::classVar( $bundleId, 'xoBundleRoot' ) . '/' . $resPath; 633 } 634 } 635 if ( substr( $path, 0, 1 ) == '/' ) { 636 $path = substr( $path, 1 ); 637 $fromDocRoot = false; 638 } 639 if ( file_exists( "$this->path/$path" ) ) { 640 return "themes/$this->folderName/$path"; 641 } 642 if ( !empty( $this->parentTheme ) ) { 643 if ( !is_object( $this->parentTheme ) ) { 644 $this->parentTheme =& XOS::create( 'xos_opal_Theme', array( 'folderName' => $this->parentTheme ) ); 645 } 646 if ( is_object( $this->parentTheme ) ) { 647 return $this->parentTheme->resourcePath( $path, $fromDocRoot ); 648 } 649 } 650 return $fromDocRoot ? "www/$path" : "themes/$this->folderName/$path"; 651 } 652 653 654 655 656 } 657 658 659 ?>
I think that is excellent work, and shows the significance and importance of clearly defined properties. Yet we could only see it's true power if the developers of XOOPS follow the same line and merge the past work with future work, otherwise this is just an example of what could have been.