Index: devel.module
===================================================================
RCS file: /cvs/drupal/contributions/modules/devel/devel.module,v
retrieving revision 1.187
diff -u -p -r1.187 devel.module
--- devel.module 16 Aug 2007 12:57:48 -0000 1.187
+++ devel.module 16 Aug 2007 22:56:37 -0000
@@ -1,5 +1,5 @@
$data) {
- // Add in devel_preprocess so it's used as the last variable preprocessor.
- // This way, it can gather *all* template suggestions, not just the ones set by modules.
- if (!isset($data['function']) && !in_array('devel_preprocess', $data['preprocess functions'])) {
- $theme_registry[$hook]['preprocess functions'][] = 'devel_preprocess';
- }
- elseif (isset($data['function'])) {
- // For intercepting theme functions not connected to template files.
- // Copy over original registry of the hook so it can be caught later.
- $theme_registry[$hook]['devel'] = $theme_registry[$hook];
- // Replace the defaults to be intercepted.
- $theme_registry[$hook]['function'] = 'devel_catch_theme_function';
- $theme_registry[$hook]['type'] = 'module';
- $theme_registry[$hook]['theme path'] = drupal_get_path('module', 'devel');
- }
+ // Supply alternate hook (theme function) free of previous data.
+ $theme_registry_alt[$hook]['function'] = 'devel_catch_theme_function';
+ $theme_registry_alt[$hook]['type'] = 'module';
+ $theme_registry_alt[$hook]['theme path'] = drupal_get_path('module', 'devel');
+ // Copy original hook data and set hook for easy access to be used in devel_catch_theme_function().
+ $theme_registry_alt[$hook]['devel'] = $theme_registry[$hook];
+ $theme_registry_alt[$hook]['devel']['hook'] = $hook;
}
- _theme_save_registry($GLOBALS['theme_info'], $theme_registry);
+ // TODO: This still doesn't work.
+ //_theme_save_registry($GLOBALS['theme_info'], $theme_registry);
+ cache_set("theme_registry:garland", $theme_registry_alt); // Hard coded just to test.
+
}
/**
- * Show all theme templates that could have been used on this page.
- * TODO: highlight the one that was actually used
+ * Process and display all theme data used on a page.
**/
function devel_template_log() {
if (variable_get('dev_template_log', 0)) {
- $extension = devel_get_theme_extension();
- $header = array('Template name', "Template files ($extension)");
if (isset($GLOBALS['devel_theme_functions'])) {
- foreach ($GLOBALS['devel_theme_functions'] as $counter => $function) {
- // TODO: path_to_theme() is not right in hook_footer()
- // TODO: drupal_discover_template() needs leading './' so as to avoid lookup in whole include pat
- // $used = drupal_discover_template($function['template_files'], $extension);
- // array_push($function['template_files'], $function['function']);
- // $key = array_search($used, $function['template_files']);
- // $function['template_files'][$key] = theme('placeholder', $function['template_files'][$key]);
- $id = "devel_template_log_link_$counter";
- $marker = "
\n";
- $rows[] = array($marker. $function['function'], implode(', ', $function['template_files']));
- // unset($function['template_files']);
- }
- return theme('table', $header, $rows);
- }
- }
-}
+
+ $version = drupal_major_version_map(VERSION);
+ $api = variable_get('devel_api_url', 'api.drupal.org');
+ static $hook_count = array();
+
+ foreach ($GLOBALS['devel_theme_functions'] as $hook_count => $data) {
+
+ $hook = $data['hook'];
+
+ $function = l('api', "http://$api/api/$version/function/". $data['function']);
+ $function .= ' | '. $data['function'] .'';
+
+ foreach ($data['wildcards'] as $wildcard => $state) {
+ $wildcards[$hook][] = $state ? ''. $wildcard .'(), ' : $wildcard .'(), ';
+ }
+ foreach ($data['template_files'] as $template => $state) {
+ // TODO: $template uses the full path if it was suggested from a module.
+ // !! path_to_theme() bug.!!
+ $templates[$hook][] = $data['path'] .'/'. ($state ? "$template" : "$template");
+ }
+ // If the hook has a template, then it has these..
+ if (isset($templates[$hook])) {
+ $variables[$hook] = isset($variables[$hook]) ? array_merge($variables[$hook], $data['variables']) : $data['variables'];
+ foreach ($data['preprocess functions'] as $pre_func) {
+ $linked_preprocess[] =
+ l('api', "http://$api/api/function/$pre_func/$version") .' | '.
+ ''. $pre_func .'';
+ }
+ $preprocess[$hook] = isset($preprocess[$hook]) ? array_merge($preprocess[$hook], $linked_preprocess) : $linked_preprocess;
+ }
-// would be nice if theme() broke this into separate function so we don't copy logic here. this one is better - has cache
-function devel_get_theme_extension() {
- global $theme_engine;
- static $extension = NULL;
-
- if (!$extension) {
- $extension_function = $theme_engine .'_extension';
- if (function_exists($extension_function)) {
- $extension = $extension_function();
- }
- else {
- $extension = '.tpl.php';
+ // Set header for hook group. This is only shown once per hook.
+ static $this_hook = NULL;
+ if ($this_hook != $hook) {
+ $run_count = devel_counter($hook, TRUE);
+ $rows[$hook .'_head'][1] = array(
+ 'data' => $function .' '. format_plural($run_count, '1 run', '!count runs', array('!count' => $run_count )) .'',
+ 'class' => 'devel_function_head',
+ );
+ $rows[$hook .'_head'][2] = array(
+ 'data' => $data['function_type'],
+ 'class' => 'devel_function_head',
+ );
+ $rows[$hook .'_head'][3] = array(
+ 'data' => $data['path'],
+ 'class' => 'devel_function_head',
+ );
+ $this_hook = $hook;
+ }
+
+ // We need to run these last. Check by comparing counters.
+ if (isset($wildcards[$hook]) && devel_counter($hook, TRUE) == $data['counter']) {
+ $rows[$hook .'_wildcards'][] = array(
+ 'data' => "
+
+ - Wild cards:
+ - $wildcards
+
+ "
+ ,
+ 'colspan' => '3',
+ );
+ }
+ if (isset($templates[$hook]) && devel_counter($hook, TRUE) == $data['counter']) {
+ $unique_templates = array_unique($templates[$hook]);
+ $unique_preprocess = array_unique($preprocess[$hook]);
+ $unique_variables = array_unique($variables[$hook]);
+ $rows[$hook .'_templates'][] = array(
+ 'data' => "
+
+ - Templates:
+ - ". implode('
', $unique_templates) ."
+
+
+ - Variable Functions:
+ - ". implode('
', $unique_preprocess) ."
+
+
+ - Variables:
+ - $". implode(', $', $unique_variables) ."
+
+ ",
+ 'colspan' => '3',
+ );
+ }
+ }
+
+ $header = array('Theme function', 'Source type', 'Source path');
+ if (!isset($rows)) {
+ $rows = array();
+ }
+
+ return theme('table', $header, $rows, array('class' => 'devel-theme-function-list'));
}
}
- return $extension;
}
/**
- * This function gets injected into the registry in devel_exit(). It logs theme template calls.
-*/
-function devel_preprocess($vars, $function) {
- $counter = devel_counter();
- $GLOBALS['devel_theme_functions'][$counter] = array(
- 'function' => $function,
- 'template_files' => $vars['template_files'],
- );
-}
-
-/**
- * Intercepts theme *functions*, adds to template log, and dispatches to original theme function.
+ * Intercepts all theme functions, adds to template log, and dispatches to original theme function.
* This function gets injected into theme registry in devel_exit().
*/
function devel_catch_theme_function() {
- static $i=0;
- $args = func_get_args();
// Get the function that is normally called.
$trace = debug_backtrace();
- $call_theme_func = $trace[2]['args'][0];
+ $hook = $trace[2]['args'][0];
- // Get registry for the original function data.
- $theme_registry = theme_get_registry();
- $hook_registry_data = $theme_registry[$call_theme_func]['devel'];
+ // The twin of theme().
+ $args = func_get_args();
+ $theme_data = devel_theme_evil_twin($hook, $args);
- // Include a file if this theme function is held elsewhere. Partial copy of theme().
- if (!empty($hook_registry_data['file'])) {
- $function_file = $hook_registry_data['file'];
- if (isset($hook_registry_data['path'])) {
- $function_file = $hook_registry_data['path'] .'/'. $function_file;
- }
- include_once($function_file);
- }
+ // Bail if there's not output. Prevents un-needed markup.
+ if ($theme_data['output']) {
+ // Add counter
+ $counter = devel_counter($hook);
+ $theme_data['meta']['counter'] = $counter;
+ $theme_data['meta']['hook'] = $hook;
- // log the call
- $counter = devel_counter();
- $GLOBALS['devel_theme_functions'][$counter] = array(
- 'function' => $hook_registry_data['function'],
- 'template_files' => array(),
- );
+ // $theme data['meta'] is keyed with the following:
+ // wildcards, path, function, function_type, extension, preprocess functions, template_files, variables (keys), output plus the above.
+ $GLOBALS['devel_theme_functions'][$hook .'_'. $counter] = $theme_data['meta'];
- return devel_template_marker($counter). call_user_func_array($hook_registry_data['function'], $args);
+ return devel_template_marker($counter, $theme_data['meta']) . $theme_data['output'];
+ }
}
-function devel_template_marker($counter) {
- $id = "devel_template_log_call_". $counter;
- return "\n";
+function devel_template_marker($counter, $meta) {
+ $id = $meta['function'] .'-'. $counter;
+ $class = 'devel-function-log-call';
+ $class .= ' function-'. $meta['function'];
+ if (isset($meta['template_files'])) {
+ $class .= ' '. implode(' ', $meta['preprocess functions']);
+ $class .= ' '. $meta['template_files'][array_search(TRUE, $meta['template_files'])];
+ }
+ return "\n";
+}
+
+// hand out counter per hook.
+function devel_counter($hook, $get = FALSE) {
+ static $counter = array();
+ if (!isset($counter[$hook])) {
+ $counter[$hook] = 0;
+ }
+ if (!$get) {
+ $counter[$hook]++;
+ }
+ return $counter[$hook];
}
-// just hand out next counter
-function devel_counter() {
- static $counter = 0;
- $counter++;
- return $counter;
+/**
+ * Copied from theme() with slight modifications to gather data about the
+ * function as it is being run. Is this a little overboard?
+ */
+function devel_theme_evil_twin($hook, $args) {;
+
+ // Get registry for the original function data.
+ $hooks = theme_get_registry();
+ $info = $hooks[$hook]['devel'];
+
+ // Gather all possible wildcard functions.
+ $meta['wildcards'] = array();
+ if (is_array($hook)) {
+ foreach ($hook as $candidate) {
+ $mets['wildcards'][$candidate] = FALSE;
+ if (isset($info['hook'][$candidate])) {
+ $mets['wildcards'][$candidate] = TRUE;
+ break;
+ }
+ }
+ $hook = $candidate;
+ }
+
+ // We can add something here. Maybe a notice or rebuild the registry?
+ if ($info['hook'] != $hook) {
+ return;
+ }
+
+ global $theme_path;
+ // point path_to_theme() to the currently used theme path:
+ $theme_path = $info['theme path'];
+ $meta['path'] = $theme_path;
+
+ if (isset($info['function'])) {
+ // The theme call is a function.
+ $meta['function'] = $info['function'];
+ $meta['function_type'] = 'function';
+ // Include a file if this theme function is held elsewhere.
+ if (!empty($info['file'])) {
+ $function_file = $info['file'];
+ if (isset($info['path'])) {
+ $function_file = $info['path'] .'/'. $function_file;
+ }
+ include_once($function_file);
+ }
+ $output = call_user_func_array($info['function'], $args);
+ }
+ else {
+ // The theme call is a template.
+ $meta['function'] = 'theme_'. $hook;
+ $meta['function_type'] = 'template';
+ $variables = array(
+ 'template_files' => array()
+ );
+ if (!empty($info['arguments'])) {
+ $count = 0;
+ foreach ($info['arguments'] as $name => $default) {
+ $variables[$name] = isset($args[$count]) ? $args[$count] : $default;
+ $count++;
+ }
+ }
+
+ // default render function and extension.
+ $render_function = 'theme_render_template';
+ $extension = '.tpl.php';
+
+ // Run through the theme engine variables, if necessary
+ global $theme_engine;
+ if (isset($theme_engine)) {
+ // If theme or theme engine is implementing this, it may have
+ // a different extension and a different renderer.
+ if ($info['type'] != 'module') {
+ if (function_exists($theme_engine .'_render_template')) {
+ $render_function = $theme_engine .'_render_template';
+ }
+ $extension_function = $theme_engine .'_extension';
+ if (function_exists($extension_function)) {
+ $extension = $extension_function();
+ }
+ }
+ }
+ $meta['extension'] = $extension;
+
+ if (isset($info['preprocess functions']) && is_array($info['preprocess functions'])) {
+ // This construct ensures that we can keep a reference through
+ // call_user_func_array.
+ $args = array(&$variables, $hook);
+ foreach ($info['preprocess functions'] as $preprocess_function) {
+ if (function_exists($preprocess_function)) {
+ call_user_func_array($preprocess_function, $args);
+ }
+ }
+ $meta['preprocess functions'] = $info['preprocess functions'];
+ }
+
+ // Get suggestions for alternate templates out of the variables
+ // that were set. This lets us dynamically choose a template
+ // from a list. The order is FILO, so this array is ordered from
+ // least appropriate first to most appropriate last.
+ $suggestions = array();
+
+ if (isset($variables['template_files'])) {
+ $suggestions = $variables['template_files'];
+ }
+ if (isset($variables['template_file'])) {
+ $suggestions[] = $variables['template_file'];
+ }
+
+ if ($suggestions) {
+ $template_file = drupal_discover_template($suggestions, $extension);
+ foreach ($suggestions as $candidate) {
+ $meta['template_files'][$candidate . $extension] = FALSE;
+ if ($candidate . $extension == $template_file) {
+ $meta['template_files'][$candidate . $extension] = TRUE;
+ }
+ }
+ }
+
+ if (empty($template_file)) {
+ $template_file = $info['file'] . $extension;
+ $meta['template_files'][$template_file] = TRUE;
+ if (isset($info['path'])) {
+ $template_file = $info['path'] .'/'. $template_file;
+ }
+ }
+ $output = $render_function($template_file, $variables);
+
+ $meta['variables'] = array_keys($variables);
+ }
+
+ return array('output' => $output, 'meta' => $meta);
}
// Menu callback. I would love prettier hierarchy browser for this.
Index: devel.css
===================================================================
RCS file: /cvs/drupal/contributions/modules/devel/devel.css,v
retrieving revision 1.3
diff -u -p -r1.3 devel.css
--- devel.css 16 Aug 2007 05:49:58 -0000 1.3
+++ devel.css 16 Aug 2007 23:34:45 -0000
@@ -2,6 +2,22 @@
padding-top: 2em;
}
-.devel_template_log_call, .devel_template_log_link {
- display: none;
+.devel-template-log-call {
+ font-size: .01em;
+ visibility: hidden;
+}
+
+.devel-theme-function-list {
+ font-size: 1em;
+}
+
+.devel-templates, .devel-preprocessors {
+ width: 45%;
+ float: left;
+}
+.devel-variables {
+ float: left;
+}
+.devel-theme-function-list .function-run {
+ font-size: .85em;
}
\ No newline at end of file