Newer
Older
Gábor Hojtsy
committed
// $Id$
// This module holds functions useful for Drupal development.
// Please contribute!
Moshe Weitzman
committed
// Suggested profiling and stacktrace library from http://www.xdebug.org/index.php
Moshe Weitzman
committed
Moshe Weitzman
committed
define('DEVEL_QUERY_SORT_BY_SOURCE', 0);
define('DEVEL_QUERY_SORT_BY_DURATION', 1);
Moshe Weitzman
committed
define('DEVEL_ERROR_HANDLER_NONE', 0);
define('DEVEL_ERROR_HANDLER_STANDARD', 1);
define('DEVEL_ERROR_HANDLER_BACKTRACE', 2);
Moshe Weitzman
committed
define('DEVEL_MIN_TEXTAREA', 50);
function devel_help($section) {
switch ($section) {
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>';
return '<p>'. t('Here are the contents of your <code>$_SESSION</code> variable.') .'</p>';
$api = variable_get('devel_api_url', 'api.drupal.org');
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>';
case 'devel/reinstall':
return t('Warning - will delete your module tables and variables.');
Moshe Weitzman
committed
}
Moshe Weitzman
committed
* Implementationation of hook_menu().
function devel_menu() {
$items = array();
// Note: we can't dynamically append destination to querystring. Do so at theme layer. Fix in D7?
$items['devel/cache/clear'] = array(
'title' => 'Empty cache',
'page callback' => 'devel_cache_clear',
'description' => 'Clear the CSS cache and all database cache tables which store page, node, theme and variable caches.',
'access arguments' => array('access devel information'),
'file' => 'devel.pages.inc',
'menu_name' => 'devel',
$items['devel/reference'] = array(
'title' => 'Function reference',
'description' => 'View a list of currently defined user functions with documentation links.',
'page callback' => 'devel_function_reference',
'access arguments' => array('access devel information'),
'file' => 'devel.pages.inc',
'menu_name' => 'devel',
);
$items['devel/reinstall'] = array(
'title' => 'Reinstall modules',
Moshe Weitzman
committed
'page callback' => 'drupal_get_form',
'page arguments' => array('devel_reinstall'),
'description' => 'Run hook_uninstall() and then hook_install() for a given module.',
'access arguments' => array('access devel information'),
'file' => 'devel.pages.inc',
'menu_name' => 'devel',
$items['devel/source'] = array(
'title' => 'Display the PHP code of any file in your Drupal installation',
'page callback' => 'devel_display_source',
'access arguments' => array('display source code'),
'type' => MENU_CALLBACK,
'file' => 'devel.pages.inc',
'menu_name' => 'devel',
'description' => 'Rebuild menu based on hook_menu() and revert any custom changes. All menu items return to their default settings.',
'page arguments' => array('devel_menu_rebuild'),
'access arguments' => array('access devel information'),
'file' => 'devel.pages.inc',
'menu_name' => 'devel',
$items['devel/menu/item'] = array(
'title' => 'Menu item',
'description' => 'Details about a given menu item.',
'page callback' => 'devel_menu_item',
'access arguments' => array('access devel information'),
'file' => 'devel.pages.inc',
'menu_name' => 'devel',
);
$items['devel/variable'] = array(
'title' => 'Variable editor',
'description' => 'Edit and delete site variables.',
'page callback' => 'devel_variable_page',
'access arguments' => array('access devel information'),
'file' => 'devel.pages.inc',
'menu_name' => 'devel',
// we don't want the abbreviated version provided by status report
$items['devel/phpinfo'] = array(
'title' => 'PHPinfo()',
'description' => 'View your server\'s PHP configuration',
'page callback' => 'devel_phpinfo',
'access arguments' => array('access devel information'),
'file' => 'devel.pages.inc',
'menu_name' => 'devel',
);
$items['devel/php'] = array(
'title' => 'Execute PHP Code',
'description' => 'Execute some PHP code',
'page callback' => 'drupal_get_form',
'page arguments' => array('devel_execute_form'),
'access arguments' => array('execute php code'),
'file' => 'devel.pages.inc',
'menu_name' => 'devel',
);
$items['devel/theme/registry'] = array(
'title' => 'Theme registry',
'description' => 'View a list of available theme functions across the whole site.',
'page callback' => 'devel_theme_registry',
'access arguments' => array('access devel information'),
'file' => 'devel.pages.inc',
'menu_name' => 'devel',
$items['devel/entity/info'] = array(
'title' => 'Entity info',
'description' => 'View entity information across the whole site.',
'page callback' => 'devel_entity_info_page',
'access arguments' => array('access devel information'),
'file' => 'devel.pages.inc',
'menu_name' => 'devel',
);
Moshe Weitzman
committed
$items['devel/field/info'] = array(
'title' => 'Field info',
'description' => 'View fields information across the whole site.',
Yves Chedemois
committed
'page callback' => 'devel_field_info_page',
Moshe Weitzman
committed
'access arguments' => array('access devel information'),
'file' => 'devel.pages.inc',
Moshe Weitzman
committed
'menu_name' => 'devel',
);
Moshe Weitzman
committed
$items['devel/elements'] = array(
'title' => 'Hook_elements()',
'description' => 'View the active form/render elements for this site.',
Moshe Weitzman
committed
'page callback' => 'devel_elements_page',
'access arguments' => array('access devel information'),
'file' => 'devel.pages.inc',
'menu_name' => 'devel',
Moshe Weitzman
committed
);
$items['devel/variable/edit/%'] = array(
'title' => 'Variable editor',
'page callback' => 'drupal_get_form',
'page arguments' => array('devel_variable_edit', 3),
'access arguments' => array('access devel information'),
'type' => MENU_CALLBACK,
'file' => 'devel.pages.inc',
'menu_name' => 'devel',
);
$items['devel/session'] = array(
'title' => 'Session viewer',
'description' => 'List the contents of $_SESSION.',
'page callback' => 'devel_session',
'access arguments' => array('access devel information'),
'file' => 'devel.pages.inc',
'menu_name' => 'devel',
);
$items['devel/switch'] = array(
'title' => 'Switch user',
'page callback' => 'devel_switch_user',
'access arguments' => array('switch users'),
'type' => MENU_CALLBACK,
'file' => 'devel.pages.inc',
'menu_name' => 'devel',
Moshe Weitzman
committed
$items['devel/explain'] = array(
'title' => 'Explain query',
'page callback' => 'devel_querylog_explain',
'description' => 'Run an EXPLAIN on a given query. Used by query log',
'access arguments' => array('access devel information'),
'file' => 'devel.pages.inc',
Moshe Weitzman
committed
'type' => MENU_CALLBACK
);
$items['devel/arguments'] = array(
'title' => 'Arguments query',
'page callback' => 'devel_querylog_arguments',
'description' => 'Return a given query, with arguments instead of placeholders. Used by query log',
Moshe Weitzman
committed
'access arguments' => array('access devel information'),
'file' => 'devel.pages.inc',
Moshe Weitzman
committed
'type' => MENU_CALLBACK
);
$items['devel/run-cron'] = array(
'title' => 'Run cron',
'page callback' => 'system_run_cron',
'access arguments' => array('administer site configuration'),
'file' => 'system.admin.inc',
'file path' => drupal_get_path('module', 'system'),
'menu_name' => 'devel',
);
Moshe Weitzman
committed
// Duplicate path in 2 different menus. See http://drupal.org/node/601788.
$items['devel/settings'] = array(
'title' => 'Devel settings',
'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.',
'page callback' => 'drupal_get_form',
'page arguments' => array('devel_admin_settings'),
'access arguments' => array('administer site configuration'),
'file' => 'devel.admin.inc',
'menu_name' => 'devel',
Moshe Weitzman
committed
$items['admin/config/development/devel'] = array(
'title' => 'Devel settings',
'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.',
'page callback' => 'drupal_get_form',
'page arguments' => array('devel_admin_settings'),
'file' => 'devel.admin.inc',
Moshe Weitzman
committed
'access arguments' => array('administer site configuration'),
);
Moshe Weitzman
committed
$items['node/%node/devel'] = array(
'title' => 'Devel',
'page callback' => 'devel_load_object',
'page arguments' => array(1, 'node'),
'access arguments' => array('access devel information'),
'type' => MENU_LOCAL_TASK,
'file' => 'devel.pages.inc',
Moshe Weitzman
committed
'weight' => 100,
Moshe Weitzman
committed
$items['node/%node/devel/load'] = array(
'title' => 'Load',
'page callback' => 'devel_load_object',
'page arguments' => array(1, 'node'),
'access arguments' => array('access devel information'),
'file' => 'devel.pages.inc',
Moshe Weitzman
committed
'type' => MENU_DEFAULT_LOCAL_TASK,
);
Moshe Weitzman
committed
'title' => 'Render',
Moshe Weitzman
committed
'page callback' => 'devel_render_object',
'page arguments' => array('node', 1),
'access arguments' => array('access devel information'),
'file' => 'devel.pages.inc',
'type' => MENU_LOCAL_TASK,
Moshe Weitzman
committed
'weight' => 100,
Moshe Weitzman
committed
$items['comment/%comment/devel'] = array(
'title' => 'Devel',
'page callback' => 'devel_load_object',
'page arguments' => array(1, 'comment'),
'access arguments' => array('access devel information'),
'type' => MENU_LOCAL_TASK,
'file' => 'devel.pages.inc',
'weight' => 100,
);
Moshe Weitzman
committed
$items['comment/%comment/devel/load'] = array(
'title' => 'Load',
'page callback' => 'devel_load_object',
'page arguments' => array(1, 'comment'),
'access arguments' => array('access devel information'),
'type' => MENU_DEFAULT_LOCAL_TASK,
'file' => 'devel.pages.inc',
Moshe Weitzman
committed
);
$items['comment/%comment/devel/render'] = array(
Moshe Weitzman
committed
'title' => 'Render',
'page callback' => 'devel_render_object',
'page arguments' => array('comment', 1),
'access arguments' => array('access devel information'),
'type' => MENU_LOCAL_TASK,
'file' => 'devel.pages.inc',
'weight' => 100,
);
Moshe Weitzman
committed
$items['user/%user/devel'] = array(
'title' => 'Devel',
'page callback' => 'devel_load_object',
'page arguments' => array(1, 'user'),
'access arguments' => array('access devel information'),
'type' => MENU_LOCAL_TASK,
'file' => 'devel.pages.inc',
Moshe Weitzman
committed
'weight' => 100,
Moshe Weitzman
committed
$items['user/%user/devel/load'] = array(
'title' => 'Load',
'page callback' => 'devel_load_object',
'page arguments' => array(1, 'user'),
'access arguments' => array('access devel information'),
'file' => 'devel.pages.inc',
Moshe Weitzman
committed
'type' => MENU_DEFAULT_LOCAL_TASK,
);
Moshe Weitzman
committed
'title' => 'Render',
Moshe Weitzman
committed
'page callback' => 'devel_render_object',
'page arguments' => array('user', 1),
'access arguments' => array('access devel information'),
'file' => 'devel.pages.inc',
Moshe Weitzman
committed
'type' => MENU_LOCAL_TASK,
Moshe Weitzman
committed
'weight' => 100,
Moshe Weitzman
committed
);
Moshe Weitzman
committed
$items['taxonomy/term/%taxonomy_term/devel'] = array(
'title' => 'Devel',
Moshe Weitzman
committed
'page callback' => 'devel_load_object',
'page arguments' => array(2, 'term'),
'access arguments' => array('access devel information'),
'file' => 'devel.pages.inc',
Moshe Weitzman
committed
'type' => MENU_LOCAL_TASK,
'weight' => 100,
);
Moshe Weitzman
committed
$items['taxonomy/term/%taxonomy_term/devel/load'] = array(
'title' => 'Load',
'page callback' => 'devel_load_object',
'page arguments' => array(2, 'term'),
'access arguments' => array('access devel information'),
'file' => 'devel.pages.inc',
Moshe Weitzman
committed
'type' => MENU_DEFAULT_LOCAL_TASK,
);
Moshe Weitzman
committed
$items['taxonomy/term/%taxonomy_term/devel/render'] = array(
Moshe Weitzman
committed
'title' => 'Render',
Moshe Weitzman
committed
'page callback' => 'devel_render_term',
'page arguments' => array(2),
'access arguments' => array('access devel information'),
'type' => MENU_LOCAL_TASK,
'file' => 'devel.pages.inc',
Moshe Weitzman
committed
'weight' => 100,
);
return $items;
}
function devel_menu_need_destination() {
Moshe Weitzman
committed
return array('devel/cache/clear', 'devel/reinstall', 'devel/menu/reset', 'devel/variable', 'admin/reports/status/run-cron');
}
/**
* An implementation of hook_menu_link_alter(). Flag this link as needing alter at display time.
Moshe Weitzman
committed
* @see devel_translated_menu_link_alter().
function devel_menu_link_alter(&$item) {
if (in_array($item['link_path'], devel_menu_need_destination()) || $item['link_path'] == 'devel/menu/item') {
$item['options']['alter'] = TRUE;
}
}
Moshe Weitzman
committed
/**
* An implementation of hook_translated_menu_item_alter(). Append dynamic
Moshe Weitzman
committed
* querystring 'destination' to several of our own menu items.
Moshe Weitzman
committed
**/
function devel_translated_menu_link_alter(&$item) {
if (in_array($item['href'], devel_menu_need_destination())) {
Moshe Weitzman
committed
$item['localized_options']['query'] = drupal_get_destination();
}
elseif ($item['href'] == 'devel/menu/item') {
$item['localized_options']['query'] = array('path' => $_GET['q']);
}
Moshe Weitzman
committed
}
/**
* Implementation of hook_theme()
*/
Moshe Weitzman
committed
function devel_theme() {
return array(
Moshe Weitzman
committed
'devel_querylog' => array(
'variables' => array('header' => array(), 'rows' => array()),
Moshe Weitzman
committed
),
'devel_querylog_row' => array(
'variables' => array('row' => array()),
Moshe Weitzman
committed
),
);
if (user_access('access devel information')) {
devel_set_handler(variable_get('devel_error_handler', DEVEL_ERROR_HANDLER_STANDARD));
Moshe Weitzman
committed
// We want to include the class early so that anyone may call krumo() as needed. See http://krumo.sourceforge.net/
has_krumo();
Moshe Weitzman
committed
// See http://www.firephp.org/HQ/Install.htm
$path = NULL;
if (@include_once('fb.php')) {
// FirePHPCore is in include_path. Probably a PEAR installation.
$path = '';
}
elseif (module_exists('libraries')) {
// Support Libraries API - http://drupal.org/project/libraries
Moshe Weitzman
committed
$path = libraries_get_path('FirePHPCore') . '/lib/FirePHPCore/';
Moshe Weitzman
committed
}
else {
Moshe Weitzman
committed
$path = './'. drupal_get_path('module', 'devel') .'/FirePHPCore/lib/FirePHPCore/';
Moshe Weitzman
committed
}
Moshe Weitzman
committed
if (file_exists($path . 'fb.php')) {
include_once $path . 'fb.php';
Moshe Weitzman
committed
Moshe Weitzman
committed
// Add CSS for query log if should be displayed.
if (variable_get('devel_query_display', 0)) {
drupal_add_css(drupal_get_path('module', 'devel') .'/devel.css');
Moshe Weitzman
committed
drupal_add_js(drupal_get_path('module', 'devel'). '/devel.js');
Moshe Weitzman
committed
}
Moshe Weitzman
committed
}
Moshe Weitzman
committed
if (variable_get('devel_rebuild_theme_registry', FALSE)) {
drupal_theme_rebuild();
if (flood_is_allowed('devel_rebuild_registry_warning', 1)) {
flood_register_event('devel_rebuild_registry_warning');
if (!devel_silent() && user_access('access devel information')) {
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'))));
Moshe Weitzman
committed
}
}
}
function devel_set_message($msg, $type = NULL) {
$function = function_exists('drush_log') ? 'drush_log' : 'drupal_set_message';
$function($msg, $type);
function has_krumo() {
// see README.txt or just download from http://krumo.sourceforge.net/
@include_once DRUPAL_ROOT . '/' . drupal_get_path('module', 'devel') .'/krumo/class.krumo.php';
return function_exists('krumo') && !drupal_is_cli();
Moshe Weitzman
committed
/**
* Decide whether or not to print a debug variable using krumo().
*
Moshe Weitzman
committed
* @return boolean
*/
function merits_krumo($input) {
return (is_object($input) || is_array($input)) && has_krumo() && variable_get('devel_krumo_skin', '') != 'disabled';
Moshe Weitzman
committed
}
Moshe Weitzman
committed
/**
* Calls the http://www.firephp.org/ fb() function if it is found.
*
* @return void
*/
function dfb() {
if (function_exists('fb') && user_access('access devel information') && !headers_sent()) {
Moshe Weitzman
committed
$args = func_get_args();
call_user_func_array('fb', $args);
}
}
Moshe Weitzman
committed
/**
* Calls dfb() to output a backtrace.
*/
function dfbt($label) {
dfb($label, FirePHP::TRACE);
}
/**
* Implements hook_watchdog().
*/
function devel_watchdog(array $log_entry) {
Moshe Weitzman
committed
if (class_exists('FirePHP') && !drupal_is_cli()) {
Moshe Weitzman
committed
switch ($log_entry['severity']) {
Moshe Weitzman
committed
case WATCHDOG_ALERT:
case WATCHDOG_CRITICAL:
case WATCHDOG_ERROR:
$type = FirePHP::ERROR;
break;
case WATCHDOG_WARNING:
$type = FirePHP::WARN;
break;
case WATCHDOG_NOTICE:
case WATCHDOG_INFO:
$type = FirePHP::INFO;
break;
case WATCHDOG_DEBUG:
DEFAULT:
$type = FirePHP::LOG;
}
}
else {
$type = 'watchdog';
}
$function = function_exists('decode_entities') ? 'decode_entities' : 'html_entity_decode';
Moshe Weitzman
committed
$watchdog = array(
'type' => $log_entry['type'],
'message' => $function(strtr($log_entry['message'], (array)$log_entry['variables'])),
Moshe Weitzman
committed
);
if (isset($log_entry['link'])) {
$watchdog['link'] = $log_entry['link'];
}
dfb($watchdog, $type);
}
Moshe Weitzman
committed
function devel_set_handler($handler) {
switch ($handler) {
case DEVEL_ERROR_HANDLER_STANDARD:
// do nothing
break;
case DEVEL_ERROR_HANDLER_BACKTRACE:
if (has_krumo()) {
set_error_handler('backtrace_error_handler');
}
break;
case DEVEL_ERROR_HANDLER_NONE:
restore_error_handler();
break;
}
}
function devel_silent() {
// isset($_GET['q']) is needed when calling the front page. q is not set.
// Don't interfere with private files/images.
Moshe Weitzman
committed
function_exists('drupal_is_cli') && drupal_is_cli() ||
(isset($_SERVER['HTTP_USER_AGENT']) && strpos($_SERVER['HTTP_USER_AGENT'], 'ApacheBench') !== FALSE) ||
!empty($_REQUEST['XDEBUG_PROFILE']) ||
isset($GLOBALS['devel_shutdown']) ||
strstr($_SERVER['PHP_SELF'], 'update.php') ||
in_array($_GET['q'], array( 'admin/content/node-settings/rebuild')) ||
substr($_GET['q'], 0, strlen('system/files')) == 'system/files' ||
substr($_GET['q'], 0, strlen('batch')) == 'batch' ||
substr($_GET['q'], 0, strlen('file/ajax')) == 'file/ajax')
Moshe Weitzman
committed
function devel_xhprof_enable() {
if (extension_loaded('xhprof') && variable_get('devel_xhprof_enabled', FALSE)) {
if ($path = variable_get('devel_xhprof_directory', '')) {
include_once $path . '/xhprof_lib/utils/xhprof_lib.php';
include_once $path . '/xhprof_lib/utils/xhprof_runs.php';
// @todo: consider a variable per-flag instead.
xhprof_enable(XHPROF_FLAGS_CPU + XHPROF_FLAGS_MEMORY);
}
}
}
/**
* Implementation of hook_boot(). Runs even for cached pages.
*/
function devel_boot() {
Moshe Weitzman
committed
// Initialize XHProf.
devel_xhprof_enable();
Moshe Weitzman
committed
Moshe Weitzman
committed
if (!devel_silent()) {
if (variable_get('dev_mem', 0)) {
global $memory_init;
$memory_init = memory_get_usage();
}
Moshe Weitzman
committed
if (devel_query_enabled()) {
@include_once DRUPAL_ROOT . '/includes/database/log.inc';
Database::startLog('devel');;
}
Moshe Weitzman
committed
}
// We need user_access() in the shutdown function. make sure it gets loaded.
// Also prime the drupal_get_filename() static with user.module's location to
// avoid a stray query.
drupal_get_filename('module', 'user', 'modules/user/user.module');
drupal_load('module', 'user');
drupal_register_shutdown_function('devel_shutdown');
Moshe Weitzman
committed
function backtrace_error_handler($errno, $message, $filename, $line) {
Moshe Weitzman
committed
// Don't respond to the error if it was suppressed with a '@'
if (error_reporting() == 0) return;
Moshe Weitzman
committed
if ($errno & (E_ALL ^ E_NOTICE ^ E_DEPRECATED)) {
Moshe Weitzman
committed
// We can't use the PHP E_* constants here as not all versions of PHP have all
// the constants defined, so for consistency, we just use the numeric equivelant.
$types = array(
1 => 'error',
2 => 'warning',
4 => 'parse error',
8 => 'notice',
16 => 'core error',
32 => 'core warning',
64 => 'compile error',
128 => 'compile warning',
256 => 'user error',
512 => 'user warning',
1024 => 'user notice',
2048 => 'strict warning',
4096 => 'recoverable error',
8192 => 'deprecated',
16384 => 'user deprecated',
);
Moshe Weitzman
committed
$entry = $types[$errno] .': '. $message .' in '. $filename .' on line '. $line .'.';
if (variable_get('error_level', 1) == 1) {
$backtrace = debug_backtrace();
foreach ($backtrace as $call) {
$nicetrace[$call['function']] = $call;
Moshe Weitzman
committed
}
krumo($nicetrace);
Moshe Weitzman
committed
}
watchdog('php', '%message in %file on line %line.', array('%error' => $types[$errno], '%message' => $message, '%file' => $filename, '%line' => $line), WATCHDOG_ERROR);
return array(
'description' => t('View developer output like variable printouts, query log, etc.'),
'title' => t('Access developer information'),
),
'execute php code' => array(
'title' => t('Execute PHP code'),
'description' => t('Run arbitrary PHP from a block. Danger!'),
),
'switch users' => array(
'title' => t('Switch users'),
'description' => t('Become any user on the site with just a click. Danger!'),
),
'display source code' => array(
'title' => t('Display source code'),
'description' => t('View the site\'s php source code. Danger!'),
),
function devel_block_info() {
$blocks['execute_php'] = array(
'info' => t('Execute PHP'),
'cache' => DRUPAL_NO_CACHE,
);
$blocks['switch_user'] = array(
'info' => t('Switch user'),
'cache' => DRUPAL_NO_CACHE,
);
Moshe Weitzman
committed
return $blocks;
}
/**
* Implementation of hook_block_configure().
Moshe Weitzman
committed
*/
function devel_block_configure($delta) {
if ($delta == 'switch_user') {
$form['list_size'] = array(
'#type' => 'textfield',
'#title' => t('Number of users to display in the list'),
'#default_value' => variable_get('devel_switch_user_list_size', 10),
'#size' => '3',
'#maxlength' => '4',
);
$form['include_anon'] = array(
'#type' => 'checkbox',
'#title' => t('Include %anonymous', array('%anonymous' => format_username(drupal_anonymous_user()))),
'#default_value' => variable_get('devel_switch_user_include_anon', FALSE),
);
$form['show_form'] = array(
'#type' => 'checkbox',
'#title' => t('Allow entering any user name'),
'#default_value' => variable_get('devel_switch_user_show_form', TRUE),
);
return $form;
}
Moshe Weitzman
committed
}
function devel_block_save($delta, $edit = array()) {
if ($delta == 'switch_user') {
variable_set('devel_switch_user_list_size', $edit['list_size']);
variable_set('devel_switch_user_include_anon', $edit['include_anon']);
variable_set('devel_switch_user_show_form', $edit['show_form']);
Moshe Weitzman
committed
}
function devel_block_view($delta) {
$block = array();
switch ($delta) {
case 'switch_user':
Moshe Weitzman
committed
$block = devel_block_switch_user();
break;
case 'execute_php':
Moshe Weitzman
committed
if (user_access('execute php code')) {
$block['content'] = drupal_get_form('devel_execute_block_form');
Moshe Weitzman
committed
}
break;
Moshe Weitzman
committed
return $block;
$links = devel_switch_user_list();
Hans Salvisberg
committed
if (!empty($links) || user_access('switch users')) {
Moshe Weitzman
committed
$block['subject'] = t('Switch user');
$build['devel_links'] = array('#theme' => 'links', '#links' => $links);
if (variable_get('devel_switch_user_show_form', TRUE)) {
$build['devel_form'] = drupal_get_form('devel_switch_user_form');
}
return $block;
}
}
function devel_switch_user_list() {
global $user;
$links = array();
if (user_access('switch users')) {
$list_size = variable_get('devel_switch_user_list_size', 10);
if ($include_anon = ($user->uid && variable_get('devel_switch_user_include_anon', FALSE))) {
--$list_size;
}
// Try to find at least $list_size users that can switch.
$roles = user_roles(TRUE, 'switch users');
if (isset($roles[DRUPAL_AUTHENTICATED_RID])) {
// If authenticated users have this permission, just grab
// the last $list_size users, since there won't be records in
// {user_roles} and every user on the system can switch.
$accounts = db_query_range("SELECT DISTINCT u.uid, u.name, u.access FROM {users} u WHERE u.uid > 0 ORDER BY u.access DESC", 0, $list_size);
}
else {
$where = array('u.uid = 1');
if (count($roles)) {
$where[] = 'r.rid IN ('. implode(',', array_keys($roles)) .')';
}
$where_sql = implode(' OR ', $where);
$accounts = db_query_range("SELECT DISTINCT u.uid, u.name, u.access FROM {users} u LEFT JOIN {users_roles} r ON u.uid = r.uid WHERE $where_sql ORDER BY u.access DESC", 0, $list_size);
foreach ($accounts as $account) {
$links[$account->uid] = array(
Hans Salvisberg
committed
'title' => drupal_placeholder($account->name),
'href' => 'devel/switch/'. $account->name,
Moshe Weitzman
committed
'query' => $dest,
'attributes' => array('title' => t('This user can switch back.')),
'html' => TRUE,
'last_access' => $account->access,
Moshe Weitzman
committed
);
if ($num_links < $list_size) {
// If we don't have enough, add distinct uids until we hit $list_size.
$accounts = db_query_range('SELECT uid, name, access FROM {users} WHERE uid > 0 AND uid NOT IN ('. implode(',', array_keys($links)) .') ORDER BY access DESC', 0, $list_size - $num_links);
foreach ($accounts as $account) {
$links[$account->uid] = array(
'title' => $account->name ? $account->name : 'anon',
'href' => 'devel/switch/'. $account->name,
Moshe Weitzman
committed
'query' => $dest,
'attributes' => array('title' => t('Caution: this user will be unable to switch back.')),
'last_access' => $account->access,
Moshe Weitzman
committed
);
uasort($links, '_devel_switch_user_list_cmp');
if ($include_anon) {
$link = array(
'title' => check_plain(format_username(drupal_anonymous_user())),
'href' => 'devel/switch',
'query' => $dest,
'attributes' => array('title' => t('Caution: the anonymous user will be unable to switch back.')),
);
Hans Salvisberg
committed
if (user_access('switch users', drupal_anonymous_user())) {
$link['title'] = drupal_placeholder($link['title']);
$link['attributes'] = array('title' => t('This user can switch back.'));
$link['html'] = TRUE;
}
$links[] = $link;
}
return $links;
/**
* Comparison helper function for uasort() in devel_switch_user_list().
*
* Sorts the Switch User links by the user's last access timestamp.
*/
function _devel_switch_user_list_cmp($a, $b) {
return $b['last_access'] - $a['last_access'];
}
function devel_switch_user_form() {
$form['username'] = array(
'#type' => 'textfield',
'#description' => t('Enter username'),
'#autocomplete_path' => 'user/autocomplete',
'#maxlength' => USERNAME_MAX_LENGTH,
'#size' => 16,
);
$form['submit'] = array(
'#value' => t('Switch'),
);
return $form;
}
$version = devel_get_core_version(VERSION);
$form['function'] = array(
'#type' => 'textfield',
'#description' => t('Enter function name for api lookup'),
'#size' => 16,
'#maxlength' => 255,
);
$form['version'] = array('#type' => 'value', '#value' => $version);
$form['submit_button'] = array(
'#value' => t('Submit'),
);
return $form;
}
function devel_doc_function_form_submit($form, &$form_state) {
$version = $form_state['values']['version'];
$function = $form_state['values']['function'];
$api = variable_get('devel_api_url', 'api.drupal.org');
$form_state['redirect'] = "http://$api/api/function/$function/$version";
function devel_switch_user_form_validate($form, &$form_state) {
if (!$account = user_load_by_name($form_state['values']['username'])) {
form_set_error('username', t('Username not found'));
}
function devel_switch_user_form_submit($form, &$form_state) {
$form_state['redirect'] = 'devel/switch/'. $form_state['values']['username'];
}
Moshe Weitzman
committed
// An implementation of hook_drupal_goto_alter().
function devel_drupal_goto_alter($path, $options, $http_response_code) {
Moshe Weitzman
committed
global $user;
Moshe Weitzman
committed
if (isset($path) && !devel_silent()) {
Jonathan Chaffer
committed
// The page we are leaving is a drupal_goto(). Present a redirection page
// so that the developer can see the intermediate query log.
Moshe Weitzman
committed
// We don't want to load user module here, so keep function_exists() call.
if (isset($user) && function_exists('user_access') && user_access('access devel information') && variable_get('devel_redirect_page', 0)) {
Moshe Weitzman
committed
$destination = function_exists('url') ? url($path, $options) : $path;
$output = t_safe('<p>The user is being redirected to <a href="@destination">@destination</a>.</p>', array('@destination' => $destination));
Moshe Weitzman
committed
drupal_deliver_page($output);
Jonathan Chaffer
committed
// Don't allow the automatic redirect to happen.
exit();
}
$GLOBALS['devel_redirecting'] = TRUE;
Jonathan Chaffer
committed
}
Moshe Weitzman
committed
}
Moshe Weitzman
committed
/**
* See devel_start() which registers this function as a shutdown function.
Moshe Weitzman
committed
*/
function devel_shutdown() {
// Register the real shutdown function so it runs later than other shutdown functions.
drupal_register_shutdown_function('devel_shutdown_real');
Moshe Weitzman
committed
// Do this now so that we don't get cut off by drush_shutdown().
global $devel_run_id;
$devel_run_id = variable_get('devel_xhprof_enabled', FALSE) ? devel_shutdown_xhprof(): NULL;
if ($devel_run_id && function_exists('drush_log')) {
drush_log('xhprof link: ' . devel_xhprof_link($devel_run_id, 'url'), 'notice');
Moshe Weitzman
committed
}
Moshe Weitzman
committed
function devel_page_alter($page) {
if (variable_get('devel_page_alter', FALSE) && user_access('access devel information')) {
dpm($page, 'page');
}
}
Moshe Weitzman
committed
// AJAX render reponses sometimers are sent as text/html so we have to catch them here
// and disable our footer stuff.
function devel_ajax_render_alter() {
$GLOBALS['devel_shutdown'] = FALSE;
}
/**
* See devel_shutdown() which registers this function as a shutdown function. Displays developer information in the footer.
*/
function devel_shutdown_real() {
global $user;
Moshe Weitzman
committed
$output = $txt = '';
Moshe Weitzman
committed
// Set $GLOBALS['devel_shutdown'] = FALSE in order to supress the
// devel footer for a page. Not necessary if your page outputs any
// of the Content-type http headers tested below (e.g. text/xml,
// text/javascript, etc). This is is advised where applicable.
Moshe Weitzman
committed
if (!devel_silent() && !isset($GLOBALS['devel_shutdown']) && !isset($GLOBALS['devel_redirecting'])) {
Moshe Weitzman
committed
// Try not to break non html pages.
if (function_exists('drupal_get_http_header')) {
$header = drupal_get_http_header('content-type');
if ($header) {
$formats = array('xml', 'javascript', 'json', 'plain', 'image', 'application', 'csv', 'x-comma-separated-values');
foreach ($formats as $format) {
if (strstr($header, $format)) {
Moshe Weitzman
committed
}
Moshe Weitzman
committed
}
Gábor Hojtsy
committed
}
Moshe Weitzman
committed
if (isset($user) && user_access('access devel information')) {
$queries = Database::getLog('devel', 'default');
$output .= devel_shutdown_summary($queries);
$output .= devel_shutdown_query($queries);
if ($output) {
Moshe Weitzman
committed
// TODO: gzip this text if we are sending a gzip page. see drupal_page_header().
// For some reason, this is not actually printing for cached pages even though it gets executed
// and $output looks good.
print $output;
Moshe Weitzman
committed
}
Moshe Weitzman
committed
}
function devel_shutdown_summary($queries) {
$sum = 0;
$output = '';
Moshe Weitzman
committed
list($counts, $query_summary) = devel_query_summary($queries);
if (variable_get('devel_query_display', FALSE)) {
// Query log on.
Moshe Weitzman
committed
$output .= $query_summary;
$output .= t_safe(' Queries exceeding @threshold ms are <span class="marker">highlighted</span>.', array('@threshold' => variable_get('devel_execution', 5)));
Moshe Weitzman
committed
}
if (variable_get('dev_timer', 0)) {
$output .= devel_timer();
}
Moshe Weitzman
committed
if (variable_get('devel_xhprof_enabled', FALSE)) {
Moshe Weitzman
committed
$output .= ' ' . devel_xhprof_link($GLOBALS['devel_run_id']);
Moshe Weitzman
committed
}
$output .= devel_shutdown_memory();
Moshe Weitzman
committed
if ($output) {
return '<div class="dev-query">' . $output . '</div>';
}
Moshe Weitzman
committed
}
Moshe Weitzman
committed
function devel_shutdown_xhprof() {
Moshe Weitzman
committed
$namespace = variable_get('site_name', ''); // namespace for your application
$xhprof_data = xhprof_disable();
$xhprof_runs = new XHProfRuns_Default();
Moshe Weitzman
committed
return $xhprof_runs->save_run($xhprof_data, $namespace);
}
function devel_xhprof_link($run_id, $type = 'link') {
Moshe Weitzman
committed
// @todo: render results from within Drupal.
$xhprof_url = variable_get('devel_xhprof_url', '');
Moshe Weitzman
committed
$namespace = variable_get('site_name', ''); // namespace for your application
Moshe Weitzman
committed
if ($xhprof_url) {
Moshe Weitzman
committed
$url = $xhprof_url . "/index.php?run=$run_id&source=$namespace";
return $type == 'url' ? $url : t('<a href="@xhprof">XHProf output</a>. ', array('@xhprof' => $url));
Moshe Weitzman
committed
}
}
function devel_shutdown_memory() {
global $memory_init;
if (variable_get('dev_mem', FALSE)) {
$memory_shutdown = memory_get_usage();
$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));
$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>';
// theme() may not be available. not t() either.
return t_safe($msg, $args);
}
}
function devel_shutdown_query($queries) {
if (!empty($queries)) {
if (function_exists('theme_get_registry') && theme_get_registry()) {
// Safe to call theme('table).
list($counts, $query_summary) = devel_query_summary($queries);
// Save all queries to a file in temp dir. Retrieved via AJAX.
devel_query_put_contents($queries);
}
else {
}
}
Moshe Weitzman
committed
// Write the variables information to the a file. It will be retrieved on demand via AJAX.
function devel_query_put_contents($queries) {
$request_id = mt_rand(1, 1000000);
$path = "temporary://devel_querylog";
// Create the devel_querylog within the temp folder, if needed.
file_prepare_directory($path, FILE_CREATE_DIRECTORY);
// Occassionally wipe the querylog dir so that files don't accumulate.
if (mt_rand(1, 1000) == 401) {