#1022418. Improve a.d.o-compatible documentation for hook_node_access_explain() and...
[project/devel.git] / devel.module
CommitLineData
22c2656f 1<?php
8a6faf5a 2// $Id$
22c2656f 3
9d450e49 4// This module holds functions useful for Drupal development.
22c2656f 5// Please contribute!
6
0f79f53f 7// Suggested profiling and stacktrace library from http://www.xdebug.org/index.php
defc4865 8
c01a051a 9define('DEVEL_QUERY_SORT_BY_SOURCE', 0);
10define('DEVEL_QUERY_SORT_BY_DURATION', 1);
dcd35dd9 11
1c411384 12define('DEVEL_ERROR_HANDLER_NONE', 0);
13define('DEVEL_ERROR_HANDLER_STANDARD', 1);
14define('DEVEL_ERROR_HANDLER_BACKTRACE', 2);
15
eb8f82fc 16define('DEVEL_MIN_TEXTAREA', 50);
17
1e7a3874 18/**
9d450e49 19 * Implementation of hook_help().
1e7a3874 20 */
9d450e49 21function devel_help($section) {
22 switch ($section) {
24b0f0d8 23 case 'devel/reference':
7a2bcf56 24 return '<p>'. t('This is a list of defined user functions that generated this current request lifecycle. Click on a function name to view its documention.') .'</p>';
24b0f0d8 25 case 'devel/session':
7a2bcf56 26 return '<p>'. t('Here are the contents of your <code>$_SESSION</code> variable.') .'</p>';
24b0f0d8 27 case 'devel/variable':
2ad63d14 28 $api = variable_get('devel_api_url', 'api.drupal.org');
7a2bcf56 29 return '<p>'. t('This is a list of the variables and their values currently stored in variables table and the <code>$conf</code> array of your settings.php file. These variables are usually accessed with <a href="@variable-get-doc">variable_get()</a> and <a href="@variable-set-doc">variable_set()</a>. Variables that are too long can slow down your pages.', array('@variable-get-doc' => "http://$api/api/HEAD/function/variable_get", '@variable-set-doc' => "http://$api/api/HEAD/function/variable_set")) .'</p>';
d17db88e 30 case 'devel/reinstall':
31 return t('Warning - will delete your module tables and variables.');
fc909860 32 }
defc4865 33}
34
1e7a3874 35/**
c7414be0 36 * Implementationation of hook_menu().
1e7a3874 37 */
b50db35f 38function devel_menu() {
1e7a3874 39 $items = array();
049a7540 40 // Note: we can't dynamically append destination to querystring. Do so at theme layer. Fix in D7?
b50db35f 41 $items['devel/cache/clear'] = array(
a30583d1 42 'title' => 'Empty cache',
b50db35f 43 'page callback' => 'devel_cache_clear',
7ce30870 44 'description' => 'Clear the CSS cache and all database cache tables which store page, node, theme and variable caches.',
b50db35f 45 'access arguments' => array('access devel information'),
61b651a0 46 'file' => 'devel.pages.inc',
7ce30870 47 'menu_name' => 'devel',
b50db35f 48 );
83013f8f 49
b50db35f 50 $items['devel/reference'] = array(
612cee0e 51 'title' => 'Function reference',
52 'description' => 'View a list of currently defined user functions with documentation links.',
b50db35f 53 'page callback' => 'devel_function_reference',
54 'access arguments' => array('access devel information'),
61b651a0 55 'file' => 'devel.pages.inc',
612cee0e 56 'menu_name' => 'devel',
b50db35f 57 );
58 $items['devel/reinstall'] = array(
a30583d1 59 'title' => 'Reinstall modules',
5b73e5e4 60 'page callback' => 'drupal_get_form',
61 'page arguments' => array('devel_reinstall'),
612cee0e 62 'description' => 'Run hook_uninstall() and then hook_install() for a given module.',
b50db35f 63 'access arguments' => array('access devel information'),
61b651a0 64 'file' => 'devel.pages.inc',
612cee0e 65 'menu_name' => 'devel',
b50db35f 66 );
c714f2b3 67 $items['devel/menu/reset'] = array(
fdea4254 68 'title' => 'Rebuild menus',
612cee0e 69 'description' => 'Rebuild menu based on hook_menu() and revert any custom changes. All menu items return to their default settings.',
c714f2b3 70 'page callback' => 'drupal_get_form',
fdea4254 71 'page arguments' => array('devel_menu_rebuild'),
c714f2b3 72 'access arguments' => array('access devel information'),
61b651a0 73 'file' => 'devel.pages.inc',
612cee0e 74 'menu_name' => 'devel',
c714f2b3 75 );
f9624262 76 $items['devel/menu/item'] = array(
77 'title' => 'Menu item',
78 'description' => 'Details about a given menu item.',
79 'page callback' => 'devel_menu_item',
80 'access arguments' => array('access devel information'),
61b651a0 81 'file' => 'devel.pages.inc',
f9624262 82 'menu_name' => 'devel',
83 );
b50db35f 84 $items['devel/variable'] = array(
a30583d1 85 'title' => 'Variable editor',
612cee0e 86 'description' => 'Edit and delete site variables.',
b50db35f 87 'page callback' => 'devel_variable_page',
88 'access arguments' => array('access devel information'),
61b651a0 89 'file' => 'devel.pages.inc',
612cee0e 90 'menu_name' => 'devel',
b50db35f 91 );
2ad63d14 92 // we don't want the abbreviated version provided by status report
93 $items['devel/phpinfo'] = array(
a30583d1 94 'title' => 'PHPinfo()',
612cee0e 95 'description' => 'View your server\'s PHP configuration',
2ad63d14 96 'page callback' => 'devel_phpinfo',
97 'access arguments' => array('access devel information'),
61b651a0 98 'file' => 'devel.pages.inc',
612cee0e 99 'menu_name' => 'devel',
c7a47f27 100 );
101 $items['devel/php'] = array(
102 'title' => 'Execute PHP Code',
103 'description' => 'Execute some PHP code',
104 'page callback' => 'drupal_get_form',
105 'page arguments' => array('devel_execute_form'),
106 'access arguments' => array('execute php code'),
61b651a0 107 'file' => 'devel.pages.inc',
c7a47f27 108 'menu_name' => 'devel',
2ad63d14 109 );
110 $items['devel/theme/registry'] = array(
a30583d1 111 'title' => 'Theme registry',
683a91e6 112 'description' => 'View a list of available theme functions across the whole site.',
2ad63d14 113 'page callback' => 'devel_theme_registry',
114 'access arguments' => array('access devel information'),
61b651a0 115 'file' => 'devel.pages.inc',
612cee0e 116 'menu_name' => 'devel',
2ad63d14 117 );
76a629dd 118 $items['devel/entity/info'] = array(
119 'title' => 'Entity info',
120 'description' => 'View entity information across the whole site.',
121 'page callback' => 'devel_entity_info_page',
122 'access arguments' => array('access devel information'),
123 'file' => 'devel.pages.inc',
124 'menu_name' => 'devel',
125 );
b5e3b6c4 126 $items['devel/field/info'] = array(
127 'title' => 'Field info',
128 'description' => 'View fields information across the whole site.',
71f1ca71 129 'page callback' => 'devel_field_info_page',
b5e3b6c4 130 'access arguments' => array('access devel information'),
61b651a0 131 'file' => 'devel.pages.inc',
b5e3b6c4 132 'menu_name' => 'devel',
133 );
fc524554 134 $items['devel/elements'] = array(
612cee0e 135 'title' => 'Hook_elements()',
136 'description' => 'View the active form/render elements for this site.',
fc524554 137 'page callback' => 'devel_elements_page',
138 'access arguments' => array('access devel information'),
61b651a0 139 'file' => 'devel.pages.inc',
612cee0e 140 'menu_name' => 'devel',
fc524554 141 );
a30583d1 142 $items['devel/variable/edit/%'] = array(
143 'title' => 'Variable editor',
b50db35f 144 'page callback' => 'drupal_get_form',
a30583d1 145 'page arguments' => array('devel_variable_edit', 3),
b50db35f 146 'access arguments' => array('access devel information'),
147 'type' => MENU_CALLBACK,
61b651a0 148 'file' => 'devel.pages.inc',
612cee0e 149 'menu_name' => 'devel',
b50db35f 150 );
151 $items['devel/session'] = array(
a30583d1 152 'title' => 'Session viewer',
612cee0e 153 'description' => 'List the contents of $_SESSION.',
b50db35f 154 'page callback' => 'devel_session',
155 'access arguments' => array('access devel information'),
61b651a0 156 'file' => 'devel.pages.inc',
612cee0e 157 'menu_name' => 'devel',
b50db35f 158 );
159 $items['devel/switch'] = array(
a30583d1 160 'title' => 'Switch user',
b50db35f 161 'page callback' => 'devel_switch_user',
162 'access arguments' => array('switch users'),
163 'type' => MENU_CALLBACK,
61b651a0 164 'file' => 'devel.pages.inc',
612cee0e 165 'menu_name' => 'devel',
b50db35f 166 );
2b11d164 167 $items['devel/explain'] = array(
168 'title' => 'Explain query',
169 'page callback' => 'devel_querylog_explain',
170 'description' => 'Run an EXPLAIN on a given query. Used by query log',
171 'access arguments' => array('access devel information'),
61b651a0 172 'file' => 'devel.pages.inc',
2b11d164 173 'type' => MENU_CALLBACK
174 );
175 $items['devel/arguments'] = array(
176 'title' => 'Arguments query',
177 'page callback' => 'devel_querylog_arguments',
c22345ef 178 'description' => 'Return a given query, with arguments instead of placeholders. Used by query log',
2b11d164 179 'access arguments' => array('access devel information'),
61b651a0 180 'file' => 'devel.pages.inc',
2b11d164 181 'type' => MENU_CALLBACK
182 );
1705c3ee
DR
183 $items['devel/run-cron'] = array(
184 'title' => 'Run cron',
185 'page callback' => 'system_run_cron',
186 'access arguments' => array('administer site configuration'),
187 'file' => 'system.admin.inc',
188 'file path' => drupal_get_path('module', 'system'),
189 'menu_name' => 'devel',
190 );
97db0981 191
192 // Duplicate path in 2 different menus. See http://drupal.org/node/601788.
193 $items['devel/settings'] = array(
612cee0e 194 'title' => 'Devel settings',
37e4c4f5 195 'description' => 'Helper functions, pages, and blocks to assist Drupal developers. The devel blocks can be managed via the <a href="' . url('admin/structure/block') . '">block administration</a> page.',
b50db35f 196 'page callback' => 'drupal_get_form',
197 'page arguments' => array('devel_admin_settings'),
198 'access arguments' => array('administer site configuration'),
61b651a0 199 'file' => 'devel.admin.inc',
612cee0e 200 'menu_name' => 'devel',
b50db35f 201 );
97db0981 202 $items['admin/config/development/devel'] = array(
203 'title' => 'Devel settings',
204 'description' => 'Helper functions, pages, and blocks to assist Drupal developers. The devel blocks can be managed via the <a href="' . url('admin/structure/block') . '">block administration</a> page.',
205 'page callback' => 'drupal_get_form',
206 'page arguments' => array('devel_admin_settings'),
61b651a0 207 'file' => 'devel.admin.inc',
97db0981 208 'access arguments' => array('administer site configuration'),
209 );
210
548c199e 211 $items['node/%node/devel'] = array(
212 'title' => 'Devel',
b50db35f 213 'page callback' => 'devel_load_object',
379b8e8c 214 'page arguments' => array('node', 1),
b50db35f 215 'access arguments' => array('access devel information'),
216 'type' => MENU_LOCAL_TASK,
410b980f 217 'file' => 'devel.pages.inc',
fd3d06ac 218 'weight' => 100,
b50db35f 219 );
548c199e 220 $items['node/%node/devel/load'] = array(
221 'title' => 'Load',
548c199e 222 'type' => MENU_DEFAULT_LOCAL_TASK,
223 );
f5be6a5f 224 $items['node/%node/devel/render'] = array(
548c199e 225 'title' => 'Render',
16ee1727 226 'page callback' => 'devel_render_object',
227 'page arguments' => array('node', 1),
b50db35f 228 'access arguments' => array('access devel information'),
410b980f 229 'file' => 'devel.pages.inc',
b50db35f 230 'type' => MENU_LOCAL_TASK,
fd3d06ac 231 'weight' => 100,
b50db35f 232 );
548c199e 233 $items['comment/%comment/devel'] = array(
234 'title' => 'Devel',
2fa92e41 235 'page callback' => 'devel_load_object',
379b8e8c 236 'page arguments' => array('comment', 1),
2fa92e41 237 'access arguments' => array('access devel information'),
238 'type' => MENU_LOCAL_TASK,
410b980f 239 'file' => 'devel.pages.inc',
2fa92e41 240 'weight' => 100,
241 );
548c199e 242 $items['comment/%comment/devel/load'] = array(
243 'title' => 'Load',
548c199e 244 'type' => MENU_DEFAULT_LOCAL_TASK,
245 );
2fa92e41 246 $items['comment/%comment/devel/render'] = array(
548c199e 247 'title' => 'Render',
2fa92e41 248 'page callback' => 'devel_render_object',
249 'page arguments' => array('comment', 1),
2fa92e41 250 'access arguments' => array('access devel information'),
251 'type' => MENU_LOCAL_TASK,
410b980f 252 'file' => 'devel.pages.inc',
2fa92e41 253 'weight' => 100,
254 );
548c199e 255 $items['user/%user/devel'] = array(
256 'title' => 'Devel',
b50db35f 257 'page callback' => 'devel_load_object',
379b8e8c 258 'page arguments' => array('user', 1),
b50db35f 259 'access arguments' => array('access devel information'),
260 'type' => MENU_LOCAL_TASK,
410b980f 261 'file' => 'devel.pages.inc',
fd3d06ac 262 'weight' => 100,
b50db35f 263 );
548c199e 264 $items['user/%user/devel/load'] = array(
265 'title' => 'Load',
548c199e 266 'type' => MENU_DEFAULT_LOCAL_TASK,
267 );
f5be6a5f 268 $items['user/%user/devel/render'] = array(
548c199e 269 'title' => 'Render',
16ee1727 270 'page callback' => 'devel_render_object',
271 'page arguments' => array('user', 1),
16ee1727 272 'access arguments' => array('access devel information'),
410b980f 273 'file' => 'devel.pages.inc',
16ee1727 274 'type' => MENU_LOCAL_TASK,
fd3d06ac 275 'weight' => 100,
16ee1727 276 );
548c199e 277 $items['taxonomy/term/%taxonomy_term/devel'] = array(
278 'title' => 'Devel',
42f270f9 279 'page callback' => 'devel_load_object',
379b8e8c 280 'page arguments' => array('taxonomy_term', 2, 'term'),
42f270f9 281 'access arguments' => array('access devel information'),
410b980f 282 'file' => 'devel.pages.inc',
42f270f9 283 'type' => MENU_LOCAL_TASK,
284 'weight' => 100,
285 );
548c199e 286 $items['taxonomy/term/%taxonomy_term/devel/load'] = array(
287 'title' => 'Load',
548c199e 288 'type' => MENU_DEFAULT_LOCAL_TASK,
289 );
42f270f9 290 $items['taxonomy/term/%taxonomy_term/devel/render'] = array(
548c199e 291 'title' => 'Render',
379b8e8c 292 'page callback' => 'devel_render_object',
293 'page arguments' => array('taxonomy_term', 2, 'term'),
42f270f9 294 'access arguments' => array('access devel information'),
295 'type' => MENU_LOCAL_TASK,
410b980f 296 'file' => 'devel.pages.inc',
42f270f9 297 'weight' => 100,
298 );
8b832e7b 299
1e7a3874
JC
300 return $items;
301}
302
4374b9c7 303/**
304 * Implements hook_admin_paths().
305 */
306function devel_admin_paths() {
307 $paths = array(
308 'devel/*' => TRUE,
309 'node/*/devel' => TRUE,
310 'node/*/devel/*' => TRUE,
311 'comment/*/devel' => TRUE,
312 'comment/*/devel/*' => TRUE,
313 'user/*/devel' => TRUE,
314 'user/*/devel/*' => TRUE,
315 'taxonomy/term/*/devel' => TRUE,
316 'taxonomy/term/*/devel/*' => TRUE,
317 );
318 return $paths;
319}
320
7ce30870 321function devel_menu_need_destination() {
de0da612 322 return array('devel/cache/clear', 'devel/reinstall', 'devel/menu/reset', 'devel/variable', 'admin/reports/status/run-cron');
7ce30870 323}
324
325/**
326 * An implementation of hook_menu_link_alter(). Flag this link as needing alter at display time.
256ca922 327 * This is more robust than setting alter in hook_menu().
2b11d164 328 * @see devel_translated_menu_link_alter().
7ce30870 329 *
330 **/
e7aa9505 331function devel_menu_link_alter(&$item) {
f9624262 332 if (in_array($item['link_path'], devel_menu_need_destination()) || $item['link_path'] == 'devel/menu/item') {
7ce30870 333 $item['options']['alter'] = TRUE;
334 }
335}
336
b4a77be0 337/**
8b832e7b 338 * An implementation of hook_translated_menu_item_alter(). Append dynamic
b4a77be0 339 * querystring 'destination' to several of our own menu items.
8b832e7b 340 *
b4a77be0 341 **/
342function devel_translated_menu_link_alter(&$item) {
7ce30870 343 if (in_array($item['href'], devel_menu_need_destination())) {
b4a77be0 344 $item['localized_options']['query'] = drupal_get_destination();
345 }
f9624262 346 elseif ($item['href'] == 'devel/menu/item') {
347 $item['localized_options']['query'] = array('path' => $_GET['q']);
348 }
b4a77be0 349}
350
9d450e49 351/**
715d62f8 352 * Implementation of hook_theme()
353 */
d46ac02a 354function devel_theme() {
6407fb84 355 return array(
5d9df190 356 'devel_querylog' => array(
f01aa6ec 357 'variables' => array('header' => array(), 'rows' => array()),
5d9df190 358 ),
359 'devel_querylog_row' => array(
f01aa6ec 360 'variables' => array('row' => array()),
5d9df190 361 ),
6407fb84 362 );
715d62f8 363}
364
ff31e645 365/**
2ad63d14 366 * Implementation of hook_init().
9d450e49 367 */
1234041c 368function devel_init() {
2ad63d14 369 if (!devel_silent()) {
1234041c 370 if (user_access('access devel information')) {
2ad63d14 371 devel_set_handler(variable_get('devel_error_handler', DEVEL_ERROR_HANDLER_STANDARD));
f129ddc8 372 // We want to include the class early so that anyone may call krumo() as needed. See http://krumo.sourceforge.net/
00399d8f 373 has_krumo();
c9f90533 374
c880e9b9 375 // See http://www.firephp.org/HQ/Install.htm
376 $path = NULL;
377 if (@include_once('fb.php')) {
378 // FirePHPCore is in include_path. Probably a PEAR installation.
379 $path = '';
380 }
381 elseif (module_exists('libraries')) {
382 // Support Libraries API - http://drupal.org/project/libraries
21548eff 383 $firephp_path = libraries_get_path('FirePHPCore') . '/lib/FirePHPCore/';
384 $chromephp_path = libraries_get_path('chromephp');
722342b4 385 }
386 else {
21548eff 387 $firephp_path = './'. drupal_get_path('module', 'devel') .'/FirePHPCore/lib/FirePHPCore/';
388 $chromephp_path = './' . drupal_get_path('module', 'devel') .'/chromephp';
722342b4 389 }
21548eff 390
391 // include FirePHP if exists...
392 if (file_exists($firephp_path .'fb.php')) {
393 include_once $firephp_path .'fb.php';
394 include_once $firephp_path .'FirePHP.class.php';
395 }
396
397 // include ChromePHP if exists...
398 if (file_exists($chromephp_path . '/ChromePhp.php')) {
399 include_once $chromephp_path . '/ChromePhp.php';
72671f4a 400 }
c880e9b9 401
21548eff 402
5d9df190 403 // Add CSS for query log if should be displayed.
404 if (variable_get('devel_query_display', 0)) {
7a2bcf56 405 drupal_add_css(drupal_get_path('module', 'devel') .'/devel.css');
2b11d164 406 drupal_add_js(drupal_get_path('module', 'devel'). '/devel.js');
5d9df190 407 }
1234041c 408 }
1c411384 409 }
a3c7d492 410 if (variable_get('devel_rebuild_theme_registry', FALSE)) {
411 drupal_theme_rebuild();
412 if (flood_is_allowed('devel_rebuild_registry_warning', 1)) {
413 flood_register_event('devel_rebuild_registry_warning');
414 if (!devel_silent() && user_access('access devel information')) {
5de9665a 415 drupal_set_message(t('The theme registry is being rebuilt on every request. Remember to <a href="!url">turn off</a> this feature on production websites.', array("!url" => url('admin/config/development/devel'))));
a3c7d492 416 }
417 }
418 }
2ad63d14 419}
420
897503be 421function devel_set_message($msg, $type = NULL) {
9efcdc47 422 $function = function_exists('drush_log') ? 'drush_log' : 'drupal_set_message';
423 $function($msg, $type);
897503be 424}
425
6531524e 426// Return boolean. No need for cache here.
00399d8f 427function has_krumo() {
428 // see README.txt or just download from http://krumo.sourceforge.net/
6531524e 429 @include_once DRUPAL_ROOT . '/' . drupal_get_path('module', 'devel') .'/krumo/class.krumo.php';
430 return function_exists('krumo') && !drupal_is_cli();
00399d8f 431}
432
f129ddc8 433/**
47257719 434 * Decide whether or not to print a debug variable using krumo().
435 *
8b832e7b 436 * @param $input
47257719 437 * @return boolean
438 */
439function merits_krumo($input) {
8b832e7b 440 return (is_object($input) || is_array($input)) && has_krumo() && variable_get('devel_krumo_skin', '') != 'disabled';
47257719 441}
442
443/**
f129ddc8 444 * Calls the http://www.firephp.org/ fb() function if it is found.
445 *
446 * @return void
447 */
448function dfb() {
cd4e857b 449 if (function_exists('fb') && user_access('access devel information') && !headers_sent()) {
f129ddc8 450 $args = func_get_args();
451 call_user_func_array('fb', $args);
452 }
453}
454
6b928890 455/**
456 * Calls dfb() to output a backtrace.
457 */
458function dfbt($label) {
459 dfb($label, FirePHP::TRACE);
460}
461
462/**
21548eff 463 * Wrapper for ChromePHP Class log method
464 */
465function dcp() {
466 if (class_exists('ChromePhp') && user_access('access devel information')) {
467 $args = func_get_args();
468 call_user_func_array(array('ChromePhp', 'log'), $args);
469 }
470}
471
472/**
6b928890 473 * Implements hook_watchdog().
474 */
475function devel_watchdog(array $log_entry) {
0b34858c 476 if (class_exists('FirePHP') && !drupal_is_cli()) {
6b928890 477 switch ($log_entry['severity']) {
6437f4c5 478 case WATCHDOG_EMERGENCY:
6b928890 479 case WATCHDOG_ALERT:
480 case WATCHDOG_CRITICAL:
481 case WATCHDOG_ERROR:
482 $type = FirePHP::ERROR;
483 break;
484 case WATCHDOG_WARNING:
485 $type = FirePHP::WARN;
486 break;
487 case WATCHDOG_NOTICE:
488 case WATCHDOG_INFO:
489 $type = FirePHP::INFO;
490 break;
491 case WATCHDOG_DEBUG:
492 DEFAULT:
493 $type = FirePHP::LOG;
494 }
495 }
496 else {
497 $type = 'watchdog';
498 }
6d8ccb50 499 $function = function_exists('decode_entities') ? 'decode_entities' : 'html_entity_decode';
6b928890 500 $watchdog = array(
501 'type' => $log_entry['type'],
6d8ccb50 502 'message' => $function(strtr($log_entry['message'], (array)$log_entry['variables'])),
6b928890 503 );
504 if (isset($log_entry['link'])) {
505 $watchdog['link'] = $log_entry['link'];
506 }
507 dfb($watchdog, $type);
508}
f129ddc8 509
2ad63d14 510function devel_set_handler($handler) {
511 switch ($handler) {
512 case DEVEL_ERROR_HANDLER_STANDARD:
513 // do nothing
514 break;
515 case DEVEL_ERROR_HANDLER_BACKTRACE:
7a2bcf56 516 if (has_krumo()) {
854d02b8 517 set_error_handler('backtrace_error_handler');
518 }
2ad63d14 519 break;
520 case DEVEL_ERROR_HANDLER_NONE:
521 restore_error_handler();
522 break;
523 }
524}
525
2ad63d14 526function devel_silent() {
1cc02057 527 // isset($_GET['q']) is needed when calling the front page. q is not set.
8ab0a6c5 528 // Don't interfere with private files/images.
8b832e7b 529 return
2b11d164 530 function_exists('drupal_is_cli') && drupal_is_cli() ||
4d321903 531 (isset($_SERVER['HTTP_USER_AGENT']) && strpos($_SERVER['HTTP_USER_AGENT'], 'ApacheBench') !== FALSE) ||
1ee49902 532 !empty($_REQUEST['XDEBUG_PROFILE']) ||
8b832e7b 533 isset($GLOBALS['devel_shutdown']) ||
534 strstr($_SERVER['PHP_SELF'], 'update.php') ||
e75a563a 535 (isset($_GET['q']) && (
bb99b63a 536 in_array($_GET['q'], array( 'admin/content/node-settings/rebuild')) ||
e75a563a 537 substr($_GET['q'], 0, strlen('system/files')) == 'system/files' ||
1cc02057 538 substr($_GET['q'], 0, strlen('batch')) == 'batch' ||
539 substr($_GET['q'], 0, strlen('file/ajax')) == 'file/ajax')
e75a563a 540 );
2ad63d14 541}
542
0b34858c 543function devel_xhprof_enable() {
bdadd538 544 if (devel_xhprof_is_enabled()) {
0b34858c 545 if ($path = variable_get('devel_xhprof_directory', '')) {
546 include_once $path . '/xhprof_lib/utils/xhprof_lib.php';
547 include_once $path . '/xhprof_lib/utils/xhprof_runs.php';
548 // @todo: consider a variable per-flag instead.
549 xhprof_enable(XHPROF_FLAGS_CPU + XHPROF_FLAGS_MEMORY);
550 }
551 }
552}
553
bdadd538 554function devel_xhprof_is_enabled() {
555 return extension_loaded('xhprof') && variable_get('devel_xhprof_enabled', FALSE);
556}
557
2ad63d14 558/**
559 * Implementation of hook_boot(). Runs even for cached pages.
560 */
561function devel_boot() {
0b34858c 562 // Initialize XHProf.
563 devel_xhprof_enable();
1c411384 564
0b34858c 565 if (!devel_silent()) {
566 if (variable_get('dev_mem', 0)) {
567 global $memory_init;
568 $memory_init = memory_get_usage();
569 }
83013f8f 570
0b34858c 571 if (devel_query_enabled()) {
572 @include_once DRUPAL_ROOT . '/includes/database/log.inc';
573 Database::startLog('devel');;
574 }
c7414be0 575 }
83013f8f 576
4a35980f 577 // We need user_access() in the shutdown function. make sure it gets loaded.
578 // Also prime the drupal_get_filename() static with user.module's location to
579 // avoid a stray query.
580 drupal_get_filename('module', 'user', 'modules/user/user.module');
4c129b0b 581 drupal_load('module', 'user');
84070d93 582 drupal_register_shutdown_function('devel_shutdown');
2ad63d14 583}
584
ad3f5e47 585function backtrace_error_handler($error_level, $message, $filename, $line, $context) {
586 // Hide stack trace and parameters from unqualified users.
587 if (!user_access('access devel information')) {
588 return _drupal_error_handler($error_level, $message, $filename, $line, $context);
589 }
97fffd59 590 // Don't respond to the error if it was suppressed with a '@'
e4f24abf 591 if (error_reporting() == 0) {
592 return;
593 }
fff77b40 594 // Don't respond to warning caused by ourselves.
0f327328 595 if (preg_match('#Cannot modify header information - headers already sent by \\([^\\)]*[/\\\\]devel[/\\\\]#', $message)) {
fff77b40 596 return;
597 }
e4f24abf 598 if ($error_level & error_reporting()) {
8c0b81e3 599 // Only write each distinct NOTICE message once, as repeats do not give any
600 // further information and can choke the page output.
601 if ($error_level == E_NOTICE) {
602 static $written = array();
603 if (!empty($written[$line][$filename][$message])) {
604 return;
605 }
606 $written[$line][$filename][$message] = TRUE;
607 }
608
f3d0e3f1 609 require_once DRUPAL_ROOT . '/includes/errors.inc';
610 $types = drupal_error_levels();
611 $type = $types[$error_level];
612 $backtrace = debug_backtrace();
613 array_shift($backtrace);
614 $variables = array('%error' => $type[0], '%message' => $message, '%function' => $backtrace[0]['function'] .'()', '%file' => $filename, '%line' => $line);
615 $counter = 0;
1c411384 616
617 if (variable_get('error_level', 1) == 1) {
854d02b8 618 foreach ($backtrace as $call) {
f3d0e3f1 619 $nicetrace[$call['function'] . '<span class="' . $counter++ . '" />'] = $call;
1c411384 620 }
f3d0e3f1 621 print t('%error: %message in %function (line %line of %file).', $variables) ." =&gt;\n";
854d02b8 622 krumo($nicetrace);
1c411384 623 }
624
f3d0e3f1 625 watchdog('php', '%error: %message in %function (line %line of %file).', $variables, $type[1]);
c380bd17 626 }
74b35ebe 627}
628
9d450e49 629/**
1e12a7ae 630 * Implement hook_permission().
9d450e49 631 */
1e12a7ae 632function devel_permission() {
32c93e75 633 return array(
e33d0a65 634 'access devel information' => array(
83013f8f 635 'description' => t('View developer output like variable printouts, query log, etc.'),
e33d0a65 636 'title' => t('Access developer information'),
e3d5f819 637 'restrict access' => TRUE,
e33d0a65 638 ),
639 'execute php code' => array(
640 'title' => t('Execute PHP code'),
e3d5f819 641 'description' => t('Run arbitrary PHP from a block.'),
642 'restrict access' => TRUE,
e33d0a65 643 ),
644 'switch users' => array(
645 'title' => t('Switch users'),
e3d5f819 646 'description' => t('Become any user on the site with just a click.'),
647 'restrict access' => TRUE,
e33d0a65 648 ),
649 'display source code' => array(
650 'title' => t('Display source code'),
e3d5f819 651 'description' => t('View the site\'s php source code.'),
652 'restrict access' => TRUE,
e33d0a65 653 ),
32c93e75 654 );
9d450e49 655}
656
d5e87c9b 657function devel_block_info() {
658 $blocks['execute_php'] = array(
659 'info' => t('Execute PHP'),
660 'cache' => DRUPAL_NO_CACHE,
661 );
662 $blocks['switch_user'] = array(
663 'info' => t('Switch user'),
664 'cache' => DRUPAL_NO_CACHE,
665 );
e83aafef 666 return $blocks;
667}
668
669/**
d5e87c9b 670 * Implementation of hook_block_configure().
e83aafef 671 */
672function devel_block_configure($delta) {
d5e87c9b 673 if ($delta == 'switch_user') {
4561c8ec 674 $form['list_size'] = array(
714bcfeb
DW
675 '#type' => 'textfield',
676 '#title' => t('Number of users to display in the list'),
677 '#default_value' => variable_get('devel_switch_user_list_size', 10),
678 '#size' => '3',
679 '#maxlength' => '4',
680 );
4561c8ec 681 $form['include_anon'] = array(
74a6c4bb 682 '#type' => 'checkbox',
6c7ce8de 683 '#title' => t('Include %anonymous', array('%anonymous' => format_username(drupal_anonymous_user()))),
74a6c4bb 684 '#default_value' => variable_get('devel_switch_user_include_anon', FALSE),
685 );
4561c8ec 686 $form['show_form'] = array(
687 '#type' => 'checkbox',
688 '#title' => t('Allow entering any user name'),
689 '#default_value' => variable_get('devel_switch_user_show_form', TRUE),
690 );
714bcfeb
DW
691 return $form;
692 }
e83aafef 693}
694
695function devel_block_save($delta, $edit = array()) {
d5e87c9b 696 if ($delta == 'switch_user') {
4561c8ec 697 variable_set('devel_switch_user_list_size', $edit['list_size']);
698 variable_set('devel_switch_user_include_anon', $edit['include_anon']);
699 variable_set('devel_switch_user_show_form', $edit['show_form']);
714bcfeb 700 }
e83aafef 701}
702
703function devel_block_view($delta) {
704 $block = array();
705 switch ($delta) {
d5e87c9b 706 case 'switch_user':
e83aafef 707 $block = devel_block_switch_user();
708 break;
d5e87c9b 709
710 case 'execute_php':
e83aafef 711 if (user_access('execute php code')) {
e29f1727 712 $block['content'] = drupal_get_form('devel_execute_block_form');
e83aafef 713 }
714 break;
2ad63d14 715 }
e83aafef 716 return $block;
2ad63d14 717}
9d450e49 718
2ad63d14 719function devel_block_switch_user() {
a200276a 720 $links = devel_switch_user_list();
43c43215 721 if (!empty($links) || user_access('switch users')) {
e83aafef 722 $block['subject'] = t('Switch user');
ecea88f1 723 $build['devel_links'] = array('#theme' => 'links', '#links' => $links);
4561c8ec 724 if (variable_get('devel_switch_user_show_form', TRUE)) {
725 $build['devel_form'] = drupal_get_form('devel_switch_user_form');
726 }
a2d2befa 727 $block['content'] = $build;
a200276a
DW
728 return $block;
729 }
730}
731
732function devel_switch_user_list() {
74a6c4bb 733 global $user;
734
2ad63d14 735 $links = array();
736 if (user_access('switch users')) {
2cbf0d1e 737 $list_size = variable_get('devel_switch_user_list_size', 10);
74a6c4bb 738 if ($include_anon = ($user->uid && variable_get('devel_switch_user_include_anon', FALSE))) {
739 --$list_size;
740 }
2ad63d14 741 $dest = drupal_get_destination();
2cbf0d1e 742 // Try to find at least $list_size users that can switch.
05380656 743 // Inactive users are omitted from all of the following db selects.
08abd453 744 $roles = user_roles(TRUE, 'switch users');
d191a7c2 745 $query = db_select('users', 'u');
746 $query->addField('u', 'uid');
747 $query->distinct();
748 $query->condition('u.uid', 0, '>');
749 $query->condition('u.status', 0, '>');
750 $query->orderBy('u.access', 'DESC');
751 $query->range(0, $list_size);
752
753 if (!isset($roles[DRUPAL_AUTHENTICATED_RID])) {
754 $query->leftJoin('users_roles', 'r', 'u.uid = r.uid');
755 $or_condition = db_or();
756 $or_condition->condition('u.uid', 1);
a7c110b1
DR
757 if (!empty($roles)) {
758 $or_condition->condition('r.rid', array_keys($roles), 'IN');
759 }
d191a7c2 760 $query->condition($or_condition);
2ad63d14 761 }
d191a7c2 762
763 $uids = $query->execute()->fetchCol();
764 $accounts = user_load_multiple($uids);
765
74a6c4bb 766 foreach ($accounts as $account) {
767 $links[$account->uid] = array(
d191a7c2 768 'title' => drupal_placeholder(format_username($account)),
74a6c4bb 769 'href' => 'devel/switch/'. $account->name,
e83aafef 770 'query' => $dest,
771 'attributes' => array('title' => t('This user can switch back.')),
772 'html' => TRUE,
74a6c4bb 773 'last_access' => $account->access,
e83aafef 774 );
2ad63d14 775 }
776 $num_links = count($links);
2cbf0d1e
DW
777 if ($num_links < $list_size) {
778 // If we don't have enough, add distinct uids until we hit $list_size.
d191a7c2 779 $uids = db_query_range('SELECT uid FROM {users} WHERE uid > 0 AND uid NOT IN (:uids) AND status > 0 ORDER BY access DESC', 0, $list_size - $num_links, array(':uids' => array_keys($links)))->fetchCol();
780 $accounts = user_load_multiple($uids);
74a6c4bb 781 foreach ($accounts as $account) {
782 $links[$account->uid] = array(
d191a7c2 783 'title' => format_username($account),
74a6c4bb 784 'href' => 'devel/switch/'. $account->name,
e83aafef 785 'query' => $dest,
08abd453 786 'attributes' => array('title' => t('Caution: this user will be unable to switch back.')),
74a6c4bb 787 'last_access' => $account->access,
e83aafef 788 );
2ad63d14 789 }
08abd453 790 uasort($links, '_devel_switch_user_list_cmp');
2ad63d14 791 }
74a6c4bb 792 if ($include_anon) {
793 $link = array(
d191a7c2 794 'title' => format_username(drupal_anonymous_user()),
74a6c4bb 795 'href' => 'devel/switch',
796 'query' => $dest,
797 'attributes' => array('title' => t('Caution: the anonymous user will be unable to switch back.')),
798 );
0c54f660 799 if (user_access('switch users', drupal_anonymous_user())) {
800 $link['title'] = drupal_placeholder($link['title']);
74a6c4bb 801 $link['attributes'] = array('title' => t('This user can switch back.'));
802 $link['html'] = TRUE;
803 }
804 $links[] = $link;
805 }
2ad63d14 806 }
a200276a 807 return $links;
2ad63d14 808}
809
08abd453 810/**
811 * Comparison helper function for uasort() in devel_switch_user_list().
812 *
813 * Sorts the Switch User links by the user's last access timestamp.
814 */
815function _devel_switch_user_list_cmp($a, $b) {
816 return $b['last_access'] - $a['last_access'];
817}
818
15dcd830 819function devel_switch_user_form() {
820 $form['username'] = array(
821 '#type' => 'textfield',
822 '#description' => t('Enter username'),
d47ac609
DW
823 '#autocomplete_path' => 'user/autocomplete',
824 '#maxlength' => USERNAME_MAX_LENGTH,
15dcd830 825 '#size' => 16,
15dcd830 826 );
827 $form['submit'] = array(
1234041c 828 '#type' => 'submit',
15dcd830 829 '#value' => t('Switch'),
830 );
831 return $form;
1234041c 832
15dcd830 833}
834
2ad63d14 835function devel_doc_function_form() {
f2a633ed 836 $version = devel_get_core_version(VERSION);
2ad63d14 837 $form['function'] = array(
838 '#type' => 'textfield',
839 '#description' => t('Enter function name for api lookup'),
840 '#size' => 16,
841 '#maxlength' => 255,
842 );
843 $form['version'] = array('#type' => 'value', '#value' => $version);
844 $form['submit_button'] = array(
1234041c 845 '#type' => 'submit',
2ad63d14 846 '#value' => t('Submit'),
847 );
848 return $form;
849}
850
1234041c
YC
851function devel_doc_function_form_submit($form, &$form_state) {
852 $version = $form_state['values']['version'];
853 $function = $form_state['values']['function'];
2ad63d14 854 $api = variable_get('devel_api_url', 'api.drupal.org');
47d90d36 855 $form_state['redirect'] = "http://$api/api/function/$function/$version";
2ad63d14 856}
857
1234041c 858function devel_switch_user_form_validate($form, &$form_state) {
a2d2befa 859 if (!$account = user_load_by_name($form_state['values']['username'])) {
15dcd830 860 form_set_error('username', t('Username not found'));
1234041c 861 }
15dcd830 862}
863
1234041c
YC
864function devel_switch_user_form_submit($form, &$form_state) {
865 $form_state['redirect'] = 'devel/switch/'. $form_state['values']['username'];
15dcd830 866}
867
bea6198f 868/**
869 * Implements hook_drupal_goto_alter().
870 */
97db0981 871function devel_drupal_goto_alter($path, $options, $http_response_code) {
f6f6cda4 872 global $user;
a30583d1 873
97db0981 874 if (isset($path) && !devel_silent()) {
54fecd4a
JC
875 // The page we are leaving is a drupal_goto(). Present a redirection page
876 // so that the developer can see the intermediate query log.
e87cf471 877 // We don't want to load user module here, so keep function_exists() call.
71d543d7 878 if (isset($user) && function_exists('user_access') && user_access('access devel information') && variable_get('devel_redirect_page', 0)) {
97db0981 879 $destination = function_exists('url') ? url($path, $options) : $path;
71d543d7 880 $output = t_safe('<p>The user is being redirected to <a href="@destination">@destination</a>.</p>', array('@destination' => $destination));
97db0981 881 drupal_deliver_page($output);
8416c64c 882
54fecd4a 883 // Don't allow the automatic redirect to happen.
54fecd4a
JC
884 exit();
885 }
8416c64c 886 else {
3f06b311 887 $GLOBALS['devel_redirecting'] = TRUE;
8416c64c 888 }
54fecd4a 889 }
ba836d3a 890}
8416c64c 891
ba836d3a 892/**
bea6198f 893 * Implements hook_library_alter().
894 */
895function devel_library_alter(&$libraries, $module) {
896 // Use an uncompressed version of jQuery for debugging.
897 if ($module === 'system' && variable_get('devel_use_uncompressed_jquery', FALSE) && isset($libraries['jquery'])) {
898 // Make sure we're not changing the jQuery version used on the site.
899 if (version_compare($libraries['jquery']['version'], '1.4.4', '=')) {
900 $libraries['jquery']['js'] = array(
901 drupal_get_path('module', 'devel') . '/jquery-1.4.4-uncompressed.js' => array('weight' => JS_LIBRARY - 20),
902 );
903 }
904 else {
905 if (!devel_silent() && user_access('access devel information')) {
906 drupal_set_message(t('jQuery could not be replaced with an uncompressed version of 1.4.4, because jQuery @version is running on the site.', array('@version' => $libraries['jquery']['version'])));
907 }
908
909 }
910 }
911}
912
913/**
6ff481db 914 * See devel_start() which registers this function as a shutdown function.
ba836d3a 915 */
916function devel_shutdown() {
b0771e29 917 // Register the real shutdown function so it runs later than other shutdown functions.
84070d93 918 drupal_register_shutdown_function('devel_shutdown_real');
0b34858c 919
0b34858c 920 global $devel_run_id;
bdadd538 921 $devel_run_id = devel_xhprof_is_enabled() ? devel_shutdown_xhprof(): NULL;
ddb4cf41 922 if ($devel_run_id && function_exists('drush_log')) {
923 drush_log('xhprof link: ' . devel_xhprof_link($devel_run_id, 'url'), 'notice');
0b34858c 924 }
b0771e29 925}
926
d131f63d 927function devel_page_alter($page) {
928 if (variable_get('devel_page_alter', FALSE) && user_access('access devel information')) {
929 dpm($page, 'page');
930 }
931}
d89dc447 932
68347211 933// AJAX render reponses sometimers are sent as text/html so we have to catch them here
934// and disable our footer stuff.
935function devel_ajax_render_alter() {
936 $GLOBALS['devel_shutdown'] = FALSE;
937}
938
b0771e29 939/**
940 * See devel_shutdown() which registers this function as a shutdown function. Displays developer information in the footer.
941 */
942function devel_shutdown_real() {
19f75490 943 global $user;
0f79f53f 944 $output = $txt = '';
2ad63d14 945
91abefea 946 // Set $GLOBALS['devel_shutdown'] = FALSE in order to supress the
947 // devel footer for a page. Not necessary if your page outputs any
948 // of the Content-type http headers tested below (e.g. text/xml,
949 // text/javascript, etc). This is is advised where applicable.
0b34858c 950 if (!devel_silent() && !isset($GLOBALS['devel_shutdown']) && !isset($GLOBALS['devel_redirecting'])) {
3386015e 951 // Try not to break non html pages.
bb99b63a 952 if (function_exists('drupal_get_http_header')) {
953 $header = drupal_get_http_header('content-type');
954 if ($header) {
851bdad2 955 $formats = array('xml', 'javascript', 'json', 'plain', 'image', 'application', 'csv', 'x-comma-separated-values');
956 foreach ($formats as $format) {
bb99b63a 957 if (strstr($header, $format)) {
851bdad2 958 return;
959 }
91abefea 960 }
3386015e 961 }
631ca979 962 }
8416c64c 963
f6f6cda4 964 if (isset($user) && user_access('access devel information')) {
19f75490 965 $queries = Database::getLog('devel', 'default');
be83baed 966 $output .= devel_shutdown_summary($queries);
19f75490 967 $output .= devel_shutdown_query($queries);
40d052a2 968 }
3efc2536 969
40d052a2 970 if ($output) {
b3c77c37 971 // TODO: gzip this text if we are sending a gzip page. see drupal_page_header().
d5e87c9b 972 // For some reason, this is not actually printing for cached pages even though it gets executed
5eb79cbf 973 // and $output looks good.
40d052a2 974 print $output;
1234041c 975 }
3386015e 976 }
ba836d3a 977}
978
be83baed 979function devel_shutdown_summary($queries) {
19f75490 980 $sum = 0;
8865b684 981 $output = '';
c7414be0 982 list($counts, $query_summary) = devel_query_summary($queries);
19f75490 983
d6032121 984 if (variable_get('devel_query_display', FALSE)) {
19f75490 985 // Query log on.
c7414be0 986 $output .= $query_summary;
d6032121 987 $output .= t_safe(' Queries exceeding @threshold ms are <span class="marker">highlighted</span>.', array('@threshold' => variable_get('devel_execution', 5)));
c7414be0 988 }
83013f8f 989
d6032121 990 if (variable_get('dev_timer', 0)) {
991 $output .= devel_timer();
992 }
993
bdadd538 994 if (devel_xhprof_is_enabled()) {
0b34858c 995 $output .= ' ' . devel_xhprof_link($GLOBALS['devel_run_id']);
50a4afca 996 }
997
d6032121 998 $output .= devel_shutdown_memory();
999
fefff8c5 1000 if ($output) {
1001 return '<div class="dev-query">' . $output . '</div>';
1002 }
c7414be0 1003}
1004
0b34858c 1005function devel_shutdown_xhprof() {
50a4afca 1006 $namespace = variable_get('site_name', ''); // namespace for your application
1007 $xhprof_data = xhprof_disable();
50a4afca 1008 $xhprof_runs = new XHProfRuns_Default();
0b34858c 1009 return $xhprof_runs->save_run($xhprof_data, $namespace);
1010}
1011
1012function devel_xhprof_link($run_id, $type = 'link') {
50a4afca 1013 // @todo: render results from within Drupal.
1014 $xhprof_url = variable_get('devel_xhprof_url', '');
0b34858c 1015 $namespace = variable_get('site_name', ''); // namespace for your application
50a4afca 1016 if ($xhprof_url) {
0b34858c 1017 $url = $xhprof_url . "/index.php?run=$run_id&source=$namespace";
1018 return $type == 'url' ? $url : t('<a href="@xhprof">XHProf output</a>. ', array('@xhprof' => $url));
50a4afca 1019 }
1020}
1021
19f75490 1022function devel_shutdown_memory() {
1023 global $memory_init;
1024
1025 if (variable_get('dev_mem', FALSE)) {
1026 $memory_shutdown = memory_get_usage();
1027 $args = array('@memory_boot' => round($memory_init / 1024 / 1024, 2), '@memory_shutdown' => round($memory_shutdown / 1024 / 1024, 2), '@memory_peak' => round(memory_get_peak_usage(TRUE) / 1024 / 1024, 2));
1028 $msg = '<span class="dev-memory-usages"> Memory used at: devel_boot()=<strong>@memory_boot</strong> MB, devel_shutdown()=<strong>@memory_shutdown</strong> MB, PHP peak=<strong>@memory_peak</strong> MB.</span>';
1029 // theme() may not be available. not t() either.
1030 return t_safe($msg, $args);
1031 }
1032}
1033
1034function devel_shutdown_query($queries) {
04a5cd55 1035 if (!empty($queries)) {
04a5cd55 1036 if (function_exists('theme_get_registry') && theme_get_registry()) {
1037 // Safe to call theme('table).
1038 list($counts, $query_summary) = devel_query_summary($queries);
321e2c8a 1039 $output = devel_query_table($queries, $counts);
19f75490 1040
04a5cd55 1041 // Save all queries to a file in temp dir. Retrieved via AJAX.
1042 devel_query_put_contents($queries);
1043 }
1044 else {
321e2c8a 1045 $output = '</div>' . dprint_r($queries, TRUE);
04a5cd55 1046 }
321e2c8a 1047 return $output;
19f75490 1048 }
19f75490 1049}
1050
2b11d164 1051// Write the variables information to the a file. It will be retrieved on demand via AJAX.
1052function devel_query_put_contents($queries) {
1053 $request_id = mt_rand(1, 1000000);
1054 $path = "temporary://devel_querylog";
1055
1056 // Create the devel_querylog within the temp folder, if needed.
1057 file_prepare_directory($path, FILE_CREATE_DIRECTORY);
2b11d164 1058
1059 // Occassionally wipe the querylog dir so that files don't accumulate.
1060 if (mt_rand(1, 1000) == 401) {
1061 devel_empty_dir($path);
1062 }
1063
a1cc25d1 1064 $path .= "/$request_id.txt";
1065 $path = file_stream_wrapper_uri_normalize($path);
2af4f983 1066 // Save queries as a json array. Suppress errors due to recursion ()
1067 file_put_contents($path, @json_encode($queries));
2b11d164 1068 $settings['devel'] = array(
1069 // A random string that is sent to the browser. It enables the AJAX to retrieve queries from this request.
1070 'request_id' => $request_id,
2b11d164 1071 );
1072 print '<script type="text/javascript">jQuery.extend(Drupal.settings, '. json_encode($settings) .");</script>\n";
1073}
1074
c7414be0 1075function devel_query_enabled() {
1b6f0f46 1076 return method_exists('Database', 'getLog') && variable_get('devel_query_display', FALSE);
c7414be0 1077}
1078
c7414be0 1079function devel_query_summary($queries) {
1b6f0f46 1080 if (variable_get('devel_query_display', FALSE) && is_array($queries)) {
c5c59e20 1081 $sum = 0;
a0610ff4 1082 foreach ($queries as $query) {
c7414be0 1083 $text[] = $query['query'];
1084 $sum += $query['time'];
a0610ff4 1085 }
1086 $counts = array_count_values($text);
19f75490 1087 return array($counts, t_safe('Executed @queries queries in @time ms.', array('@queries' => count($queries), '@time' => round($sum * 1000, 2))));
ed856aa6 1088 }
ed856aa6 1089}
1090
2ad63d14 1091function t_safe($string, $args) {
6407fb84 1092 // get_t caused problems here with theme registry after changing on admin/build/modules. the theme_get_registry call is needed.
0f79f53f 1093 if (function_exists('t') && function_exists('theme_get_registry')) {
1094 theme_get_registry();
1095 return t($string, $args);
1096 }
1097 else {
1098 strtr($string, $args);
1099 }
3386015e 1100}
1101
f2a633ed
DW
1102function devel_get_core_version($version) {
1103 $version_parts = explode('.', $version);
1104 // Map from 4.7.10 -> 4.7
1105 if ($version_parts[0] < 5) {
1106 return $version_parts[0] .'.'. $version_parts[1];
1107 }
1108 // Map from 5.5 -> 5 or 6.0-beta2 -> 6
1109 else {
1110 return $version_parts[0];
1111 }
2ad63d14 1112}
1234041c 1113
c871aa0b 1114// See http://drupal.org/node/126098
1115function devel_is_compatible_optimizer() {
1116 ob_start();
1117 phpinfo();
1118 $info = ob_get_contents();
1119 ob_end_clean();
1120
fdd7e742 1121 // Match the Zend Optimizer version in the phpinfo information
c871aa0b 1122 $found = preg_match('/Zend&nbsp;Optimizer&nbsp;v([0-9])\.([0-9])\.([0-9])/', $info, $matches);
1123
1124 if ($matches) {
1125 $major = $matches[1];
1126 $minor = $matches[2];
1127 $build = $matches[3];
8b832e7b 1128
c871aa0b 1129 if ($major >= 3) {
1130 if ($minor >= 3) {
1131 return TRUE;
8b832e7b 1132 }
c871aa0b 1133 elseif ($minor == 2 && $build >= 8) {
1134 return TRUE;
8b832e7b 1135 }
c871aa0b 1136 else {
1137 return FALSE;
1138 }
8b832e7b 1139 }
c871aa0b 1140 else {
1141 return FALSE;
1142 }
1143 }
1144 else {
1145 return TRUE;
1146 }
1147}
1148
1e7a3874 1149/**
d31522e4 1150 * Generates the execute block form.
1e7a3874 1151 */
e29f1727 1152function devel_execute_block_form() {
1153 $form['execute'] = array(
1154 '#type' => 'fieldset',
1155 '#title' => t('Execute PHP Code'),
1156 '#collapsible' => TRUE,
1157 '#collapsed' => (!isset($_SESSION['devel_execute_code'])),
1158 );
1159 $form['#submit'] = array('devel_execute_form_submit');
1160 return array_merge_recursive($form, devel_execute_form());
1161}
1162
1163/**
1164 * Generates the execute form.
1165 */
d31522e4 1166function devel_execute_form() {
e29f1727 1167 $form['execute']['code'] = array(
f04f2a57 1168 '#type' => 'textarea',
9d450e49 1169 '#title' => t('PHP code to execute'),
e29f1727 1170 '#description' => t('Enter some code. Do not use <code>&lt;?php ?&gt;</code> tags.'),
1171 '#default_value' => (isset($_SESSION['devel_execute_code']) ? $_SESSION['devel_execute_code'] : ''),
bf1922cb 1172 '#rows' => 20,
9d450e49 1173 );
e29f1727 1174 $form['execute']['op'] = array('#type' => 'submit', '#value' => t('Execute'));
d31522e4 1175 $form['#redirect'] = FALSE;
a8307cf5 1176 if (isset($_SESSION['devel_execute_code'])) {
1177 unset($_SESSION['devel_execute_code']);
1178 }
d31522e4 1179 return $form;
e0379979 1180}
1181
1e7a3874 1182/**
d31522e4 1183 * Process PHP execute form submissions.
1e7a3874 1184 */
91f34f17 1185function devel_execute_form_submit($form, &$form_state) {
d31522e4 1186 ob_start();
278b805f 1187 print eval($form_state['values']['code']);
e29f1727 1188 $_SESSION['devel_execute_code'] = $form_state['values']['code'];
d31522e4 1189 dsm(ob_get_clean());
22c2656f 1190}
1191
f3ef93f8 1192/**
9d450e49 1193 * Switch from original user to another user and back.
5b73e5e4 1194 * We don't call session_save_session() because we really want to change users. Usually unsafe!
9d450e49 1195 *
372cce23 1196 * @param $name The username to switch to, or NULL to log out.
9f4ee613 1197 */
15dcd830 1198function devel_switch_user($name = NULL) {
9d450e49 1199 global $user;
9d450e49 1200
372cce23 1201 if ($user->uid) {
1202 module_invoke_all('user_logout', $user);
9d450e49 1203 }
372cce23 1204 if (isset($name) && $account = user_load_by_name($name)) {
79fdb1a2 1205 $old_uid = $user->uid;
372cce23 1206 $user = $account;
f9bc1654 1207 $user->timestamp = time() - 9999;
79fdb1a2 1208 if (!$old_uid) {
1209 // Switch from anonymous to authorized.
1210 drupal_session_regenerate();
1211 }
372cce23 1212 $edit = array();
1213 user_module_invoke('login', $edit, $user);
9f4ee613 1214 }
372cce23 1215 elseif ($user->uid) {
1216 session_destroy();
9d450e49 1217 }
1218 drupal_goto();
9f4ee613
MI
1219}
1220
0356e334 1221/**
3efc2536
JR
1222 * Print an object or array using either Krumo (if installed) or devel_print_object()
1223 *
1224 * @param $object
1225 * array or object to print
1226 * @param $prefix
1227 * prefixing for output items
1228 */
1229function kdevel_print_object($object, $prefix = NULL) {
1230 return has_krumo() ? krumo_ob($object) : devel_print_object($object, $prefix);
00399d8f 1231}
1232
1233// Save krumo htlm using output buffering.
1234function krumo_ob($object) {
1235 ob_start();
1236 krumo($object);
1237 $output = ob_get_contents();
1238 ob_end_clean();
1239 return $output;
fc524554 1240}
1241
3efc2536
JR
1242/**
1243 * Display an object or array
1244 *
1245 * @param $object
1246 * the object or array
1247 * @param $prefix
1248 * prefix for the output items (example "$node->", "$user->", "$")
1249 * @param $header
1250 * set to FALSE to suppress the output of the h3
1251 */
1252function devel_print_object($object, $prefix = NULL, $header = TRUE) {
7a2bcf56 1253 drupal_add_css(drupal_get_path('module', 'devel') .'/devel.css');
3efc2536
JR
1254 $output = '<div class="devel-obj-output">';
1255 if ($header) {
7a2bcf56 1256 $output .= '<h3>'. t('Display of !type !obj', array('!type' => str_replace(array('$', '->'), '', $prefix), '!obj' => gettype($object))) .'</h3>';
3efc2536
JR
1257 }
1258 $output .= _devel_print_object($object, $prefix);
1259 $output .= '</div>';
1260 return $output;
1261}
0356e334 1262
3efc2536
JR
1263/**
1264 * Recursive (and therefore magical) function goes through an array or object and
1265 * returns a nicely formatted listing of its contents.
1266 *
1267 * @param $obj
1268 * array or object to recurse through
1269 * @param $prefix
1270 * prefix for the output items (example "$node->", "$user->", "$")
1271 * @param $parents
1272 * used by recursion
1273 * @param $object
1274 * used by recursion
1275 * @return
1276 * fomatted html
1277 *
1278 * @todo
1279 * currently there are problems sending an array with a varname
1280 */
381c8e5e 1281function _devel_print_object($obj, $prefix = NULL, $parents = NULL, $object = FALSE) {
3efc2536 1282 static $root_type, $out_format;
83013f8f 1283
e763e01d 1284 // TODO: support objects with references. See http://drupal.org/node/234581.
1285 if (isset($obj->view)) {
8b832e7b 1286 return;
e763e01d 1287 }
83013f8f 1288
3efc2536
JR
1289 if (!isset($root_type)) {
1290 $root_type = gettype($obj);
381c8e5e
JR
1291 if ($root_type == 'object') {
1292 $object = TRUE;
1293 }
3efc2536
JR
1294 }
1295
1296 if (is_object($obj)) {
1297 $obj = (array)$obj;
1298 }
1299 if (is_array($obj)) {
b0771e29 1300 $output = "<dl>\n";
7a2bcf56 1301 foreach ($obj as $field => $value) {
d1e79aee 1302 if ($field == 'devel_flag_reference') {
1303 continue;
1304 }
381c8e5e 1305 if (!is_null($parents)) {
3efc2536
JR
1306 if ($object) {
1307 $field = $parents .'->'. $field;
1308 }
1309 else {
1310 if (is_int($field)) {
1311 $field = $parents .'['. $field .']';
1312 }
1313 else {
1314 $field = $parents .'[\''. $field .'\']';
1315 }
1316 }
1317 }
1318
6ff481db
JR
1319 $type = gettype($value);
1320
1321 $show_summary = TRUE;
124c3715 1322 $summary = NULL;
6ff481db
JR
1323 if ($show_summary) {
1324 switch ($type) {
1325 case 'string' :
1326 case 'float' :
1327 case 'integer' :
1328 if (strlen($value) == 0) {
1329 $summary = t("{empty}");
1330 }
1331 elseif (strlen($value) < 40) {
82a6dbc2 1332 $summary = htmlspecialchars($value);
ff31e645
JR
1333 }
1334 else {
1335 $summary = format_plural(drupal_strlen($value), '1 character', '@count characters');
6ff481db
JR
1336 }
1337 break;
1338 case 'array' :
1339 case 'object' :
ff31e645 1340 $summary = format_plural(count((array)$value), '1 element', '@count elements');
6ff481db
JR
1341 break;
1342 case 'boolean' :
1343 $summary = $value ? t('TRUE') : t('FALSE');
1344 break;
1345 }
1346 }
124c3715 1347 if (!is_null($summary)) {
6ff481db
JR
1348 $typesum = '('. $type .', <em>'. $summary .'</em>)';
1349 }
1350 else {
1351 $typesum = '('. $type .')';
3efc2536
JR
1352 }
1353
1354 $output .= '<span class="devel-attr">';
6ff481db 1355 $output .= "<dt><span class=\"field\">{$prefix}{$field}</span> $typesum</dt>\n";
3efc2536 1356 $output .= "<dd>\n";
d1e79aee 1357 // Check for references.
1358 if (is_array($value) && isset($value['devel_flag_reference'])) {
1359 $value['devel_flag_reference'] = TRUE;
1360 }
1361 // Check for references to prevent errors from recursions.
1362 if (is_array($value) && isset($value['devel_flag_reference']) && !$value['devel_flag_reference']) {
1363 $value['devel_flag_reference'] = FALSE;
1364 $output .= _devel_print_object($value, $prefix, $field);
3efc2536
JR
1365 }
1366 elseif (is_object($value)) {
d1e79aee 1367 $value->devel_flag_reference = FALSE;
3efc2536
JR
1368 $output .= _devel_print_object((array)$value, $prefix, $field, TRUE);
1369 }
1370 else {
1371 $value = is_bool($value) ? ($value ? 'TRUE' : 'FALSE') : $value;
1372 $output .= htmlspecialchars(print_r($value, TRUE)) ."\n";
1373 }
1374 $output .= "</dd></span>\n";
1375 }
1376 $output .= "</dl>\n";
0356e334 1377 }
0356e334
JC
1378 return $output;
1379}
9f6e6334
GK
1380
1381/**
9d450e49 1382 * Adds a table at the bottom of the page cataloguing data on all the database queries that were made to
1383 * generate the page.
9f6e6334 1384 */
9d450e49 1385function devel_query_table($queries, $counts) {
f2a633ed 1386 $version = devel_get_core_version(VERSION);
2b11d164 1387 $header = array ('ms', '#', 'where', 'ops', 'query', 'target');
9d450e49 1388 $i = 0;
2ad63d14 1389 $api = variable_get('devel_api_url', 'api.drupal.org');
9d450e49 1390 foreach ($queries as $query) {
efaea73d 1391 $function = !empty($query['caller']['class']) ? $query['caller']['class'] . '::' : '';
1392 $function .= $query['caller']['function'];
c7414be0 1393 $count = isset($counts[$query['query']]) ? $counts[$query['query']] : 0;
9d450e49 1394
c7414be0 1395 $diff = round($query['time'] * 1000, 2);
9d450e49 1396 if ($diff > variable_get('devel_execution', 5)) {
1397 $cell[$i][] = array ('data' => $diff, 'class' => 'marker');
1398 }
1399 else {
1400 $cell[$i][] = $diff;
1401 }
d7d07506 1402 $cell[$i][] = $count;
53aa3f47 1403 $cell[$i][] = l($function, "http://$api/api/function/$function/$version");
269cf33d 1404 $ops[] = l('P', '', array('attributes' => array('title' => 'Show placeholders', 'class' => 'dev-placeholders', 'qid' => $i)));
1405 $ops[] = l('A', '', array('attributes' => array('title' => 'Show arguments', 'class' => 'dev-arguments', 'qid' => $i)));
32d7f5c7 1406 // EXPLAIN only valid for select queries.
1407 if (strpos($query['query'], 'UPDATE') === FALSE && strpos($query['query'], 'INSERT') === FALSE && strpos($query['query'], 'DELETE') === FALSE) {
1408 $ops[] = l('E', '', array('attributes' => array('title' => 'Show EXPLAIN', 'class' => 'dev-explain', 'qid' => $i)));
1409 }
2b11d164 1410 $cell[$i][] = implode(' ', $ops);
1411 // 3 divs for each variation of the query. Last 2 are hidden by default.
1412 $placeholders = '<div class="dev-placeholders">' . check_plain($query['query']) . "</div>\n";
1413 $args = '<div class="dev-arguments" style="display: none;"></div>' . "\n";
1414 $explain = '<div class="dev-explain" style="display: none;"></div>' . "\n";
269cf33d 1415 $cell[$i][] = array(
1416 'id' => "devel-query-$i",
1417 'data' => $placeholders . $args . $explain,
1418 );
35c0c4c3 1419 $cell[$i][] = $query['target'];
9d450e49 1420 $i++;
2b11d164 1421 unset($diff, $count, $ops);
9d450e49 1422 }
dcd35dd9 1423 if (variable_get('devel_query_sort', DEVEL_QUERY_SORT_BY_SOURCE)) {
1424 usort($cell, '_devel_table_sort');
1425 }
fe87489f 1426 return theme('devel_querylog', array('header' => $header, 'rows' => $cell));
5d9df190 1427}
1428
f01aa6ec
DR
1429function theme_devel_querylog_row($variables) {
1430 $row = $variables['row'];
5d9df190 1431 $i = 0;
1432 $output = '';
1433 foreach ($row as $cell) {
1434 $i++;
1435
1436 if (is_array($cell)) {
1437 $data = !empty($cell['data']) ? $cell['data'] : '';
1438 unset($cell['data']);
1439 $attr = $cell;
1440 }
1441 else {
1442 $data = $cell;
1443 $attr = array();
1444 }
1445
1446 if (!empty($attr['class'])) {
1447 $attr['class'] .= " cell cell-$i";
1448 }
1449 else {
1450 $attr['class'] = "cell cell-$i";
1451 }
1452 $attr = drupal_attributes($attr);
1453
1454 $output .= "<div $attr>$data</div>";
1455 }
1456 return $output;
1457}
1458
fe87489f 1459function theme_devel_querylog($variables) {
1460 $header = $variables['header'];
1461 $rows = $variables['rows'];
5d9df190 1462 $output = '';
1463 if (!empty($header)) {
1464 $output .= "<div class='devel-querylog devel-querylog-header clear-block'>";
f01aa6ec 1465 $output .= theme('devel_querylog_row', array('row' => $header));
5d9df190 1466 $output .= "</div>";
1467 }
1468 if (!empty($rows)) {
1469 $i = 0;
1470 foreach ($rows as $row) {
1471 $i++;
1472 $zebra = ($i % 2) == 0 ? 'even' : 'odd';
1473 $output .= "<div class='devel-querylog devel-querylog-$zebra clear-block'>";
f01aa6ec 1474 $output .= theme('devel_querylog_row', array('row' => $row));
5d9df190 1475 $output .= "</div>";
1476 }
1477 }
1478 return $output;
9f6e6334
GK
1479}
1480
dcd35dd9 1481function _devel_table_sort($a, $b) {
1234041c
YC
1482 $a = is_array($a[0]) ? $a[0]['data'] : $a[0];
1483 $b = is_array($b[0]) ? $b[0]['data'] : $b[0];
d5e87c9b 1484 if ($a < $b) {
7a2bcf56 1485 return 1;
1486 }
d5e87c9b 1487 if ($a > $b) {
7a2bcf56 1488 return -1;
1489 }
1234041c 1490 return 0;
dcd35dd9 1491}
1492
9f6e6334 1493/**
9d450e49 1494 * Displays page execution time at the bottom of the page.
9f6e6334 1495 */
9d450e49 1496function devel_timer() {
1497 $time = timer_read('page');
683a91e6 1498 return t_safe(' Page execution time was @time ms.', array('@time' => $time));
9d450e49 1499}
1500
8637423e 1501// An alias for drupal_debug().
1502function dd($data, $label = NULL) {
1503 return drupal_debug($data, $label);
1504}
1505
1506// Log any variable to a drupal_debug.log in the site's temp directory.
1507// See http://drupal.org/node/314112
1508function drupal_debug($data, $label = NULL) {
1509 ob_start();
1510 print_r($data);
1511 $string = ob_get_clean();
1512 if ($label) {
7a2bcf56 1513 $out = $label .': '. $string;
8637423e 1514 }
1515 else {
1516 $out = $string;
1517 }
1518 $out .= "\n";
8b832e7b 1519
8637423e 1520 // The temp directory does vary across multiple simpletest instances.
87521fef 1521 $file = 'temporary://drupal_debug.txt';
8637423e 1522 if (file_put_contents($file, $out, FILE_APPEND) === FALSE) {
1523 drupal_set_message(t('The file could not be written.'), 'error');
1524 return FALSE;
1525 }
1526}
1527
9d450e49 1528/**
976f7b43 1529 * Prints the arguments passed into the current function
9d450e49 1530 */
8e015c96 1531function dargs($always = TRUE) {
1532 static $printed;
1533 if ($always || !$printed) {
1534 $bt = debug_backtrace();
976f7b43 1535 print kdevel_print_object($bt[1]['args']);
8e015c96 1536 $printed = TRUE;
1537 }
1234041c 1538}
8e015c96 1539
1540/**
e0361a6f 1541 * Print a SQL string from a DBTNG Query object. Includes quoted arguments.
1542 *
1543 * @param $query
1544 * A Query object.
a581ba4f 1545 * @param $return
1546 * Whether to return or print the string. Default to FALSE.
1547 * @param $name
1548 * Optional name for identifying the output.
e0361a6f 1549 */
9b2507f9 1550function dpq($query, $return = FALSE, $name = NULL) {
e0361a6f 1551 if (user_access('access devel information')) {
cbec8f6a 1552 $query->preExecute();
e0361a6f 1553 $sql = (string) $query;
1554 $quoted = array();
1555 $connection = Database::getConnection();
1556 foreach ((array)$query->arguments() as $key => $val) {
1557 $quoted[$key] = $connection->quote($val);
1558 }
1559 $sql = strtr($sql, $quoted);
9b2507f9 1560 if ($return) {
1561 return $sql;
1562 }
1563 else {
1564 dpm($sql, $name);
1565 }
e0361a6f 1566 }
1567}
1568
1569/**
8e015c96 1570 * Print a variable to the 'message' area of the page. Uses drupal_set_message()
1571 */
eb8f82fc 1572function dpm($input, $name = NULL) {
9d450e49 1573 if (user_access('access devel information')) {
83fa67d2 1574 $export = kprint_r($input, TRUE, $name);
8e015c96 1575 drupal_set_message($export);
9d450e49 1576 }
1577}
1578
1579/**
3631a73c 1580 * drupal_var_export() a variable to the 'message' area of the page. Uses drupal_set_message()
cc43a9ce 1581 */
eb8f82fc 1582function dvm($input, $name = NULL) {
cc43a9ce 1583 if (user_access('access devel information')) {
3631a73c 1584 $export = dprint_r($input, TRUE, $name, 'drupal_var_export', FALSE);
cc43a9ce 1585 drupal_set_message($export);
1586 }
1587}
eb8f82fc 1588
1589// legacy function that was poorly named. use dpm() instead, since the 'p' maps to 'print_r'
1590function dsm($input, $name = NULL) {
e8432427 1591 dpm($input, $name);
eb8f82fc 1592}
1593
cc43a9ce 1594/**
8e015c96 1595 * An alias for dprint_r(). Saves carpal tunnel syndrome.
1596 */
cc43a9ce 1597function dpr($input, $return = FALSE, $name = NULL) {
773d198e 1598 return dprint_r($input, $return, $name);
1599}
1600
1601/**
1602 * An alias for kprint_r(). Saves carpal tunnel syndrome.
1603 */
1604function kpr($input, $return = FALSE, $name = NULL) {
83fa67d2 1605 return kprint_r($input, $return, $name);
8e015c96 1606}
1607
1608/**
3631a73c 1609 * Like dpr, but uses drupal_var_export() instead
cc43a9ce 1610 */
1611function dvr($input, $return = FALSE, $name = NULL) {
3631a73c 1612 return dprint_r($input, $return, $name, 'drupal_var_export', FALSE);
cc43a9ce 1613}
1614
00399d8f 1615function kprint_r($input, $return = FALSE, $name = NULL, $function = 'print_r') {
47257719 1616 // We do not want to krumo() strings and integers and such
1617 if (merits_krumo($input)) {
00399d8f 1618 if (user_access('access devel information')) {
0acb632a 1619 return $return ? (isset($name) ? $name .' => ' : '') . krumo_ob($input) : krumo($input);
00399d8f 1620 }
1621 }
1622 else {
412e2c0d 1623 return dprint_r($input, $return, $name, $function);
00399d8f 1624 }
1625}
1626
cc43a9ce 1627/**
773d198e 1628 * Pretty-print a variable to the browser (no krumo).
9d450e49 1629 * Displays only for users with proper permissions. If
8e015c96 1630 * you want a string returned instead of a print, use the 2nd param.
9d450e49 1631 */
6115f4e2 1632function dprint_r($input, $return = FALSE, $name = NULL, $function = 'print_r', $check= TRUE) {
9d450e49 1633 if (user_access('access devel information')) {
1234041c
YC
1634 if ($name) {
1635 $name .= ' => ';
1636 }
3631a73c 1637 if ($function == 'drupal_var_export') {
1638 include_once DRUPAL_ROOT . '/includes/utility.inc';
1639 $output = drupal_var_export($input);
1640 }
1641 else {
1642 ob_start();
1643 $function($input);
1644 $output = ob_get_clean();
1645 }
08abd453 1646
6115f4e2 1647 if ($check) {
1648 $output = check_plain($output);
1649 }
eb8f82fc 1650 if (count($input, COUNT_RECURSIVE) > DEVEL_MIN_TEXTAREA) {
00399d8f 1651 // don't use fapi here because sometimes fapi will not be loaded
7a2bcf56 1652 $printed_value = "<textarea rows=30 style=\"width: 100%;\">\n". $name . $output .'</textarea>';
eb8f82fc 1653 }
1234041c 1654 else {
7a2bcf56 1655 $printed_value = '<pre>'. $name . $output .'</pre>';
1234041c 1656 }
8b832e7b 1657
9d450e49 1658 if ($return) {
3d5b4cb9 1659 return $printed_value;
1234041c 1660 }
cc43a9ce 1661 else {
3d5b4cb9 1662 print $printed_value;
9d450e49 1663 }
1664 }
1665}
1666
1667/**
bfa6d6f5 1668 * Prints a renderable array element to the screen using kprint_r().
1669 *
1670 * #pre_render and/or #post_render pass-through callback for kprint_r().
1671 *
1672 * @todo Investigate appending to #suffix.
1673 * @todo Investigate label derived from #id, #title, #name, and #theme.
1674 */
1675function devel_render() {
1676 $args = func_get_args();
1677 // #pre_render and #post_render pass the rendered $element as last argument.
1678 kprint_r(end($args));
1679 // #pre_render and #post_render expect the first argument to be returned.
1680 return reset($args);
1681}
1682
1683/**
0f79f53f 1684 * Print the function call stack.
9d450e49 1685 */
1686function ddebug_backtrace() {
1687 if (user_access('access devel information')) {
7c412e59 1688 $trace = debug_backtrace();
1689 array_shift($trace);
c073db1a 1690 $count = count($trace);
1691 foreach ($trace as $i => $call) {
1692 $key = ($count - $i) . ': ' . $call['function'];
1693 $rich_trace[$key] = $call;
12ee375b 1694 }
7c412e59 1695 if (has_krumo()) {
12ee375b 1696 print krumo($rich_trace);
9d450e49 1697 }
1698 else {
12ee375b 1699 dprint_r($rich_trace);
9d450e49 1700 }
1701 }
9f6e6334 1702}
61d85437 1703
2b11d164 1704// Delete all files in a dir. http://www.plus2net.com/php_tutorial/php-files-delete.php
1705function devel_empty_dir($dir) {
a1cc25d1 1706 foreach (new DirectoryIterator($dir) as $fileInfo) {
1707 unlink($fileInfo->getPathname());
2b11d164 1708 }
2b11d164 1709}
1710
b2de72f1 1711/*
1712 * migration related functions
1713 */
a30583d1 1714
b2de72f1 1715/**
12417900 1716 * Regenerate the data in node_comment_statistics table. Technique comes from
1717 * http://www.artfulsoftware.com/infotree/queries.php?&bw=1280#101
a30583d1 1718 *
b2de72f1 1719 * @return void
a30583d1 1720 **/
b2de72f1 1721function devel_rebuild_node_comment_statistics() {
092ef28f 1722 // Empty table
0b34858c 1723 db_truncate('node_comment_statistics')->execute();
83013f8f 1724
12417900 1725 // TODO: DBTNG. Ignore keyword is Mysql only? Is only used in the rare case when
1726 // two comments on the same node share same timestamp.
72f36a29 1727 $sql = "
12417900 1728 INSERT IGNORE INTO {node_comment_statistics} (nid, cid, last_comment_timestamp, last_comment_name, last_comment_uid, comment_count) (
1ad2161d 1729 SELECT c.nid, c.cid, c.created, c.name, c.uid, c2.comment_count FROM {comment} c
12417900 1730 JOIN (
1ad2161d 1731 SELECT c.nid, MAX(c.created) AS created, COUNT(*) AS comment_count FROM {comment} c WHERE status = 1 GROUP BY c.nid
1732 ) AS c2 ON c.nid = c2.nid AND c.created = c2.created
72f36a29 1733 )";
b58fa20c 1734 db_query($sql, array(':published' => COMMENT_PUBLISHED));
8b832e7b 1735
ee41df0e 1736 // Insert records into the node_comment_statistics for nodes that are missing.
1737 $query = db_select('node', 'n');
1738 $query->leftJoin('node_comment_statistics', 'ncs', 'ncs.nid = n.nid');
1739 $query->addField('n', 'changed', 'last_comment_timestamp');
1740 $query->addField('n', 'uid', 'last_comment_uid');
1741 $query->addField('n', 'nid');
1742 $query->addExpression('0', 'comment_count');
1743 $query->addExpression('NULL', 'last_comment_name');
1744 $query->isNull('ncs.comment_count');
1745
1746 db_insert('node_comment_statistics')
1747 ->from($query)
1748 ->execute();
8a210f69 1749}