Rewriting the Drupal Theme layer

2,780 views

Published on

Okay. Once upon a time there was a content management system that needed to render markup. So this Content Managment System returned strings of HTML. Well that really wasn't that great because there were these people out in the world called Front End Developers and it just so happens that Front End Developers actually care about what the HTML looks like. Oh, by the way, this Content Management System is named Drupal.

Read more: http://2013.drupalcampcolorado.org/sessions/rewriting-drupal-theme-layer

Published in: Technology
0 Comments
0 Likes
Statistics
Notes
  • Be the first to comment

  • Be the first to like this

No Downloads
Views
Total views
2,780
On SlideShare
0
From Embeds
0
Number of Embeds
39
Actions
Shares
0
Downloads
0
Comments
0
Likes
0
Embeds 0
No embeds

No notes for slide

Rewriting the Drupal Theme layer

  1. 1. Rewriting the Drupal theme layer Carl Wiedemann DrupalCamp Colorado • 2013-06-29
  2. 2. Hi! c4rl Carl Wiedemann
  3. 3. Rewriting the Drupal theme layer
  4. 4. Theming: What's happening in D8?
  5. 5. Theming: Why is what is happening in D8 happening in D8?
  6. 6. A history of theming to understand why what is happening in D8 happening in D8?
  7. 7. What/Why/How is theming?
  8. 8. Theming?
  9. 9. ??
  10. 10. This is not a how-to session. This is a how-come? session.
  11. 11. how-come? Where are we? How did we get here? Where are we going?
  12. 12. Act I. commit c678127f72bc52ba992cd9eb8a8195473334daaa Author: Kjartan Mannes <kjartan@2.no-reply.drupal.org> Date: Fri May 19 11:20:58 2000 +0000 I got bored *G*
  13. 13. 2000
  14. 14. ./theme.inc
  15. 15. <? global $user; if (isset($user)) $cookie = explode(":", base64_decode($user)); if (isset($cookie[9])) include "themes/$cookie[9]/theme.class"; else include "themes/default/theme.class"; $theme = new Theme(); ?>
  16. 16. ./themes/jeroen/theme.class
  17. 17. <? class Theme { ### color set #1: var $bgcolor1 = "blue"; // background color var $fgcolor1 = "red"; // table body color var $hlcolor1 = "#AAAAAA"; // high-light color ### color set #2: var $bgcolor2 = "#EEEEEE"; var $fgcolor2 = "#666699"; var $hlcolor2 = "#666699"; ### color set #3: var $bgcolor3 = "#EEEEEE"; var $fgcolor3 = "yellow"; var $hlcolor3 = "yellow"; ###### # Syntax.......: header($title); # Description..: a function to draw the page header. function header($title) { ?> <HTML> <HEAD> <TITLE><? include "config.inc"; echo $sitename; ?></TITLE> <META NAME="description" CONTENT="drop.org"> <META NAME="keywords" CONTENT="drop, weblog, portal, community, news, article, announcements, stories, story, computer, science, space, hype, cult, geek, nerd, foo, bar"> </HEAD> <STYLE type="text/css"> <!-- BODY,TD,P,UL,LI,DIV,FORM,EM,BLOCKQUOTE { font-size: 8pt; font-family: verdana,helvetica, arial; } --> </STYLE> <BODY TEXT="#000000" BGCOLOR="#FFFFFF" ALINK="#CCCCCC" LINK="#444444" VLINK="#666666"> <TABLE BORDER="0" CELLPADDING="2" CELLSPACING="2">
  18. 18. commit e6998fd3225da8e1a3621168f3e55d78de0eb1b4 Author: Kjartan Mannes <kjartan@2.no-reply.drupal.org> Date: Fri May 19 23:33:15 2000 +0000 Wheeeeee... I think I finally figured this out :P
  19. 19. <?PHP # Natrak was here! # Dries was here! # Natra was here again! include "functions.inc"; //...
  20. 20. commit 3a62885aef7aa410288760c20a834dbdce260ca7 Author: Jeroen Bensch <jeroen@44.no-reply.drupal.org> Date: Mon May 22 10:05:10 2000 +0000 sigh commit a0cb7b5503ce0e798d4ad898fffab80173d66f81 Author: Jeroen Bensch <jeroen@44.no-reply.drupal.org> Date: Mon May 22 10:02:48 2000 +0000 testi-i-ing commit 4e042672186634e93d6fd0eba4514a9a8eac61f3 Author: Jeroen Bensch <jeroen@44.no-reply.drupal.org> Date: Mon May 22 10:01:22 2000 +0000 testing ... please
  21. 21. commit 65d0fcf50f370cc0ce59e620aa8183269e3be184 Author: Dries Buytaert <dries@buytaert.net> Date: Mon May 22 10:31:36 2000 +0000 como'n, work commit 2ce00a9d4548e94838ee276e698810c888e2e486 Author: Dries Buytaert <dries@buytaert.net> Date: Mon May 22 10:20:17 2000 +0000 fdasfdas commit d3d8c37d3c731a0f863929703df85f2ab88d1408 Author: Dries Buytaert <dries@buytaert.net> Date: Mon May 22 10:18:58 2000 +0000 testing
  22. 22. <? class Theme { ### color set #1: var $bgcolor1 = "blue"; // background color var $fgcolor1 = "red"; // table body color var $hlcolor1 = "#AAAAAA"; // high-light color ### color set #2: var $bgcolor2 = "#EEEEEE"; var $fgcolor2 = "#666699"; var $hlcolor2 = "#666699"; ### color set #3: var $bgcolor3 = "#EEEEEE"; var $fgcolor3 = "yellow"; var $hlcolor3 = "yellow"; ###### # Syntax.......: header($title); # Description..: a function to draw the page header. function header($title) { ?> <HTML> <HEAD> <TITLE><? include "config.inc"; echo $sitename; ?></TITLE> <META NAME="description" CONTENT="drop.org"> <META NAME="keywords" CONTENT="drop, weblog, portal, community, news, article, announcements, stories, story, computer, science, space, hype, cult, geek, nerd, foo, bar"> </HEAD> <STYLE type="text/css"> <!-- BODY,TD,P,UL,LI,DIV,FORM,EM,BLOCKQUOTE { font-size: 8pt; font-family: verdana,helvetica, arial; } --> </STYLE> <BODY TEXT="#000000" BGCOLOR="#FFFFFF" ALINK="#CCCCCC" LINK="#444444" VLINK="#666666"> <TABLE BORDER="0" CELLPADDING="2" CELLSPACING="2">
  23. 23. In Open Source, we tend to guess.:)
  24. 24. commit 85594eb9a1f9355152d0008eb2875cb7fc71221d Author: Jeroen Bensch <jeroen@44.no-reply.drupal.org> Date: Sun Jun 4 12:20:44 2000 +0000 like this?
  25. 25. FEATURE :) In 2000, there existed an overridable way of generating markup via PHP classes.
  26. 26. PROBLEM :( Too much in a Theme class. As Drupal grows, so must a Theme Class.
  27. 27. ./index.php
  28. 28. <?PHP $theme->header(); $theme->abstract($story); $theme->footer(); // Our theme class needs methods for **all** of these.
  29. 29. Act II. commit fc3d320d860acbe9e31a968b2ca09f4ffaf179d7 Author: Dries Buytaert <dries@buytaert.net> Date: Mon Jan 1 12:00:09 2001 +0000 - fixed a few typoes in the documentation
  30. 30. 2001
  31. 31. ./includes/theme.inc
  32. 32. <?PHP function theme_new_headlines($theme, $num = 10) { global $user; $content = ""; $result = db_query("SELECT id, subject FROM stories WHERE status = 2 ORDER BY id DESC LIMIT $num"); while ($story = db_fetch_object($result)) $content .= "<LI><A HREF="discussion. php?id=$story->id">". check_output($story->subject) ."</A></LI>n"; $content .= "<P ALIGN="right">[ <A HREF="search.php"><FONT COLOR="$theme- >hlcolor2">more</FONT></A> ]</P>"; $theme->box("Latest headlines", $content); }
  33. 33. <?php class BaseTheme { function links($links = array(), $status = 0, $node = 0) { if ($status == 1) $links = array_merge(theme_morelink($node), $links); foreach ($links as $link) { $_links[] = count($link) == 2 ? "<A HREF="$link[0]"><FONT COLOR="$theme- >link">". t($link[1]) ."</FONT></A>" : t($link[0]); } if ($status == 2) return ($_links ? implode(" | ", $_links) : ""); else return ($_links ? "[ ". implode(" | ", $_links) ." ]" : ""); } }
  34. 34. FEATURE :) We have default markup in core. Themes only override what they need to.
  35. 35. <?php /********************************************************************* Theme: Marvin Author: Dries Buytaert (Dries) Email: dries@drop.org Description: Classic theme, white, basic design with a fresh look. Notes: Only supports blocks on the right. *********************************************************************/ class Theme extends BaseTheme {
  36. 36. 2002
  37. 37. commit 08b82913d44a79720211cf252100cf316400e7cd Author: Dries Buytaert <dries@buytaert.net> Date: Sun Jun 23 13:31:30 2002 +0000 - Added the theme_invoke() function from Moshe's sandbox.
  38. 38. ./includes/theme.inc
  39. 39. <?php function theme_invoke() { global $theme; $args = func_get_args(); $function = array_shift($args); if (method_exists($theme, $function)) { return call_user_method_array($function, $theme, $args); } else { return call_user_func_array($function, $args); } }
  40. 40. PROBLEM :( Concatenating strings in PHP to write markup is not how people want to write markup.
  41. 41. Act III. commit 8b6d92fc69beb41d1c8d652c3f22143c375dc55d Author: Dries Buytaert <dries@buytaert.net> Date: Mon Jan 20 21:00:31 2003 +0000 - Added a template driven theme.
  42. 42. 2003
  43. 43. ./themes/xtemplate.theme
  44. 44. <?php class Theme_xtemplate extends BaseTheme { // ... function node($node, $main) { // ... $this->template->assign(array ( "title" => ucfirst($node->title), "taxonomy" => $this->links($terms), "author" => format_name($node), "date" => format_date($node->created), "content" => ($main && $node->teaser) ? check_output($node->teaser) : check_output($node->body))); if ($links = link_node($node, $main)) { $this->template->assign("links", $this->links($links)); } $this->template->parse("node"); print $this->template->text("node"); $this->template->reset("node"); }
  45. 45. ./themes/xtemplate.xtmpl
  46. 46. <!-- BEGIN: header --> <?xml version="1.0" encoding="iso-8859-1"?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3. org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en"> <head> <title>{name} - {slogan}</title> <style type="text/css" media="all"> @import "themes/xtemplate/xtemplate.css"; </style> </head> <body> <!-- END: header --> <!-- ... --> <!-- BEGIN: message --> <div id="message">{message}</div> <!-- END: message --> <!-- BEGIN: node --> <div id="node"> <div id="title">{title}</div> <span id="author">Submitted by {author} on {date}.</span> <span id="taxonomy">{taxonomy}</span> <div id="content">{content}</div> <div id="links">&raquo; {links}</div> </div> <!-- END: node -->
  47. 47. FEATURE :) We have template-based markup in core.
  48. 48. PROBLEM :( Xtemplate isn't very intuitive, dynamic, powerful, elegant ... or popular.
  49. 49. 2005
  50. 50. [drupal-devel] phptemplate in core? Dries Buytaert dries at buytaert.net Sun May 1 15:49:22 UTC 2005 Previous message: [drupal-devel] Talk with Rasmus Next message: [drupal-devel] phptemplate in core? Messages sorted by: [ date ] [ thread ] [ subject ] [ author ] Adrian et al, here is what I'd like to see happen: get PHPTemplate in core. We'd need to: 1. remove Xtemplate from core. 2. remove the Bluemarine theme from core (Xtemplate based). 3. remove the Pushbutton theme from core (Xtemplate based). 4. add PHPTemplate to core. 5. add a PHPTemplate-based theme to core. ...
  51. 51. [drupal-devel] phptemplate in core? Adrian Rossouw adrian at bryght.com Sun May 1 18:15:12 UTC 2005 Previous message: [drupal-devel] phptemplate in core? Next message: [drupal-devel] phptemplate in core? Messages sorted by: [ date ] [ thread ] [ subject ] [ author ] -----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 On 01 May 2005, at 5:49 PM, Dries Buytaert wrote: > - What needs to be done to get PHPTemplate in core? From what I can > tell, PHPTemplate's engine-specific settings (eg. the primary and > secondary links) need some work. My plan was to get this done this weekend, but unfortunately I didn't have the time for it this week. ...
  52. 52. commit e274f97c879bff059511472a4d14a3584941393e Author: Dries Buytaert <dries@buytaert.net> Date: Wed May 4 18:12:18 2005 +0000 - Removed the Xtemplate engine and added the PHPTemplate engine. - Converted the Bluemarine theme from XTemplate to PHPTemplate. - Moved the the Pushbutton theme and the Xtemplate engine to the contributions repository.
  53. 53. ./themes/engines/phptemplate/node.tpl.php
  54. 54. <div class="node<?php print ($sticky) ? " sticky" : ""; ?>"> <?php if ($page == 0): ?> <h2><a href="<?php print $node_url ?>" title="<?php print $title ?>"><?php print $title ?></a></h2> <?php endif; ?> <?php print $picture ?> <div class="info"><?php print $submitted ?><span class="terms"><?php print $terms ?></span></div> <div class="content"> <?php print $content ?> </div> <?php if ($links): ?> <?php if ($picture): ?> <br class='clear' /> <?php endif; ?> <div class="links"><?php print $links ?></div> <?php endif; ?> </div>
  55. 55. ./includes/theme.inc
  56. 56. <?php function theme() { global $theme, $theme_engine; if (!$theme) { // Initialize the enabled theme. $theme = init_theme(); } $args = func_get_args(); $function = array_shift($args); if (($theme != '') && function_exists($theme .'_'. $function)) { // call theme function return call_user_func_array($theme .'_'. $function, $args); } elseif (($theme != '') && isset($theme_engine) && function_exists($theme_engine .'_'. $function)) { // call engine function return call_user_func_array($theme_engine .'_'. $function, $args); } elseif (function_exists('theme_'. $function)){ // call Drupal function return call_user_func_array('theme_'. $function, $args); } }
  57. 57. ./themes/engines/phptemplate/phptemplate. engine
  58. 58. <?php function phptemplate_node($node, $main = 0, $page = 0) { //... $variables = array( 'title' => check_plain($node->title), 'node_url' => url('node/' . $node->nid), 'name' => format_name($node), 'date' => format_date($node->created), 'sticky' => $node->sticky, 'picture' => theme_get_setting('toggle_node_user_picture') ? theme ('user_picture', $node) : '', 'content' => ($main && $node->teaser) ? $node->teaser : $node->body, 'links' => $node->links ? theme('links', $node->links) : '', 'mission' => $mission, 'page' => $page, /* Lastly , pass the actual node to allow more customization */ 'node' => $node, 'main' => $main, 'page' => $page ); // Display info only on certain node types. if (theme_get_setting('toggle_node_info_' . $node->type)) { $variables['submitted'] = t('Submitted by %a on %b.', array('%a' => format_name ($node), '%b' => format_date($node->created))); } return _phptemplate_callback('node', $variables, 'node-' . $node->type); }
  59. 59. <?php function _phptemplate_callback($hook, $variables = array(), $file = null) { $variables = array_merge($variables, _phptemplate_default_variables($hook, $variables)); // Allow specified variables to be overridden if (function_exists('_phptemplate_variables')) { $variables = array_merge($variables, _phptemplate_variables($hook, $variables)); } if ($variables['template_file']) { $file = $variables['template_file']; } if (function_exists('_phptemplate_' . $hook)) { return call_user_func('_phptemplate_' . $hook, $variables, $file); } elseif (function_exists('_phptemplate_default')) { return call_user_func('_phptemplate_default', $hook, $variables, $file); } }
  60. 60. <?php function _phptemplate_default($hook, $variables, $file = null) { if ($file && file_exists(path_to_theme() . "/$file.tpl.php")) { $file = path_to_theme() . "/$file.tpl.php"; } else { if (file_exists(path_to_theme() . "/$hook.tpl.php")) { $file = path_to_theme() . "/$hook.tpl.php"; } else { if (in_array($hook, array('node', 'block', 'box', 'comment'))) { $file = "themes/engines/phptemplate/$hook.tpl.php"; } else { $variables['hook'] = $hook; watchdog('error', 'PHPTemplate was instructed to override the ' . $hook . ' theme function, but no valid template file was found.'); $file = "themes/engines/phptemplate/default.tpl.php"; } } } if ($file) { extract($variables); // Extract the vars to local namespace ob_start(); // Start output buffering include($file); // Include the file $contents = ob_get_contents(); // Get the contents of the buffer ob_end_clean(); // End buffering and discard return $contents; // Return the contents } }
  61. 61. FEATURES :) Powerful templates. Pluggable, atomic, extensible. Variable overrides. Engine/theme/core default markup. Wow!
  62. 62. PROBLEMS :( Tooling isn't well documented or understood, malperformant, flimsy APIs.
  63. 63. 2007
  64. 64. commit 5bbbf10ba84042b8576d67576d98922c0063c6d6 Author: Dries Buytaert <dries@buytaert.net> Date: Fri Apr 6 13:27:23 2007 +0000 - Patch #130987 by merlinofchaos: added theme registry for easier themability.
  65. 65. ./includes/theme.inc
  66. 66. <?php /** * ... * ENGINE_engine_variables(&$variables) * This function should only be implemented by theme engines and is exists * so that the theme engine can set necessary variables. It is commonly * used to set global variables such as $directory and $is_front_page. * ENGINE_engine_variables_HOOK(&$variables) * This is the same as the previous function, but is called only per hook. * ENGINE_variables_HOOK(&$variables) * ENGINE_variables(&$variables) * This is meant to be used by themes that utilize a theme engine; as it is * good practice for these themes to use the theme engine's name for * their functions so that they may share code. In PHPTemplate, these * functions will appear in template.php * THEME_variables_HOOK(&$variables) * THEME_variables(&$variables) * These functions are based upon the raw theme; they should primarily be * used by themes that do not use an engine or by themes that need small * changes to what has already been established in the theme engine version * of the function. * * There are two special variables that these hooks can set: * 'template_file' and 'template_files'. These will be merged together * to form a list of 'suggested' alternate template files to use, in * reverse order of priority. template_file will always be a higher * priority than items in template_files. theme() will then look for these * files, one at a time, and use the first one * that exists. * ... */ function theme() {
  67. 67. <?php /** * ... * ENGINE_engine_preprocess(&$variables) * This function should only be implemented by theme engines and is exists * so that the theme engine can set necessary variables. It is commonly * used to set global variables such as $directory and $is_front_page. * ENGINE_engine_preprocess_HOOK(&$variables) * This is the same as the previous function, but is called only per hook. * ENGINE_preprocess_HOOK(&$variables) * ENGINE_preprocess(&$variables) * This is meant to be used by themes that utilize a theme engine; as it is * good practice for these themes to use the theme engine's name for * their functions so that they may share code. In PHPTemplate, these * functions will appear in template.php * THEME_preprocess_HOOK(&$variables) * THEME_preprocess(&$variables) * These functions are based upon the raw theme; they should primarily be * used by themes that do not use an engine or by themes that need small * changes to what has already been established in the theme engine version * of the function. * template_preprocess(&$variables) * This function will only be called for theme functions registered by * the named module. In general it is preferred to use the following * function if possible, but it may not always be the case. * template_preprocess_HOOK(&$variables) * This is the same as the previous function, but is called only per hook. * * ... */ function theme() {
  68. 68. FEATURES :) More variable overrides, template registry overrides.
  69. 69. PROBLEMS??
  70. 70. Act IV. commit 2e8ca690ff471b1d6604226e8153f401b1827204 Author: Dries Buytaert <dries@buytaert.net> Date: Tue Jan 27 00:22:27 2009 +0000 - Patch #351235 by dmitrig01, webchick, frando, moshe weitzman, et al: hook_page_alter. Oh, behave.
  71. 71. 2005
  72. 72. commit 7e1527ee61bc10b3765b95b9af8faaa2254da5a8 Author: Dries Buytaert <dries@buytaert.net> Date: Fri Oct 7 06:11:12 2005 +0000 - Patch #29465: new form API by Adrian et al.
  73. 73. ALTER ALL OF THE THINGS ALTER ALL OF THE THINGS
  74. 74. 2007
  75. 75. <?php /** * Implementation of hook_elements(). */ function system_elements() { // Top level form $type['form'] = array( '#method' => 'post', '#action' => request_uri(), ); $type['page'] = array( '#show_messages' => TRUE, '#show_blocks' => TRUE, '#theme' => 'page', ); $type['list'] = array( '#title' => '', '#list_type' => 'ul', '#attributes' => array(), '#items' => array(), ); // ...
  76. 76. FEATURES :) Form structure is abstracted for better module extensibility.
  77. 77. <?php /** * Implementation of hook_elements(). */ function system_elements() { // Top level form $type['form'] = array( '#method' => 'post', '#action' => request_uri(), ); $type['page'] = array( '#show_messages' => TRUE, '#show_blocks' => TRUE, '#theme' => 'page', ); $type['list'] = array( '#title' => '', '#list_type' => 'ul', '#attributes' => array(), '#items' => array(), ); // ...
  78. 78. 2.0
  79. 79. http://drupal.org/node/134478#comment-538674 Posted by bjaspan on October 15, 2007 at 1:39pm "menu callbacks ought to return structured data, not rendered data"
  80. 80. <?php // OLD return theme('foo', $bar); <?php // NEW return array( '#theme' => 'foo', '#bar' => $bar, );
  81. 81. FEATURES :) Page structure is abstracted for better extensibility.
  82. 82. PROBLEMS :( Everything just changed.
  83. 83. 2009
  84. 84. hook_page_alter() template_preprocess_foo() template_preprocess() template_process_foo() template_process() Templates can be overridden... Preprocessors can be overridden... Processors can be overridden... Theme functions still exist...
  85. 85. PROBLEMS PROBLEMS PROBLEMS PROBLEMS PROBLEMS PROBLEMS PROBLEMS PROBLEMS PROBLEMS PROBLEMS
  86. 86. let's fix the problems
  87. 87. Act V. commit ee2acd68cca6ecfb2a1f3436c6adb593659489a8 Author: Dries <dries@buytaert.net> Date: Sat Nov 3 10:36:10 2012 -0700 Issue #1696786 by Fabianx, jenlampton, stevector, steveoliver, jwilson3, amateescu, chx: Integrate Twig into core: Implementation issue.
  88. 88. RAD
  89. 89. this was a lot of work
  90. 90. the future? http://drupal.org/node/1899454
  91. 91. Rewriting the Drupal theme layer Carl Wiedemann DrupalCamp Colorado • 2013-06-29

×