Skip to content
Commits on Source (1)
FirePHPCore*
\ No newline at end of file
The master branch is empty. Please see 8.x-1.x and 7.x-1.x branches.
\ No newline at end of file
README.txt
==========
A module containing helper functions for Drupal developers and
inquisitive admins. This module can print a log of
all database queries for each page request at the bottom of each page. The
summary includes how many times each query was executed on a page, and how long
each query took.
It also offers
- a block for running custom PHP on a page
- a block for quickly accessing devel pages
- a block for masquerading as other users (useful for testing)
- reports memory usage at bottom of page
- more
This module is safe to use on a production site. Just be sure to only grant
'access development information' permission to developers.
Also a dpr() function is provided, which pretty prints arrays and strings.
Useful during development. Many other nice functions like dpm(), dvm().
AJAX developers in particular ought to install FirePHP Core from
http://www.firephp.org/ and put it in the devel directory.
This happens automatically when you enable via drush. You may also
use a drush command to download the library. If downloading by hand,
your path to fb.php should look like devel/FirePHPCore/lib/FirePHPCore/fb.php.
You can use svn checkout http://firephp.googlecode.com/svn/trunk/trunk/Libraries/FirePHPCore.
Then you can log php variables to the Firebug console. Is quite useful.
Included in this package is also:
- devel_node_access module which prints out the node_access records for a given node. Also offers hook_node_access_explain for all node access modules to implement. Handy.
- devel_generate.module which bulk creates nodes, users, comment, terms for development
Some nifty drush integration ships with devel and devel_generate. See drush help for details.
COMPATIBILITY NOTES
==================
- Modules that use AHAH may have incompatibility with the query log and other
footer info. Consider setting $GLOBALS['devel_shutdown'] = FALSE if you run into
any issues.
AUTHOR/MAINTAINER
======================
-moshe weitzman <weitzman at tejasa DOT com>
http://cyrve.com
Hans Salvisberg <drupal at salvisberg DOT com>
README
======
This module contains tools for developers using access control modules
to restrict access to some nodes. It is intended to help catch some
common mistakes and provide feedback to confirm that restricted nodes
are in fact visible only to the intended users.
Provides a summary page which queries the node_access table and
reports common mistakes such as the presence of Drupal's default entry
which grants all users read access to all nodes. Also reports the
presence of nodes not represented in node_access table. This may
occur when an access control module is installed after nodes have
already been created.
Provides a block which shows all node_access entries for the nodes
shown on a given page. This gives developers a quick check to see
that grants are provided as they should be. This block auto-enables
to the footer region. You may move it as desired.
If Views module is installed, allows browsing of nodes by realm,
including those nodes not in the node_access table (NULL realm).
WISHLIST
========
Things I'd like to see but haven't had time to do:
* Automatically solve common problems. I.e. delete the "all" realm
entry, and automatically save all nodes not in the node_access table.
* Nicer feedback indicating whether nodes are visible to the public or
not. I.e. use color coding or icons.
* Summary does not differentiate between view grants and other types
of grants. I personally use node_access only for view grants so I'm
not sure exactly what else it should show.
AUTHOR
======
Dave Cohen AKA yogadex on drupal.org
.dev-query, .dev-timer, .dev-memory-usage {
align: left;
direction: ltr;
padding-top: inherit;
}
.dev-query, .dev-timer, .dev-memory-usage table {
direction: ltr;
}
\ No newline at end of file
<?php
function devel_admin_settings() {
$form['queries'] = array('#type' => 'fieldset', '#title' => t('Query log'));
$description = t('Display a log of the database queries needed to generate the current page, and the execution time for each. Also, queries which are repeated during a single page view are summed in the # column, and printed in red since they are candidates for caching.');
if (!devel_is_compatible_optimizer()) {
$description = t('You must disable or upgrade the php Zend Optimizer extension in order to enable this feature. The minimum required version is 3.2.8. Earlier versions of Zend Optimizer are <a href="!url">horribly buggy and segfault your Apache</a> ... ', array('!url' => url('http://drupal.org/node/126098'))) . $description;
}
$form['queries']['devel_query_display'] = array('#type' => 'checkbox',
'#title' => t('Display query log'),
'#default_value' => variable_get('devel_query_display', 0),
'#description' => $description,
'#disabled' => !devel_is_compatible_optimizer(),
);
$form['queries']['settings'] = array(
'#type' => 'container',
'#states' => array(
// Hide the query log settings when not displaying query log.
'invisible' => array(
'input[name="devel_query_display"]' => array('checked' => FALSE),
),
),
);
$form['queries']['settings']['devel_query_sort'] = array('#type' => 'radios',
'#title' => t('Sort query log'),
'#default_value' => variable_get('devel_query_sort', DEVEL_QUERY_SORT_BY_SOURCE),
'#options' => array(t('by source'), t('by duration')),
'#description' => t('The query table can be sorted in the order that the queries were executed or by descending duration.'),
);
$form['queries']['settings']['devel_execution'] = array('#type' => 'textfield',
'#title' => t('Slow query highlighting'),
'#default_value' => variable_get('devel_execution', 5),
'#size' => 4,
'#maxlength' => 4,
'#description' => t('Enter an integer in milliseconds. Any query which takes longer than this many milliseconds will be highlighted in the query log. This indicates a possibly inefficient query, or a candidate for caching.'),
);
$form['xhprof'] = array(
'#type' => 'fieldset',
'#title' => 'XHProf',
'#description' => t('XHProf is a php extension which is essential for profiling your Drupal site. It pinpoints slow functions, and also memory hogging functions.'),
);
$description = extension_loaded('xhprof') ? t('Profile requests with the xhprof php extension.') : '<span class="warning">' . t('You must enable the <a href="!url">xhprof php extension</a> to use this feature.', array('!url' => url('http://techportal.ibuildings.com/2009/12/01/profiling-with-xhprof/'))) . '</span>';
$form['xhprof']['devel_xhprof_enabled'] = array(
'#type' => 'checkbox',
'#title' => t('Enable profiling of all page views and <a href="!drush">drush</a> requests.', array('!drush' => url('http://drush.ws'))),
'#default_value' => variable_get('devel_xhprof_enabled', FALSE),
'#description' => $description,
'#disabled' => !extension_loaded('xhprof'),
);
$form['xhprof']['settings'] = array(
'#type' => 'container',
'#states' => array(
'invisible' => array(
'input[name="devel_xhprof_enabled"]' => array('checked' => FALSE),
),
),
);
$form['xhprof']['settings']['devel_xhprof_directory'] = array(
'#type' => 'textfield',
'#title' => 'xhprof directory',
'#description' => t('Location of the xhprof source code on your system, usually somewhere in /usr/local/share or /usr/share, include the leading forward slash.'),
'#default_value' => variable_get('devel_xhprof_directory', ''),
'#states' => array(
'invisible' => array(
'input[name="devel_xhprof_enabled"]' => array('checked' => FALSE),
),
),
);
$form['xhprof']['settings']['devel_xhprof_url'] = array(
'#type' => 'textfield',
'#title' => 'XHProf URL',
'#description' => t('Path to the publically accessible xhprof_html - required to display profiler reports. You will need to set this up outside Drupal, for example at http://xhprof.localhost/xhprof_html'),
'#default_value' => variable_get('devel_xhprof_url', ''),
'#states' => array(
'invisible' => array(
'input[name="devel_xhprof_enabled"]' => array('checked' => FALSE),
),
),
);
$form['devel_api_url'] = array('#type' => 'textfield',
'#title' => t('API Site'),
'#default_value' => variable_get('devel_api_url', 'api.drupal.org'),
'#description' => t('The base URL for your developer documentation links. You might change this if you run <a href="!url">api.module</a> locally.', array('!url' => url('http://drupal.org/project/api'))));
$form['dev_timer'] = array('#type' => 'checkbox',
'#title' => t('Display page timer'),
'#default_value' => variable_get('dev_timer', 0),
'#description' => t('Display page execution time in the query log box.'),
);
$form['dev_mem'] = array('#type' => 'checkbox',
'#title' => t('Display memory usage'),
'#default_value' => variable_get('dev_mem', 0),
'#description' => t('Display how much memory is used to generate the current page. This will show memory usage when devel_init() is called and when devel_exit() is called.'),
);
$form['devel_redirect_page'] = array('#type' => 'checkbox',
'#title' => t('Display redirection page'),
'#default_value' => variable_get('devel_redirect_page', 0),
'#description' => t('When a module executes drupal_goto(), the query log and other developer information is lost. Enabling this setting presents an intermediate page to developers so that the log can be examined before continuing to the destination page.'),
);
$form['devel_page_alter'] = array('#type' => 'checkbox',
'#title' => t('Display $page array'),
'#default_value' => variable_get('devel_page_alter', FALSE),
'#description' => t('Display $page array from <a href="http://api.drupal.org/api/function/hook_page_alter/7">hook_page_alter()</a> in the messages area of each page.'),
);
$form['devel_error_handler'] = array('#type' => 'radios',
'#title' => t('Error handler'),
'#default_value' => variable_get('devel_error_handler', DEVEL_ERROR_HANDLER_STANDARD),
'#options' => array(DEVEL_ERROR_HANDLER_NONE => t('None'), DEVEL_ERROR_HANDLER_STANDARD => t('Standard drupal')),
'#description' => t('Choose an error handler for your site. <em>Backtrace</em> prints nice debug information when an error is noticed, and you <a href="@choose">choose to show errors on screen</a>. <strong>Backtrace requires the <a href="@krumo">krumo library</a></strong>. <em>None</em> is a good option when stepping through the site in your debugger.', array('@krumo' => url('http://krumo.sourceforge.net'), '@choose' => url('admin/config/development/logging'))),
);
if (has_krumo()) {
$form['devel_error_handler']['#options'][DEVEL_ERROR_HANDLER_BACKTRACE] = t('Backtrace');
}
$options = drupal_map_assoc(array('default', 'blue', 'green', 'orange', 'white', 'disabled'));
$form['devel_krumo_skin'] = array(
'#type' => 'radios',
'#title' => t('Krumo display'),
'#description' => t('Select a skin for your debug messages or select <em>disabled</em> to display object and array output in standard PHP format.'),
'#options' => $options,
'#default_value' => variable_get('devel_krumo_skin', 'default'),
);
$form['devel_rebuild_theme_registry'] = array(
'#type' => 'checkbox',
'#title' => t('Rebuild the theme registry on every page load'),
'#description' => t('While creating new templates and theme_ overrides the theme registry needs to be rebuilt.'),
'#default_value' => variable_get('devel_rebuild_theme_registry', FALSE),
);
$form['devel_use_uncompressed_jquery'] = array(
'#type' => 'checkbox',
'#title' => t('Use uncompressed jQuery'),
'#default_value' => variable_get('devel_use_uncompressed_jquery', FALSE),
'#description' => t("Use a human-readable version of jQuery instead of the minified version that ships with Drupal, to make JavaScript debugging easier."),
);
return system_settings_form($form);
}
.dev-query, .dev-timer, .dev-memory-usage {
padding: 1em;
}
.devel-obj-output .field {
color: red;
}
.devel-obj-output dd {
display: block;
}
/**
* Query summary
*/
div.dev-query {
font-size:11px;
background:#fff;
border-top:3px solid #ccc;
color:#333;
/*padding:.5em;*/
}
div.dev-query .marker {
color: #f00;
font-weight: bold;
}
/**
* Querylog
*/
div.devel-querylog {
color:#333;
border-bottom:1px solid #eee;
font-size:11px;
line-height:100%;
padding-left:30em;
padding-right:2em;
position:relative;
overflow:hidden;
}
div.devel-querylog .marker {
color: #f00;
font-weight: bold;
}
div.devel-querylog-header {
border-top:3px solid #ccc;
background:#fff;
font-weight:bold;
}
div.devel-querylog-even {
background:#fff;
}
div.devel-querylog-odd {
background:#f8f8f8;
}
div.devel-querylog div.cell {
overflow:hidden;
padding: 1em .5em;
}
div.devel-querylog div.cell-1 {
position:absolute;
left:0px;
width:4em;
}
div.devel-querylog div.cell-2 {
position:absolute;
left:4em;
width:3em;
}
div.devel-querylog div.cell-3 {
position:absolute;
left:6em;
width:19em;
}
div.devel-querylog div.cell-4 {
position:absolute;
left:26em;
width:4em;
}
div.devel-querylog-even div.cell-5,
div.devel-querylog-odd div.cell-5 {
/*max-height:18em;*/
font-family: 'Andale Mono', monospace;
}
div.devel-querylog div.cell-6 {
position:absolute;
right:0em;
top:0em;
/*width:9em;*/
}
<?php
/**
* @file
* Drush integration for the devel module.
*/
/**
* Implements hook_drush_command().
*/
function devel_drush_command() {
$items['devel-download'] = array(
'description' => dt('Downloads the FirePHP library from http://firephp.org/.'),
'arguments' => array(
'path' => dt('Optional. A path to the download folder. If omitted Drush will use the default location (sites/all/libraries/firephp).'),
),
);
$items['devel-reinstall'] = array(
'description' => dt('Disable, Uninstall, and Install a list of projects.'),
'arguments' => array(
'path' => dt('A space separated list of project names.'),
),
'aliases' => array('dre'),
);
$items['fn-hook'] = array(
'description' => 'List implementations of a given hook and explore source of specified one.',
'arguments' => array(
'hook' => 'The name of the hook to explore.'
),
'aliases' => array('fnh', 'hook'),
);
$items['fn-view'] = array(
'description' => 'Show the source of specified function or method.',
'arguments' => array(
'function' => 'The name of the function or method to view.',
),
'options' => array(
'--pipe' => 'Output just the filename of the function',
),
'examples' => array(
'fn-view drupal_set_breadcrumb' => 'View the source code for function "drupal_set_breadcrumb"',
'vi `drush --pipe fn-view user_access`' => 'Edit the file that contains the function "user_access"',
'fn-view NodeController::load' => 'View the source code for method load in the class NodeController'
),
'aliases' => array('fnv'),
);
$items['devel-token'] = array(
'description' => dt('List available tokens'),
'aliases' => array('token'),
'core' => array(7), // Remove once 3.0 is released.
);
return $items;
}
/**
* Implementation of hook_drush_help().
*/
function devel_drush_help($section) {
switch ($section) {
case 'drush:devel-reinstall':
return dt('Disable, Uninstall, and Install a list of projects.');
case 'drush:devel-download':
return dt("Downloads the FirePHP library from http://firephp.org/. Places it in the devel module directory. Skips download if library already present. This all happens automatically if you enable devel using drush.");
}
}
/**
* A command callback. This is faster than 3 separate bootstraps.
*/
function drush_devel_reinstall() {
$projects = func_get_args();
$args = array_merge(array('pm-disable'), $projects);
call_user_func_array('drush_invoke', $args);
$args = array_merge(array('pm-uninstall'), $projects);
call_user_func_array('drush_invoke', $args);
$args = array_merge(array('pm-enable'), $projects);
call_user_func_array('drush_invoke', $args);
}
/**
* A command callback.
*/
function drush_devel_download() {
$args = func_get_args();
if (isset($args[0])) {
$path = $args[0];
}
else {
$path = drush_get_context('DRUSH_DRUPAL_ROOT');
if (module_exists('libraries')) {
$path .= '/' . libraries_get_path('FirePHPCore') . '/FirePHPCore';
}
else {
$path .= '/'. drupal_get_path('module', 'devel') . '/FirePHPCore';
}
}
if (is_dir($path)) {
drush_log('FirePHP already present. No download required.', 'ok');
}
elseif (drush_shell_exec('svn checkout http://firephp.googlecode.com/svn/branches/Library-FirePHPCore-0.3 ' . $path)) {
drush_log(dt('FirePHP has been checked out via svn to @path.', array('@path' => $path)), 'success');
}
else {
drush_log(dt('Drush was unable to checkout FirePHP to @path.', array('@path' => $path)), 'error');
}
}
/**
* Implements drush_MODULE_post_COMMAND().
*/
function drush_devel_post_pm_enable() {
$modules = func_get_args();
if (in_array('devel', $modules) && !drush_get_option('skip')) {
drush_devel_download();
}
}
/**
* Command handler. Show hook implementations
*/
function drush_devel_fn_hook($hook) {
// Get implementations in the .install files as well.
include_once './includes/install.inc';
drupal_load_updates();
if ($hook_implementations = module_implements($hook)) {
if ($choice = drush_choice(array_combine($hook_implementations, $hook_implementations), 'Enter the number of the hook implementation you wish to view.')) {
return drush_devel_fn_view($choice . "_$hook");
}
}
else {
drush_log(dt('No implementations.'), 'ok');
}
}
/**
* Command handler. Show source code of specified function or method.
*/
function drush_devel_fn_view($function_name) {
// Get implementations in the .install files as well.
include_once './includes/install.inc';
drupal_load_updates();
if (strpos($function_name, '::') === FALSE) {
if (!function_exists($function_name)) {
return drush_set_error(dt('Function not found'));
}
$reflect = new ReflectionFunction($function_name);
}
else {
list($class, $method) = explode('::', $function_name);
if (!method_exists($class, $method)) {
return drush_set_error(dt('Method not found'));
}
$reflect = new ReflectionMethod($class, $method);
}
$func_info = array('!file' => $reflect->getFileName(), '!startline' => $reflect->getStartLine(), '!endline' => $reflect->getEndLine());
//drush_print_pipe(dt("!file -line !startline", $func_info));
drush_print_pipe($reflect->getFileName());
drush_print(dt("// file: !file, lines !startline-!endline", $func_info));
_drush_devel_print_function($reflect->getFileName(), $reflect->getStartLine(), $reflect->getEndLine());
}
/**
* Command callback. List available tokens.
*/
function drush_devel_token() {
$rows[] = array(dt('Group'), dt('Token'), dt('Name'));
$all = token_info();
foreach ($all['tokens'] as $group => $tokens) {
foreach ($tokens as $key => $token) {
$rows[] = array($group, $key, $token['name']);
}
}
drush_print_table($rows, TRUE);
}
/**
* Print the specified function, including any
* doxygen-style comments that come before it.
*/
function _drush_devel_print_function($file, $start_line, $end_line) {
$line_num = 0;
$doxygen = NULL;
$fp = fopen( $file, 'r' );
while (!feof($fp) && ($line_num < ($start_line - 1))) {
$line = fgets($fp);
++$line_num;
if (substr($line,0,3) == '/**') {
$doxygen = $line;
}
elseif (isset($doxygen)) {
$doxygen .= $line;
if ($line_num + 1 == $start_line) {
drush_print(rtrim($doxygen));
}
if (strstr($line, '*/') !== FALSE) {
$doxygen = NULL;
}
}
}
while (!feof($fp) && ($line_num < $end_line)) {
$line = fgets($fp);
++$line_num;
drush_print(rtrim($line));
}
}
name = Devel
description = Various blocks, pages, and functions for developers.
package = Development
core = 7.x
configure = admin/config/development/devel
tags[] = developer
<?php
/**
* Implements hook_enable().
*/
function devel_enable() {
$t = get_t();
// New module weights in core: put devel as the very last in the chain.
db_update('system')
->fields(array(
'weight' => 88,
))
->condition('type', 'module')
->condition('name', 'devel')
->execute();
// Create a custom menu, if Menu module is enabled.
// @see devel_modules_installed()
if (module_exists('menu')) {
$menu = array(
'menu_name' => 'devel',
'title' => $t('Development'),
'description' => $t('Development link'),
);
menu_save($menu);
}
}
/**
* Implements hook_uninstall().
*/
function devel_uninstall() {
variable_del('devel_form_weights');
variable_del('devel_execution');
variable_del('dev_timer');
variable_del('devel_query_display');
variable_del('devel_redirect_page');
variable_del('devel_api_url');
variable_del('dev_mem');
variable_del('devel_error_handler');
variable_del('devel_switch_user_list_size');
variable_del('devel_switch_user_include_anon');
variable_del('devel_switch_user_show_form');
// Delete the development menu.
if (module_exists('menu')) {
if ($devel_menu = menu_load('devel')) {
menu_delete($devel_menu);
}
}
}
/**
* Remove feature for storing queries. Cleanup deprecated tables and variables.
*/
function devel_update_7000() {
db_drop_table('devel_queries');
db_drop_table('devel_times');
}
/**
* Rebuild the menus since everything is defined by devel_menu().
*/
function devel_update_7001() {
db_delete('menu_links')
->condition('module', 'devel')
->execute();
variable_set('menu_rebuild_needed', TRUE);
return t('Devel module menu links will be rebuilt.');
}
/**
* Remove deprecated variables - dev_query, devel_code_coverage
*/
function devel_update_7002() {
variable_del('dev_query'); // Sad trombone. http://drupalcode.org/viewvc/drupal/drupal/includes/database.mysql.inc?revision=1.2&view=markup
variable_del('devel_code_coverage');
}
/**
* As per issue #813132: change schablon.com to white for krumo.
*/
function devel_update_7003() {
if (variable_get('devel_krumo_skin', 'white') == 'schablon.com') {
variable_set('devel_krumo_skin', 'white');
}
}
(function ($) {
// Explain link in query log
Drupal.behaviors.devel_explain = {
attach: function() {
$('a.dev-explain').click(function () {
qid = $(this).attr("qid");
cell = $('#devel-query-' + qid);
$('.dev-explain', cell).load(Drupal.settings.basePath + '?q=devel/explain/' + Drupal.settings.devel.request_id + '/' + qid).show();
$('.dev-placeholders', cell).hide();
$('.dev-arguments', cell).hide();
return false;
});
}
}
// Arguments link in query log
Drupal.behaviors.devel_arguments = {
attach: function() {
$('a.dev-arguments').click(function () {
qid = $(this).attr("qid");
cell = $('#devel-query-' + qid);
$('.dev-arguments', cell).load(Drupal.settings.basePath + '?q=devel/arguments/' + Drupal.settings.devel.request_id + '/' + qid).show();
$('.dev-placeholders', cell).hide();
$('.dev-explain', cell).hide();
return false;
});
}
}
// Placeholders link in query log
Drupal.behaviors.devel_placeholders = {
attach: function() {
$('a.dev-placeholders').click(function () {
qid = $(this).attr("qid");
cell = $('#devel-query-' + qid);
$('.dev-explain', cell).hide();
$('.dev-arguments', cell).hide();
$('.dev-placeholders', cell).show();
return false;
});
}
}
})(jQuery);
<?php
// This module holds functions useful for Drupal development.
// Please contribute!
// Suggested profiling and stacktrace library from http://www.xdebug.org/index.php
define('DEVEL_QUERY_SORT_BY_SOURCE', 0);
define('DEVEL_QUERY_SORT_BY_DURATION', 1);
define('DEVEL_ERROR_HANDLER_NONE', 0);
define('DEVEL_ERROR_HANDLER_STANDARD', 1);
define('DEVEL_ERROR_HANDLER_BACKTRACE', 2);
define('DEVEL_MIN_TEXTAREA', 50);
/**
* Implementation of hook_help().
*/
function devel_help($section) {
switch ($section) {
case 'devel/reference':
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>';
case 'devel/session':
return '<p>'. t('Here are the contents of your <code>$_SESSION</code> variable.') .'</p>';
case 'devel/variable':
$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.');
}
}
/**
* Implements hook_modules_installed().
*
* @see devel_install()
*/
function devel_modules_installed($modules) {
if (in_array('menu', $modules)) {
$menu = array(
'menu_name' => 'devel',
'title' => t('Development'),
'description' => t('Development link'),
);
menu_save($menu);
}
}
/**
* Implements hook_menu().
*/
function devel_menu() {
// 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',
'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/menu/reset'] = array(
'title' => 'Rebuild menus',
'description' => 'Rebuild menu based on hook_menu() and revert any custom changes. All menu items return to their default settings.',
'page callback' => 'drupal_get_form',
'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',
);
$items['devel/field/info'] = array(
'title' => 'Field info',
'description' => 'View fields information across the whole site.',
'page callback' => 'devel_field_info_page',
'access arguments' => array('access devel information'),
'file' => 'devel.pages.inc',
'menu_name' => 'devel',
);
$items['devel/elements'] = array(
'title' => 'Hook_elements()',
'description' => 'View the active form/render elements for this site.',
'page callback' => 'devel_elements_page',
'access arguments' => array('access devel information'),
'file' => 'devel.pages.inc',
'menu_name' => 'devel',
);
$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',
);
$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',
'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',
'access arguments' => array('access devel information'),
'file' => 'devel.pages.inc',
'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',
);
// 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',
);
$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',
'access arguments' => array('administer site configuration'),
);
$items['node/%node/devel'] = array(
'title' => 'Devel',
'page callback' => 'devel_load_object',
'page arguments' => array('node', 1),
'access arguments' => array('access devel information'),
'type' => MENU_LOCAL_TASK,
'file' => 'devel.pages.inc',
'weight' => 100,
);
$items['node/%node/devel/load'] = array(
'title' => 'Load',
'type' => MENU_DEFAULT_LOCAL_TASK,
);
$items['node/%node/devel/render'] = array(
'title' => 'Render',
'page callback' => 'devel_render_object',
'page arguments' => array('node', 1),
'access arguments' => array('access devel information'),
'file' => 'devel.pages.inc',
'type' => MENU_LOCAL_TASK,
'weight' => 100,
);
$items['comment/%comment/devel'] = array(
'title' => 'Devel',
'page callback' => 'devel_load_object',
'page arguments' => array('comment', 1),
'access arguments' => array('access devel information'),
'type' => MENU_LOCAL_TASK,
'file' => 'devel.pages.inc',
'weight' => 100,
);
$items['comment/%comment/devel/load'] = array(
'title' => 'Load',
'type' => MENU_DEFAULT_LOCAL_TASK,
);
$items['comment/%comment/devel/render'] = array(
'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,
);
$items['user/%user/devel'] = array(
'title' => 'Devel',
'page callback' => 'devel_load_object',
'page arguments' => array('user', 1),
'access arguments' => array('access devel information'),
'type' => MENU_LOCAL_TASK,
'file' => 'devel.pages.inc',
'weight' => 100,
);
$items['user/%user/devel/load'] = array(
'title' => 'Load',
'type' => MENU_DEFAULT_LOCAL_TASK,
);
$items['user/%user/devel/render'] = array(
'title' => 'Render',
'page callback' => 'devel_render_object',
'page arguments' => array('user', 1),
'access arguments' => array('access devel information'),
'file' => 'devel.pages.inc',
'type' => MENU_LOCAL_TASK,
'weight' => 100,
);
$items['taxonomy/term/%taxonomy_term/devel'] = array(
'title' => 'Devel',
'page callback' => 'devel_load_object',
'page arguments' => array('taxonomy_term', 2, 'term'),
'access arguments' => array('access devel information'),
'file' => 'devel.pages.inc',
'type' => MENU_LOCAL_TASK,
'weight' => 100,
);
$items['taxonomy/term/%taxonomy_term/devel/load'] = array(
'title' => 'Load',
'type' => MENU_DEFAULT_LOCAL_TASK,
);
$items['taxonomy/term/%taxonomy_term/devel/render'] = array(
'title' => 'Render',
'page callback' => 'devel_render_object',
'page arguments' => array('taxonomy_term', 2, 'term'),
'access arguments' => array('access devel information'),
'type' => MENU_LOCAL_TASK,
'file' => 'devel.pages.inc',
'weight' => 100,
);
return $items;
}
/**
* Implements hook_admin_paths().
*/
function devel_admin_paths() {
$paths = array(
'devel/*' => TRUE,
'node/*/devel' => TRUE,
'node/*/devel/*' => TRUE,
'comment/*/devel' => TRUE,
'comment/*/devel/*' => TRUE,
'user/*/devel' => TRUE,
'user/*/devel/*' => TRUE,
'taxonomy/term/*/devel' => TRUE,
'taxonomy/term/*/devel/*' => TRUE,
);
return $paths;
}
function devel_menu_need_destination() {
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.
* This is more robust than setting alter in hook_menu().
* @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;
}
}
/**
* An implementation of hook_translated_menu_item_alter(). Append dynamic
* querystring 'destination' to several of our own menu items.
*
**/
function devel_translated_menu_link_alter(&$item) {
if (in_array($item['href'], devel_menu_need_destination())) {
$item['localized_options']['query'] = drupal_get_destination();
}
elseif ($item['href'] == 'devel/menu/item') {
$item['localized_options']['query'] = array('path' => $_GET['q']);
}
}
/**
* Implementation of hook_theme()
*/
function devel_theme() {
return array(
'devel_querylog' => array(
'variables' => array('header' => array(), 'rows' => array()),
),
'devel_querylog_row' => array(
'variables' => array('row' => array()),
),
);
}
/**
* Implementation of hook_init().
*/
function devel_init() {
if (!devel_silent()) {
if (user_access('access devel information')) {
devel_set_handler(variable_get('devel_error_handler', DEVEL_ERROR_HANDLER_STANDARD));
// We want to include the class early so that anyone may call krumo() as needed. See http://krumo.sourceforge.net/
has_krumo();
// 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
$firephp_path = libraries_get_path('FirePHPCore') . '/lib/FirePHPCore/';
$chromephp_path = libraries_get_path('chromephp');
}
else {
$firephp_path = './'. drupal_get_path('module', 'devel') .'/FirePHPCore/lib/FirePHPCore/';
$chromephp_path = './' . drupal_get_path('module', 'devel') .'/chromephp';
}
// include FirePHP if exists...
if (file_exists($firephp_path .'fb.php')) {
include_once $firephp_path .'fb.php';
include_once $firephp_path .'FirePHP.class.php';
}
// include ChromePHP if exists...
if (file_exists($chromephp_path . '/ChromePhp.php')) {
include_once $chromephp_path . '/ChromePhp.php';
}
// 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');
drupal_add_js(drupal_get_path('module', 'devel'). '/devel.js');
}
}
}
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'))));
}
}
}
}
function devel_set_message($msg, $type = NULL) {
$function = function_exists('drush_log') ? 'drush_log' : 'drupal_set_message';
$function($msg, $type);
}
// Return boolean. No need for cache here.
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();
}
/**
* Decide whether or not to print a debug variable using krumo().
*
* @param $input
* @return boolean
*/
function merits_krumo($input) {
return (is_object($input) || is_array($input)) && has_krumo() && variable_get('devel_krumo_skin', '') != 'disabled';
}
/**
* 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()) {
$args = func_get_args();
call_user_func_array('fb', $args);
}
}
/**
* Calls dfb() to output a backtrace.
*/
function dfbt($label) {
dfb($label, FirePHP::TRACE);
}
/**
* Wrapper for ChromePHP Class log method
*/
function dcp() {
if (class_exists('ChromePhp') && user_access('access devel information')) {
$args = func_get_args();
call_user_func_array(array('ChromePhp', 'log'), $args);
}
}
/**
* Implements hook_watchdog().
*/
function devel_watchdog(array $log_entry) {
if (class_exists('FirePHP') && !drupal_is_cli()) {
switch ($log_entry['severity']) {
case WATCHDOG_EMERGENCY:
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';
$watchdog = array(
'type' => $log_entry['type'],
'message' => $function(strtr($log_entry['message'], (array)$log_entry['variables'])),
);
if (isset($log_entry['link'])) {
$watchdog['link'] = $log_entry['link'];
}
dfb($watchdog, $type);
}
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.
return
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') ||
(isset($_GET['q']) && (
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')
);
}
function devel_xhprof_enable() {
if (devel_xhprof_is_enabled()) {
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);
}
}
}
function devel_xhprof_is_enabled() {
return extension_loaded('xhprof') && variable_get('devel_xhprof_enabled', FALSE);
}
/**
* Implementation of hook_boot(). Runs even for cached pages.
*/
function devel_boot() {
// Initialize XHProf.
devel_xhprof_enable();
if (!devel_silent()) {
if (variable_get('dev_mem', 0)) {
global $memory_init;
$memory_init = memory_get_usage();
}
if (devel_query_enabled()) {
@include_once DRUPAL_ROOT . '/includes/database/log.inc';
Database::startLog('devel');;
}
}
// 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');
}
function backtrace_error_handler($error_level, $message, $filename, $line, $context) {
// Hide stack trace and parameters from unqualified users.
if (!user_access('access devel information')) {
return _drupal_error_handler($error_level, $message, $filename, $line, $context);
}
// Don't respond to the error if it was suppressed with a '@'
if (error_reporting() == 0) {
return;
}
// Don't respond to warning caused by ourselves.
if (preg_match('#Cannot modify header information - headers already sent by \\([^\\)]*[/\\\\]devel[/\\\\]#', $message)) {
return;
}
if ($error_level & error_reporting()) {
// Only write each distinct NOTICE message once, as repeats do not give any
// further information and can choke the page output.
if ($error_level == E_NOTICE) {
static $written = array();
if (!empty($written[$line][$filename][$message])) {
return;
}
$written[$line][$filename][$message] = TRUE;
}
require_once DRUPAL_ROOT . '/includes/errors.inc';
$types = drupal_error_levels();
$type = $types[$error_level];
$backtrace = debug_backtrace();
array_shift($backtrace);
$variables = array('%error' => $type[0], '%message' => $message, '%function' => $backtrace[0]['function'] .'()', '%file' => $filename, '%line' => $line);
$counter = 0;
if (variable_get('error_level', 1) == 1) {
foreach ($backtrace as $call) {
$nicetrace[$call['function'] . '<span class="' . $counter++ . '" />'] = $call;
}
print t('%error: %message in %function (line %line of %file).', $variables) ." =&gt;\n";
krumo($nicetrace);
}
watchdog('php', '%error: %message in %function (line %line of %file).', $variables, $type[1]);
}
}
/**
* Implement hook_permission().
*/
function devel_permission() {
return array(
'access devel information' => array(
'description' => t('View developer output like variable printouts, query log, etc.'),
'title' => t('Access developer information'),
'restrict access' => TRUE,
),
'execute php code' => array(
'title' => t('Execute PHP code'),
'description' => t('Run arbitrary PHP from a block.'),
'restrict access' => TRUE,
),
'switch users' => array(
'title' => t('Switch users'),
'description' => t('Become any user on the site with just a click.'),
'restrict access' => TRUE,
),
'display source code' => array(
'title' => t('Display source code'),
'description' => t('View the site\'s php source code.'),
'restrict access' => TRUE,
),
);
}
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,
);
return $blocks;
}
/**
* Implementation of hook_block_configure().
*/
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;
}
}
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']);
}
}
function devel_block_view($delta) {
$block = array();
switch ($delta) {
case 'switch_user':
$block = devel_block_switch_user();
break;
case 'execute_php':
if (user_access('execute php code')) {
$block['content'] = drupal_get_form('devel_execute_block_form');
}
break;
}
return $block;
}
function devel_block_switch_user() {
$links = devel_switch_user_list();
if (!empty($links) || user_access('switch users')) {
$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');
}
$block['content'] = $build;
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;
}
$dest = drupal_get_destination();
// Try to find at least $list_size users that can switch.
// Inactive users are omitted from all of the following db selects.
$roles = user_roles(TRUE, 'switch users');
$query = db_select('users', 'u');
$query->addField('u', 'uid');
$query->addField('u', 'access');
$query->distinct();
$query->condition('u.uid', 0, '>');
$query->condition('u.status', 0, '>');
$query->orderBy('u.access', 'DESC');
$query->range(0, $list_size);
if (!isset($roles[DRUPAL_AUTHENTICATED_RID])) {
$query->leftJoin('users_roles', 'r', 'u.uid = r.uid');
$or_condition = db_or();
$or_condition->condition('u.uid', 1);
if (!empty($roles)) {
$or_condition->condition('r.rid', array_keys($roles), 'IN');
}
$query->condition($or_condition);
}
$uids = $query->execute()->fetchCol();
$accounts = user_load_multiple($uids);
foreach ($accounts as $account) {
$links[$account->uid] = array(
'title' => drupal_placeholder(format_username($account)),
'href' => 'devel/switch/'. $account->name,
'query' => $dest,
'attributes' => array('title' => t('This user can switch back.')),
'html' => TRUE,
'last_access' => $account->access,
);
}
$num_links = count($links);
if ($num_links < $list_size) {
// If we don't have enough, add distinct uids until we hit $list_size.
$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();
$accounts = user_load_multiple($uids);
foreach ($accounts as $account) {
$links[$account->uid] = array(
'title' => format_username($account),
'href' => 'devel/switch/'. $account->name,
'query' => $dest,
'attributes' => array('title' => t('Caution: this user will be unable to switch back.')),
'last_access' => $account->access,
);
}
uasort($links, '_devel_switch_user_list_cmp');
}
if ($include_anon) {
$link = array(
'title' => format_username(drupal_anonymous_user()),
'href' => 'devel/switch',
'query' => $dest,
'attributes' => array('title' => t('Caution: the anonymous user will be unable to switch back.')),
);
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(
'#type' => 'submit',
'#value' => t('Switch'),
);
return $form;
}
function devel_doc_function_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(
'#type' => 'submit',
'#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'];
}
/**
* Implements hook_drupal_goto_alter().
*/
function devel_drupal_goto_alter($path, $options, $http_response_code) {
global $user;
if (isset($path) && !devel_silent()) {
// The page we are leaving is a drupal_goto(). Present a redirection page
// so that the developer can see the intermediate query log.
// 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)) {
$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));
drupal_deliver_page($output);
// Don't allow the automatic redirect to happen.
exit();
}
else {
$GLOBALS['devel_redirecting'] = TRUE;
}
}
}
/**
* Implements hook_library_alter().
*/
function devel_library_alter(&$libraries, $module) {
// Use an uncompressed version of jQuery for debugging.
if ($module === 'system' && variable_get('devel_use_uncompressed_jquery', FALSE) && isset($libraries['jquery'])) {
// Make sure we're not changing the jQuery version used on the site.
if (version_compare($libraries['jquery']['version'], '1.4.4', '=')) {
$libraries['jquery']['js'] = array(
drupal_get_path('module', 'devel') . '/jquery-1.4.4-uncompressed.js' => array('weight' => JS_LIBRARY - 20),
);
}
else {
if (!devel_silent() && user_access('access devel information')) {
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'])));
}
}
}
}
/**
* See devel_start() which registers this function as a shutdown function.
*/
function devel_shutdown() {
// Register the real shutdown function so it runs later than other shutdown functions.
drupal_register_shutdown_function('devel_shutdown_real');
global $devel_run_id;
$devel_run_id = devel_xhprof_is_enabled() ? devel_shutdown_xhprof(): NULL;
if ($devel_run_id && function_exists('drush_log')) {
drush_log('xhprof link: ' . devel_xhprof_link($devel_run_id, 'url'), 'notice');
}
}
function devel_page_alter($page) {
if (variable_get('devel_page_alter', FALSE) && user_access('access devel information')) {
dpm($page, 'page');
}
}
// 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;
$output = $txt = '';
// 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.
if (!devel_silent() && !isset($GLOBALS['devel_shutdown']) && !isset($GLOBALS['devel_redirecting'])) {
// 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)) {
return;
}
}
}
}
if (isset($user) && user_access('access devel information')) {
$queries = (devel_query_enabled() ? Database::getLog('devel', 'default') : NULL);
$output .= devel_shutdown_summary($queries);
$output .= devel_shutdown_query($queries);
}
if ($output) {
// 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;
}
}
}
function devel_shutdown_summary($queries) {
$sum = 0;
$output = '';
list($counts, $query_summary) = devel_query_summary($queries);
if (variable_get('devel_query_display', FALSE)) {
// Query log on.
$output .= $query_summary;
$output .= t_safe(' Queries exceeding @threshold ms are <span class="marker">highlighted</span>.', array('@threshold' => variable_get('devel_execution', 5)));
}
if (variable_get('dev_timer', 0)) {
$output .= devel_timer();
}
if (devel_xhprof_is_enabled()) {
$output .= ' ' . devel_xhprof_link($GLOBALS['devel_run_id']);
}
$output .= devel_shutdown_memory();
if ($output) {
return '<div class="dev-query">' . $output . '</div>';
}
}
function devel_shutdown_xhprof() {
$namespace = variable_get('site_name', ''); // namespace for your application
$xhprof_data = xhprof_disable();
$xhprof_runs = new XHProfRuns_Default();
return $xhprof_runs->save_run($xhprof_data, $namespace);
}
function devel_xhprof_link($run_id, $type = 'link') {
// @todo: render results from within Drupal.
$xhprof_url = variable_get('devel_xhprof_url', '');
$namespace = variable_get('site_name', ''); // namespace for your application
if ($xhprof_url) {
$url = $xhprof_url . "/index.php?run=$run_id&source=$namespace";
return $type == 'url' ? $url : t('<a href="@xhprof">XHProf output</a>. ', array('@xhprof' => $url));
}
}
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);
$output = devel_query_table($queries, $counts);
// Save all queries to a file in temp dir. Retrieved via AJAX.
devel_query_put_contents($queries);
}
else {
$output = '</div>' . dprint_r($queries, TRUE);
}
return $output;
}
}
// 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) {
devel_empty_dir($path);
}
$path .= "/$request_id.txt";
$path = file_stream_wrapper_uri_normalize($path);
// Save queries as a json array. Suppress errors due to recursion ()
file_put_contents($path, @json_encode($queries));
$settings['devel'] = array(
// A random string that is sent to the browser. It enables the AJAX to retrieve queries from this request.
'request_id' => $request_id,
);
print '<script type="text/javascript">jQuery.extend(Drupal.settings, '. json_encode($settings) .");</script>\n";
}
function devel_query_enabled() {
return method_exists('Database', 'getLog') && variable_get('devel_query_display', FALSE);
}
function devel_query_summary($queries) {
if (variable_get('devel_query_display', FALSE) && is_array($queries)) {
$sum = 0;
foreach ($queries as $query) {
$text[] = $query['query'];
$sum += $query['time'];
}
$counts = array_count_values($text);
return array($counts, t_safe('Executed @queries queries in @time ms.', array('@queries' => count($queries), '@time' => round($sum * 1000, 2))));
}
}
function t_safe($string, $args) {
// get_t caused problems here with theme registry after changing on admin/build/modules. the theme_get_registry call is needed.
if (function_exists('t') && function_exists('theme_get_registry')) {
theme_get_registry();
return t($string, $args);
}
else {
strtr($string, $args);
}
}
function devel_get_core_version($version) {
$version_parts = explode('.', $version);
// Map from 4.7.10 -> 4.7
if ($version_parts[0] < 5) {
return $version_parts[0] .'.'. $version_parts[1];
}
// Map from 5.5 -> 5 or 6.0-beta2 -> 6
else {
return $version_parts[0];
}
}
// See http://drupal.org/node/126098
function devel_is_compatible_optimizer() {
ob_start();
phpinfo();
$info = ob_get_contents();
ob_end_clean();
// Match the Zend Optimizer version in the phpinfo information
$found = preg_match('/Zend&nbsp;Optimizer&nbsp;v([0-9])\.([0-9])\.([0-9])/', $info, $matches);
if ($matches) {
$major = $matches[1];
$minor = $matches[2];
$build = $matches[3];
if ($major >= 3) {
if ($minor >= 3) {
return TRUE;
}
elseif ($minor == 2 && $build >= 8) {
return TRUE;
}
else {
return FALSE;
}
}
else {
return FALSE;
}
}
else {
return TRUE;
}
}
/**
* Generates the execute block form.
*/
function devel_execute_block_form() {
$form['execute'] = array(
'#type' => 'fieldset',
'#title' => t('Execute PHP Code'),
'#collapsible' => TRUE,
'#collapsed' => (!isset($_SESSION['devel_execute_code'])),
);
$form['#submit'] = array('devel_execute_form_submit');
return array_merge_recursive($form, devel_execute_form());
}
/**
* Generates the execute form.
*/
function devel_execute_form() {
$form['execute']['code'] = array(
'#type' => 'textarea',
'#title' => t('PHP code to execute'),
'#description' => t('Enter some code. Do not use <code>&lt;?php ?&gt;</code> tags.'),
'#default_value' => (isset($_SESSION['devel_execute_code']) ? $_SESSION['devel_execute_code'] : ''),
'#rows' => 20,
);
$form['execute']['op'] = array('#type' => 'submit', '#value' => t('Execute'));
$form['#redirect'] = FALSE;
if (isset($_SESSION['devel_execute_code'])) {
unset($_SESSION['devel_execute_code']);
}
return $form;
}
/**
* Process PHP execute form submissions.
*/
function devel_execute_form_submit($form, &$form_state) {
ob_start();
print eval($form_state['values']['code']);
$_SESSION['devel_execute_code'] = $form_state['values']['code'];
dsm(ob_get_clean());
}
/**
* Switch from original user to another user and back.
* We don't call session_save_session() because we really want to change users. Usually unsafe!
*
* @param $name The username to switch to, or NULL to log out.
*/
function devel_switch_user($name = NULL) {
global $user;
if ($user->uid) {
module_invoke_all('user_logout', $user);
}
if (isset($name) && $account = user_load_by_name($name)) {
$old_uid = $user->uid;
$user = $account;
$user->timestamp = time() - 9999;
if (!$old_uid) {
// Switch from anonymous to authorized.
drupal_session_regenerate();
}
$edit = array();
user_module_invoke('login', $edit, $user);
}
elseif ($user->uid) {
session_destroy();
}
drupal_goto();
}
/**
* Print an object or array using either Krumo (if installed) or devel_print_object()
*
* @param $object
* array or object to print
* @param $prefix
* prefixing for output items
*/
function kdevel_print_object($object, $prefix = NULL) {
return has_krumo() ? krumo_ob($object) : devel_print_object($object, $prefix);
}
// Save krumo htlm using output buffering.
function krumo_ob($object) {
ob_start();
krumo($object);
$output = ob_get_contents();
ob_end_clean();
return $output;
}
/**
* Display an object or array
*
* @param $object
* the object or array
* @param $prefix
* prefix for the output items (example "$node->", "$user->", "$")
* @param $header
* set to FALSE to suppress the output of the h3
*/
function devel_print_object($object, $prefix = NULL, $header = TRUE) {
drupal_add_css(drupal_get_path('module', 'devel') .'/devel.css');
$output = '<div class="devel-obj-output">';
if ($header) {
$output .= '<h3>'. t('Display of !type !obj', array('!type' => str_replace(array('$', '->'), '', $prefix), '!obj' => gettype($object))) .'</h3>';
}
$output .= _devel_print_object($object, $prefix);
$output .= '</div>';
return $output;
}
/**
* Recursive (and therefore magical) function goes through an array or object and
* returns a nicely formatted listing of its contents.
*
* @param $obj
* array or object to recurse through
* @param $prefix
* prefix for the output items (example "$node->", "$user->", "$")
* @param $parents
* used by recursion
* @param $object
* used by recursion
* @return
* fomatted html
*
* @todo
* currently there are problems sending an array with a varname
*/
function _devel_print_object($obj, $prefix = NULL, $parents = NULL, $object = FALSE) {
static $root_type, $out_format;
// TODO: support objects with references. See http://drupal.org/node/234581.
if (isset($obj->view)) {
return;
}
if (!isset($root_type)) {
$root_type = gettype($obj);
if ($root_type == 'object') {
$object = TRUE;
}
}
if (is_object($obj)) {
$obj = (array)$obj;
}
if (is_array($obj)) {
$output = "<dl>\n";
foreach ($obj as $field => $value) {
if ($field == 'devel_flag_reference') {
continue;
}
if (!is_null($parents)) {
if ($object) {
$field = $parents .'->'. $field;
}
else {
if (is_int($field)) {
$field = $parents .'['. $field .']';
}
else {
$field = $parents .'[\''. $field .'\']';
}
}
}
$type = gettype($value);
$show_summary = TRUE;
$summary = NULL;
if ($show_summary) {
switch ($type) {
case 'string' :
case 'float' :
case 'integer' :
if (strlen($value) == 0) {
$summary = t("{empty}");
}
elseif (strlen($value) < 40) {
$summary = htmlspecialchars($value);
}
else {
$summary = format_plural(drupal_strlen($value), '1 character', '@count characters');
}
break;
case 'array' :
case 'object' :
$summary = format_plural(count((array)$value), '1 element', '@count elements');
break;
case 'boolean' :
$summary = $value ? t('TRUE') : t('FALSE');
break;
}
}
if (!is_null($summary)) {
$typesum = '('. $type .', <em>'. $summary .'</em>)';
}
else {
$typesum = '('. $type .')';
}
$output .= '<span class="devel-attr">';
$output .= "<dt><span class=\"field\">{$prefix}{$field}</span> $typesum</dt>\n";
$output .= "<dd>\n";
// Check for references.
if (is_array($value) && isset($value['devel_flag_reference'])) {
$value['devel_flag_reference'] = TRUE;
}
// Check for references to prevent errors from recursions.
if (is_array($value) && isset($value['devel_flag_reference']) && !$value['devel_flag_reference']) {
$value['devel_flag_reference'] = FALSE;
$output .= _devel_print_object($value, $prefix, $field);
}
elseif (is_object($value)) {
$value->devel_flag_reference = FALSE;
$output .= _devel_print_object((array)$value, $prefix, $field, TRUE);
}
else {
$value = is_bool($value) ? ($value ? 'TRUE' : 'FALSE') : $value;
$output .= htmlspecialchars(print_r($value, TRUE)) ."\n";
}
$output .= "</dd></span>\n";
}
$output .= "</dl>\n";
}
return $output;
}
/**
* Adds a table at the bottom of the page cataloguing data on all the database queries that were made to
* generate the page.
*/
function devel_query_table($queries, $counts) {
$version = devel_get_core_version(VERSION);
$header = array ('ms', '#', 'where', 'ops', 'query', 'target');
$i = 0;
$api = variable_get('devel_api_url', 'api.drupal.org');
foreach ($queries as $query) {
$function = !empty($query['caller']['class']) ? $query['caller']['class'] . '::' : '';
$function .= $query['caller']['function'];
$count = isset($counts[$query['query']]) ? $counts[$query['query']] : 0;
$diff = round($query['time'] * 1000, 2);
if ($diff > variable_get('devel_execution', 5)) {
$cell[$i][] = array ('data' => $diff, 'class' => 'marker');
}
else {
$cell[$i][] = $diff;
}
$cell[$i][] = $count;
$cell[$i][] = l($function, "http://$api/api/function/$function/$version");
$ops[] = l('P', '', array('attributes' => array('title' => 'Show placeholders', 'class' => 'dev-placeholders', 'qid' => $i)));
$ops[] = l('A', '', array('attributes' => array('title' => 'Show arguments', 'class' => 'dev-arguments', 'qid' => $i)));
// EXPLAIN only valid for select queries.
if (strpos($query['query'], 'UPDATE') === FALSE && strpos($query['query'], 'INSERT') === FALSE && strpos($query['query'], 'DELETE') === FALSE) {
$ops[] = l('E', '', array('attributes' => array('title' => 'Show EXPLAIN', 'class' => 'dev-explain', 'qid' => $i)));
}
$cell[$i][] = implode(' ', $ops);
// 3 divs for each variation of the query. Last 2 are hidden by default.
$placeholders = '<div class="dev-placeholders">' . check_plain($query['query']) . "</div>\n";
$args = '<div class="dev-arguments" style="display: none;"></div>' . "\n";
$explain = '<div class="dev-explain" style="display: none;"></div>' . "\n";
$cell[$i][] = array(
'id' => "devel-query-$i",
'data' => $placeholders . $args . $explain,
);
$cell[$i][] = $query['target'];
$i++;
unset($diff, $count, $ops);
}
if (variable_get('devel_query_sort', DEVEL_QUERY_SORT_BY_SOURCE)) {
usort($cell, '_devel_table_sort');
}
return theme('devel_querylog', array('header' => $header, 'rows' => $cell));
}
function theme_devel_querylog_row($variables) {
$row = $variables['row'];
$i = 0;
$output = '';
foreach ($row as $cell) {
$i++;
if (is_array($cell)) {
$data = !empty($cell['data']) ? $cell['data'] : '';
unset($cell['data']);
$attr = $cell;
}
else {
$data = $cell;
$attr = array();
}
if (!empty($attr['class'])) {
$attr['class'] .= " cell cell-$i";
}
else {
$attr['class'] = "cell cell-$i";
}
$attr = drupal_attributes($attr);
$output .= "<div $attr>$data</div>";
}
return $output;
}
function theme_devel_querylog($variables) {
$header = $variables['header'];
$rows = $variables['rows'];
$output = '';
if (!empty($header)) {
$output .= "<div class='devel-querylog devel-querylog-header clear-block'>";
$output .= theme('devel_querylog_row', array('row' => $header));
$output .= "</div>";
}
if (!empty($rows)) {
$i = 0;
foreach ($rows as $row) {
$i++;
$zebra = ($i % 2) == 0 ? 'even' : 'odd';
$output .= "<div class='devel-querylog devel-querylog-$zebra clear-block'>";
$output .= theme('devel_querylog_row', array('row' => $row));
$output .= "</div>";
}
}
return $output;
}
function _devel_table_sort($a, $b) {
$a = is_array($a[0]) ? $a[0]['data'] : $a[0];
$b = is_array($b[0]) ? $b[0]['data'] : $b[0];
if ($a < $b) {
return 1;
}
if ($a > $b) {
return -1;
}
return 0;
}
/**
* Displays page execution time at the bottom of the page.
*/
function devel_timer() {
$time = timer_read('page');
return t_safe(' Page execution time was @time ms.', array('@time' => $time));
}
// An alias for drupal_debug().
function dd($data, $label = NULL) {
return drupal_debug($data, $label);
}
// Log any variable to a drupal_debug.log in the site's temp directory.
// See http://drupal.org/node/314112
function drupal_debug($data, $label = NULL) {
ob_start();
print_r($data);
$string = ob_get_clean();
if ($label) {
$out = $label .': '. $string;
}
else {
$out = $string;
}
$out .= "\n";
// The temp directory does vary across multiple simpletest instances.
$file = 'temporary://drupal_debug.txt';
if (file_put_contents($file, $out, FILE_APPEND) === FALSE) {
drupal_set_message(t('The file could not be written.'), 'error');
return FALSE;
}
}
/**
* Prints the arguments passed into the current function
*/
function dargs($always = TRUE) {
static $printed;
if ($always || !$printed) {
$bt = debug_backtrace();
print kdevel_print_object($bt[1]['args']);
$printed = TRUE;
}
}
/**
* Print a SQL string from a DBTNG Query object. Includes quoted arguments.
*
* @param $query
* A Query object.
* @param $return
* Whether to return or print the string. Default to FALSE.
* @param $name
* Optional name for identifying the output.
*/
function dpq($query, $return = FALSE, $name = NULL) {
if (user_access('access devel information')) {
$query->preExecute();
$sql = (string) $query;
$quoted = array();
$connection = Database::getConnection();
foreach ((array)$query->arguments() as $key => $val) {
$quoted[$key] = $connection->quote($val);
}
$sql = strtr($sql, $quoted);
if ($return) {
return $sql;
}
else {
dpm($sql, $name);
}
}
}
/**
* Print a variable to the 'message' area of the page. Uses drupal_set_message()
*/
function dpm($input, $name = NULL) {
if (user_access('access devel information')) {
$export = kprint_r($input, TRUE, $name);
drupal_set_message($export);
}
}
/**
* drupal_var_export() a variable to the 'message' area of the page. Uses drupal_set_message()
*/
function dvm($input, $name = NULL) {
if (user_access('access devel information')) {
$export = dprint_r($input, TRUE, $name, 'drupal_var_export', FALSE);
drupal_set_message($export);
}
}
// legacy function that was poorly named. use dpm() instead, since the 'p' maps to 'print_r'
function dsm($input, $name = NULL) {
dpm($input, $name);
}
/**
* An alias for dprint_r(). Saves carpal tunnel syndrome.
*/
function dpr($input, $return = FALSE, $name = NULL) {
return dprint_r($input, $return, $name);
}
/**
* An alias for kprint_r(). Saves carpal tunnel syndrome.
*/
function kpr($input, $return = FALSE, $name = NULL) {
return kprint_r($input, $return, $name);
}
/**
* Like dpr, but uses drupal_var_export() instead
*/
function dvr($input, $return = FALSE, $name = NULL) {
return dprint_r($input, $return, $name, 'drupal_var_export', FALSE);
}
function kprint_r($input, $return = FALSE, $name = NULL, $function = 'print_r') {
// We do not want to krumo() strings and integers and such
if (merits_krumo($input)) {
if (user_access('access devel information')) {
return $return ? (isset($name) ? $name .' => ' : '') . krumo_ob($input) : krumo($input);
}
}
else {
return dprint_r($input, $return, $name, $function);
}
}
/**
* Pretty-print a variable to the browser (no krumo).
* Displays only for users with proper permissions. If
* you want a string returned instead of a print, use the 2nd param.
*/
function dprint_r($input, $return = FALSE, $name = NULL, $function = 'print_r', $check= TRUE) {
if (user_access('access devel information')) {
if ($name) {
$name .= ' => ';
}
if ($function == 'drupal_var_export') {
include_once DRUPAL_ROOT . '/includes/utility.inc';
$output = drupal_var_export($input);
}
else {
ob_start();
$function($input);
$output = ob_get_clean();
}
if ($check) {
$output = check_plain($output);
}
if (count($input, COUNT_RECURSIVE) > DEVEL_MIN_TEXTAREA) {
// don't use fapi here because sometimes fapi will not be loaded
$printed_value = "<textarea rows=30 style=\"width: 100%;\">\n". $name . $output .'</textarea>';
}
else {
$printed_value = '<pre>'. $name . $output .'</pre>';
}
if ($return) {
return $printed_value;
}
else {
print $printed_value;
}
}
}
/**
* Prints a renderable array element to the screen using kprint_r().
*
* #pre_render and/or #post_render pass-through callback for kprint_r().
*
* @todo Investigate appending to #suffix.
* @todo Investigate label derived from #id, #title, #name, and #theme.
*/
function devel_render() {
$args = func_get_args();
// #pre_render and #post_render pass the rendered $element as last argument.
kprint_r(end($args));
// #pre_render and #post_render expect the first argument to be returned.
return reset($args);
}
/**
* Print the function call stack.
*/
function ddebug_backtrace() {
if (user_access('access devel information')) {
$trace = debug_backtrace();
array_shift($trace);
$count = count($trace);
foreach ($trace as $i => $call) {
$key = ($count - $i) . ': ' . $call['function'];
$rich_trace[$key] = $call;
}
if (has_krumo()) {
print krumo($rich_trace);
}
else {
dprint_r($rich_trace);
}
}
}
// Delete all files in a dir. http://www.plus2net.com/php_tutorial/php-files-delete.php
function devel_empty_dir($dir) {
foreach (new DirectoryIterator($dir) as $fileInfo) {
unlink($fileInfo->getPathname());
}
}
/*
* migration related functions
*/
/**
* Regenerate the data in node_comment_statistics table. Technique comes from
* http://www.artfulsoftware.com/infotree/queries.php?&bw=1280#101
*
* @return void
**/
function devel_rebuild_node_comment_statistics() {
// Empty table
db_truncate('node_comment_statistics')->execute();
// TODO: DBTNG. Ignore keyword is Mysql only? Is only used in the rare case when
// two comments on the same node share same timestamp.
$sql = "
INSERT IGNORE INTO {node_comment_statistics} (nid, cid, last_comment_timestamp, last_comment_name, last_comment_uid, comment_count) (
SELECT c.nid, c.cid, c.created, c.name, c.uid, c2.comment_count FROM {comment} c
JOIN (
SELECT c.nid, MAX(c.created) AS created, COUNT(*) AS comment_count FROM {comment} c WHERE status = 1 GROUP BY c.nid
) AS c2 ON c.nid = c2.nid AND c.created = c2.created
)";
db_query($sql, array(':published' => COMMENT_PUBLISHED));
// Insert records into the node_comment_statistics for nodes that are missing.
$query = db_select('node', 'n');
$query->leftJoin('node_comment_statistics', 'ncs', 'ncs.nid = n.nid');
$query->addField('n', 'changed', 'last_comment_timestamp');
$query->addField('n', 'uid', 'last_comment_uid');
$query->addField('n', 'nid');
$query->addExpression('0', 'comment_count');
$query->addExpression('NULL', 'last_comment_name');
$query->isNull('ncs.comment_count');
db_insert('node_comment_statistics')
->from($query)
->execute();
}
<?php
// Menu callback that shows menu item.
function devel_menu_item() {
$item = menu_get_item($_GET['path']);
return kdevel_print_object($item);
}
/**
* Returns a list of all currently defined user functions in the current
* request lifecycle, with links their documentation.
*/
function devel_function_reference() {
$functions = get_defined_functions();
$version = devel_get_core_version(VERSION);
$ufunctions = $functions['user'];
sort($ufunctions);
$api = variable_get('devel_api_url', 'api.drupal.org');
foreach ($ufunctions as $function) {
$links[] = l($function, "http://$api/api/$version/function/$function");
}
return theme('item_list', array('items' => $links));
}
/**
* Menu callback; clears all caches, then redirects to the previous page.
*/
function devel_cache_clear() {
drupal_flush_all_caches();
drupal_set_message('Cache cleared.');
drupal_goto();
}
// A menu callback. Called by the AJAX link in query log.
function devel_querylog_explain($request_id, $qid) {
if (!is_numeric($request_id)) {
return MENU_ACCESS_DENIED;
}
$path = "temporary://devel_querylog/$request_id.txt";
$path = file_stream_wrapper_uri_normalize($path);
$queries = json_decode(file_get_contents($path));
$query = $queries[$qid];
$result = db_query('EXPLAIN ' . $query->query, (array)$query->args)->fetchAllAssoc('table');
$i = 1;
foreach ($result as $row) {
$row = (array)$row;
if ($i == 1) {
$header = array_keys($row);
}
$rows[] = array_values($row);
$i++;
}
// Print and return nothing thus avoiding page wrapper.
$output = theme('table', array('header' => $header, 'rows' => $rows));
print $output;
$GLOBALS['devel_shutdown'] = FALSE;
}
// A menu callback. Called by the AJAX link in query log.
function devel_querylog_arguments($request_id, $qid) {
if (!is_numeric($request_id)) {
return MENU_ACCESS_DENIED;
}
$path = "temporary://devel_querylog/$request_id.txt";
$path = file_stream_wrapper_uri_normalize($path);
$queries = json_decode(file_get_contents($path));
$query = $queries[$qid];
$conn = Database::getConnection();
$quoted = array();
foreach ((array)$query->args as $key => $val) {
$quoted[$key] = $conn->quote($val);
}
$output = strtr($query->query, $quoted);
// print and return nothing thus avoiding page wrapper.
print $output;
$GLOBALS['devel_shutdown'] = FALSE;
}
/**
* Menu callback; clear the database, resetting the menu to factory defaults.
*/
function devel_menu_rebuild() {
menu_rebuild();
drupal_set_message(t('The menu router has been rebuilt.'));
drupal_goto();
}
/**
* Display a dropdown of installed modules with the option to reinstall them.
*/
function devel_reinstall($form, &$form_state) {
$output = '';
$modules = module_list();
sort($modules);
$options = drupal_map_assoc($modules);
$form['list'] = array(
'#type' => 'checkboxes',
'#options' => $options,
'#description' => t('Uninstall and then install the selected modules. <code>hook_uninstall()</code> and <code>hook_install()</code> will be executed and the schema version number will be set to the most recent update number. You may have to manually clear out any existing tables first if the module doesn\'t implement <code>hook_uninstall()</code>.'),
);
$form['submit'] = array(
'#value' => t('Reinstall'),
'#type' => 'submit',
);
return $form;
}
/**
* Process reinstall menu form submissions.
*/
function devel_reinstall_submit($form, &$form_state) {
// require_once './includes/install.inc';
$modules = array_filter($form_state['values']['list']);
module_disable($modules, FALSE);
drupal_uninstall_modules($modules);
module_enable($modules, FALSE);
drupal_set_message(t('Uninstalled and installed: %names.', array('%names' => implode(', ', $modules))));
}
// Menu callback.
function devel_theme_registry() {
drupal_theme_initialize();
$hooks = theme_get_registry();
ksort($hooks);
return kprint_r($hooks, TRUE);
}
// Menu callback. $entity_type argument not currently used in the UI.
function devel_entity_info_page($entity_type = NULL) {
$info = entity_get_info($entity_type);
ksort($info);
return kprint_r($info, TRUE);
}
// Menu callback.
function devel_field_info_page() {
$info = field_info_fields();
$output = kprint_r($info, TRUE, t('Fields'));
$info = field_info_instances();
$output .= kprint_r($info, TRUE, t('Instances'));
$info = field_info_bundles();
$output .= kprint_r($info, TRUE, t('Bundles'));
return $output;
}
/**
* Menu callback; display all variables.
*/
function devel_variable_page() {
// We return our own $page so as to avoid blocks.
$output = drupal_get_form('devel_variable_form');
drupal_set_page_content($output);
$page = element_info('page');
return $page;
}
function devel_variable_form() {
$header = array(
'name' => array('data' => t('Name'), 'field' => 'name', 'sort' => 'asc'),
'value' => array('data' => t('Value'), 'field' => 'value'),
'length' => array('data' => t('Length'), 'field' => 'length'),
'edit' => array('data' => t('Operations')),
);
// TODO: we could get variables out of $conf but that would include hard coded ones too. ideally i would highlight overrridden/hard coded variables
$query = db_select('variable', 'v')->extend('TableSort');
$query->fields('v', array('name', 'value'));
switch (db_driver()) {
case 'mssql':
$query->addExpression("COL_LENGTH('{variable}', 'value')", 'length');
break;
case 'pgsql':
$query->addExpression("CONVERT(LENGTH(v.value), INTEGER)", 'length');
break;
default:
$query->addExpression("CONVERT(LENGTH(v.value), UNSIGNED INTEGER)", 'length');
break;
}
$result = $query
->orderByHeader($header)
->execute();
foreach ($result as $row) {
// $variables[$row->name] = '';
$options[$row->name]['name'] = check_plain($row->name);
if (merits_krumo($row->value)) {
$value = krumo_ob(variable_get($row->name, NULL));
}
else {
if (drupal_strlen($row->value) > 70) {
$value = check_plain(drupal_substr($row->value, 0, 65)) .'...';
}
else {
$value = check_plain($row->value);
}
}
$options[$row->name]['value'] = $value;
$options[$row->name]['length'] = $row->length;
$options[$row->name]['edit'] = l(t('Edit'), "devel/variable/edit/$row->name");
}
$form['variables'] = array(
'#type' => 'tableselect',
'#header' => $header,
'#options' => $options,
'#empty' => t('No variables.'),
);
$form['submit'] = array(
'#type' => 'submit',
'#value' => t('Delete'),
);
// krumo($form);
return $form;
}
function devel_variable_form_submit($form, &$form_state) {
$deletes = array_filter($form_state['values']['variables']);
array_walk($deletes, 'variable_del');
if (count($deletes)) {
drupal_set_message(format_plural(count($deletes), 'One variable deleted.', '@count variables deleted.'));
}
}
function devel_variable_edit($form, &$form_state, $name) {
$value = variable_get($name, 'not found');
$form['name'] = array(
'#type' => 'value',
'#value' => $name
);
$form['value'] = array(
'#type' => 'item',
'#title' => t('Old value'),
'#markup' => dpr($value, TRUE),
);
if (is_string($value) || is_numeric($value)) {
$form['new'] = array(
'#type' => 'textarea',
'#title' => t('New value'),
'#default_value' => $value
);
$form['submit'] = array(
'#type' => 'submit',
'#value' => t('Submit'),
);
}
else {
$api = variable_get('devel_api_url', 'api.drupal.org');
$form['new'] = array(
'#type' => 'item',
'#title' => t('New value'),
'#value' => t('Sorry, complex variable types may not be edited yet. Use the <em>Execute PHP</em> block and the <a href="@variable-set-doc">variable_set()</a> function.', array('@variable-set-doc' => "http://$api/api/HEAD/function/variable_set"))
);
}
drupal_set_title($name);
return $form;
}
function devel_variable_edit_submit($form, &$form_state) {
variable_set($form_state['values']['name'], $form_state['values']['new']);
drupal_set_message(t('Saved new value for %name.', array('%name' => $form_state['values']['name'])));
'devel/variable';
}
/**
* Menu callback: display the session.
*/
function devel_session() {
global $user;
$output = kprint_r($_SESSION, TRUE);
$headers = array(t('Session name'), t('Session ID'));
$output .= theme('table', array('headers' => $headers, 'rows' => array(array(session_name(), session_id()))));
return $output;
}
/**
* Menu callback; prints the loaded structure of the current node/user.
*/
function devel_load_object($type, $object, $name = NULL) {
$name = isset($name) ? $name : $type;
return kdevel_print_object($object, '$'. $name .'->');
}
/**
* Menu callback; prints the render structure of the current object (currently node or user).
*/
function devel_render_object($type, $object, $name = NULL) {
$name = isset($name) ? $name : $type;
$function = $type . '_view';
$build = $function($object);
return kdevel_print_object($build, '$'. $name .'->');
}
function devel_elements_page() {
return kdevel_print_object(module_invoke_all('element_info'));
}
function devel_phpinfo() {
print phpinfo();
drupal_exit();
}
<?php
/**
* Implements hook_rules_action_info().
*/
function devel_rules_action_info() {
return array(
'devel_debug' => array(
'base' => 'devel_rules_debug_action',
'label' => t('Debug value'),
'group' => t('Devel'),
'parameter' => array(
'value' => array('type' => 'unknown', 'label' => t('Value to debug')),
),
),
);
}
/**
* Rules action for debugging values.
*/
function devel_rules_debug_action($value) {
dpm($value);
}
<?php
/**
* @file
* Generate content, taxonomy, menu, and users via drush framework.
*/
/**
* Implementation of hook_drush_command().
*/
function devel_generate_drush_command() {
$items['generate-users'] = array(
'description' => 'Create users.',
'arguments' => array(
'number_users' => 'Number of users to generate.',
),
'options' => array(
'kill' => 'Delete all users before generating new ones.',
'roles' => 'A comma delimited list of role IDs which should be granted to the new users. No need to specify authenticated user role.',
),
'aliases' => array('genu'),
);
$items['generate-terms'] = array(
'description' => 'Create terms in specified vocabulary.',
'arguments' => array(
'machine_name' => 'Vocabulary machine name into which new terms will be inserted.',
'number_terms' => 'Number of terms to insert. Defaults to 10.',
),
'options' => array(
'kill' => 'Delete all terms in specified vocabulary before generating.',
'feedback' => 'An integer representing interval for insertion rate logging. Defaults to 500',
),
'aliases' => array('gent'),
);
$items['generate-vocabs'] = array(
'description' => 'Create vocabularies.',
'arguments' => array(
'num_vocabs' => 'Number of vocabularies to create. Defaults to 1.',
),
'options' => array(
'kill' => 'Delete all vocabularies before generating.',
),
'aliases' => array('genv'),
);
$items['generate-content'] = array(
'description' => 'Create content.',
'drupal dependencies' => array('devel_generate'),
'arguments' => array(
'number_nodes' => 'Number of nodes to generate.',
'maximum_comments' => 'Maximum number of comments to generate.',
),
'options' => array(
'kill' => 'Delete all content before generating new content.',
'types' => 'A comma delimited list of content types to create. Defaults to page,article.',
'feedback' => 'An integer representing interval for insertion rate logging. Defaults to 500',
'skip-fields' => 'A comma delimited list of fields to omit when generating random values',
'languages' => 'A comma-separated list of language codes',
),
'aliases' => array('genc'),
);
$items['generate-menus'] = array(
'description' => 'Create menus and menu items.',
'drupal dependencies' => array('devel_generate'), // Remove these once devel.module is moved down a directory. http://drupal.org/node/925246
'arguments' => array(
'number_menus' => 'Number of menus to generate. Defaults to 2.',
'number_links' => 'Number of links to generate. Defaults to 50.',
'max_depth' => 'Max link depth. Defaults to 3',
'max_width' => 'Max width of first level of links. Defaults to 8.',
),
'options' => array(
'kill' => 'Delete all previously generated menus and links before generating new menus and links.',
),
'aliases' => array('genm'),
);
return $items;
}
/**
* Command callback. Generate a number of users.
*/
function drush_devel_generate_users($num_users = NULL) {
if (drush_generate_is_number($num_users) == FALSE) {
return drush_set_error('DEVEL_GENERATE_INVALID_INPUT', t('Invalid number of users.'));
}
drush_generate_include_devel();
$roles = drush_get_option('roles') ? explode(',', drush_get_option('roles')) : array();
devel_create_users($num_users, drush_get_option('kill'), 0, $roles);
drush_log(t('Generated @number users.', array('@number' => $num_users)), 'success');
}
/**
* Command callback. Generate a number of terms in given vocabs.
*/
function drush_devel_generate_terms($vname = NULL, $num_terms = 10) {
// Try to convert machine name to a vocab ID
if (!$vocab = taxonomy_vocabulary_machine_name_load($vname)) {
return drush_set_error('DEVEL_GENERATE_INVALID_INPUT', dt('Invalid vocabulary name: !name', array('!name' => $vname)));
}
if (drush_generate_is_number($num_terms) == FALSE) {
return drush_set_error('DEVEL_GENERATE_INVALID_INPUT', dt('Invalid number of terms: !num', array('!num' => $num_terms)));
}
drush_generate_include_devel();
$vocabs[$vocab->vid] = $vocab;
devel_generate_term_data($vocabs, $num_terms, '12', drush_get_option('kill'));
drush_log(dt('Generated @num_terms terms.', array('@num_terms' => $num_terms)), 'success');
}
/**
* Command callback. Generate a number of vocabularies.
*/
function drush_devel_generate_vocabs($num_vocab = 1) {
if (drush_generate_is_number($num_vocab) == FALSE) {
return drush_set_error('DEVEL_GENERATE_INVALID_INPUT', dt('Invalid number of vocabularies: !num.', array('!num' => $num_vocab)));
}
drush_generate_include_devel();
devel_generate_vocab_data($num_vocab, '12', drush_get_option('kill'));
drush_log(dt('Generated @num_vocab vocabularies.', array('@num_vocab' => $num_vocab)), 'success');
}
/**
* Command callback. Generate a number of nodes and comments.
*/
function drush_devel_generate_content($num_nodes = NULL, $max_comments = NULL) {
if (drush_generate_is_number($num_nodes) == FALSE) {
return drush_set_error('DEVEL_GENERATE_INVALID_INPUT', dt('Invalid number of nodes'));
}
if (!empty($max_comments) && drush_generate_is_number($max_comments) == FALSE) {
return drush_set_error('DEVEL_GENERATE_INVALID_INPUT', dt('Invalid number of comments.'));
}
$add_language = drush_get_option('languages');
if (!empty($add_language)) {
$add_language = explode(',', str_replace(' ', '', $add_language));
// Intersect with the enabled languages to make sure the language args
// passed are actually enabled.
$values['values']['add_language'] = array_flip(array_intersect($add_language, array_keys(locale_language_list())));
}
// Load user 1; is needed for creating *published* comments.
if ($max_comments) {
global $user;
$user_one = user_load(1);
$user = $user_one;
drupal_save_session(FALSE);
}
$values['values']['kill_content'] = drush_get_option('kill');
$values['values']['title_length'] = 6;
$values['values']['num_nodes'] = $num_nodes;
$values['values']['max_comments'] = $max_comments;
$values['values']['node_types'] = drupal_map_assoc(explode(',', drush_get_option('types', 'page,article')));
drush_generate_include_devel();
devel_generate_content($values);
drush_log(t('Generated @num_nodes nodes, @max_comments comments (or less) per node.', array('@num_nodes' => (int)$num_nodes, '@max_comments' => (int)$max_comments)), 'success');
}
/**
* Command callback. Generate a number of menus and menu links.
*/
function drush_devel_generate_menus($number_menus = 2, $number_links = 50, $max_depth = 3, $max_width = 8) {
if (drush_generate_is_number($number_menus) == FALSE) {
return drush_set_error('DEVEL_GENERATE_INVALID_INPUT', dt('Invalid number of menus'));
}
if (drush_generate_is_number($number_links) == FALSE) {
return drush_set_error('DEVEL_GENERATE_INVALID_INPUT', dt('Invalid number of links'));
}
if (drush_generate_is_number($max_depth) == FALSE || $max_depth > 9 || $max_depth < 1) {
return drush_set_error('DEVEL_GENERATE_INVALID_INPUT', dt('Invalid maximum link depth. Use a value between 1 and 9'));
}
if (drush_generate_is_number($max_width) == FALSE || $max_width < 1) {
return drush_set_error('DEVEL_GENERATE_INVALID_INPUT', dt('Invalid maximum menu width. Use a positive numeric value.'));
}
global $user;
$user_one = user_load(1);
$user = $user_one;
drupal_save_session(FALSE);
$kill = drush_get_option('kill');
drush_generate_include_devel();
$link_types = drupal_map_assoc(array('node', 'front', 'external'));
devel_generate_menu_data($number_menus, array(), $number_links, 12, $link_types, $max_depth, $max_width, $kill);
drush_log(t('Generated @number_menus menus, @number_links links.', array('@number_menus' => (int)$number_menus, '@number_links' => (int)$number_links)), 'success');
}
//////////////////////////////////////////////////////////////////////////////
// Helper functions
// Verify if param is a number.
function drush_generate_is_number($number) {
if ($number == NULL) return FALSE;
if (!is_numeric($number)) return FALSE;
return TRUE;
}
// Include devel_generate.inc.
function drush_generate_include_devel() {
$path = drupal_get_path('module', 'devel_generate');
require_once($path .'/devel_generate.inc');
}
<?php
/**
* @file
* Functions needed for devel_generate Fields API integration.
*/
/**
* Enrich the $object that is about to be saved with arbitrary
* information in each of its fields.
**/
function devel_generate_fields(&$object, $obj_type, $bundle) {
$field_types = field_info_field_types();
$instances = field_info_instances($obj_type, $bundle);
$skips = function_exists('drush_get_option') ? drush_get_option('skip-fields', '') : @$_REQUEST['skip-fields'];
foreach (explode(',', $skips) as $skip) {
unset($instances[$skip]);
}
foreach ($instances as $instance) {
$field_name = $instance['field_name'];
$field = field_info_field($field_name);
$object_field = array();
// If module handles own multiples, then only call its hook once.
if (field_behaviors_widget('multiple values', $instance) == FIELD_BEHAVIOR_CUSTOM) {
$max = 0;
}
else {
switch ($field['cardinality']) {
case FIELD_CARDINALITY_UNLIMITED:
$max = rand(0, 3); //just an arbitrary number for 'unlimited'
break;
default:
$max = $field['cardinality'] - 1;
break;
}
}
for ($i = 0; $i <= $max; $i++) {
$module = $field_types[$field['type']]['module'];
// Include any support file that might exist for this field.
if (in_array($module, array('file', 'image', 'taxonomy', 'number', 'text', 'comment', 'list'))) {
// devel_generate implements on behalf of core and special friends.
module_load_include('inc', 'devel_generate', "$module.devel_generate");
}
else {
module_load_include('inc', $module, "$module.devel_generate");
}
$function = $module . '_devel_generate';
if (function_exists($function)) {
if ($result = $function($object, $field, $instance, $bundle)) {
if (field_behaviors_widget('multiple values', $instance) == FIELD_BEHAVIOR_CUSTOM) {
// Fields that handle their own multiples will add their own deltas.
$object_field = $result;
}
else {
// When multiples are handled by the content module, add a delta for each result.
$object_field[$i] = $result;
}
}
}
}
// TODO: Completely overriding any existing $object->{$field['field_name']}
// is necessary here because the forum module has a bug where it
// initializes the property with incorrect data.
// @see http://drupal.org/node/652176
$object->{$field['field_name']} = array(
$object->language => $object_field,
);
}
}
/**
* A simple function to return multiple values for fields that use
* custom multiple value widgets but don't need any other special multiple
* values handling. This will call the field generation function
* a random number of times and compile the results into a node array.
*/
function devel_generate_multiple($function, $object, $field, $instance, $bundle) {
$object_field = array();
if (function_exists($function)) {
switch ($field['cardinality']) {
case FIELD_CARDINALITY_UNLIMITED:
$max = rand(0, 3); //just an arbitrary number for 'unlimited'
break;
default:
$max = $field['cardinality'] - 1;
break;
}
for ($i = 0; $i <= $max; $i++) {
$result = $function($object, $field, $instance, $bundle);
if (!empty($result)) {
$object_field[$i] = $result;
}
}
}
return $object_field;
}
<?php
/**
* Generate some random users.
*
* @param $num
* Number of users to generate.
* @param $kill
* Boolean that indicates if existing users should be removed first.
* @param $age
* The max age of each randomly-generated user, in seconds.
* @param $roles
* An array of role IDs that the users should receive.
*/
function devel_create_users($num, $kill, $age = 0, $roles = array()) {
$url = parse_url($GLOBALS['base_url']);
if ($kill) {
$uids = db_select('users', 'u')
->fields('u', array('uid'))
->condition('uid', 1, '>')
->execute()
->fetchAllAssoc('uid');
user_delete_multiple(array_keys($uids));
drupal_set_message(format_plural(count($uids), '1 user deleted', '@count users deleted.'));
}
// Determine if we should create user pictures.
$pic_config = FALSE;
module_load_include('inc', 'system', 'image.gd');
if (variable_get('user_pictures', 0) && function_exists('image_gd_check_settings') && image_gd_check_settings()) {
$pic_config['path'] = variable_get('user_picture_path', 'pictures');
list($pic_config['width'], $pic_config['height']) = explode('x', variable_get('user_picture_dimensions', '85x85'));
}
if ($num > 0) {
$names = array();
while (count($names) < $num) {
$name = devel_generate_word(mt_rand(6, 12));
$names[$name] = '';
}
if (empty($roles)) {
$roles = array(DRUPAL_AUTHENTICATED_RID);
}
foreach ($names as $name => $value) {
$edit = array(
'uid' => NULL,
'name' => $name,
'pass' => NULL, // No password avoids user_hash_password() which is expensive.
'mail' => $name . '@' . $url['host'],
'status' => 1,
'created' => REQUEST_TIME - mt_rand(0, $age),
'roles' => drupal_map_assoc($roles),
);
// Populate all core fields on behalf of field.module
module_load_include('inc', 'devel_generate', 'devel_generate.fields');
$edit = (object) $edit;
$edit->language = LANGUAGE_NONE;
devel_generate_fields($edit, 'user', 'user');
$edit = (array) $edit;
$account = user_save(drupal_anonymous_user(), $edit);
if ($pic_config) {
// Since the image.module should scale the picture just pick an
// arbitrary size that it's too big for our font.
$im = imagecreatetruecolor(200, 200);
// Randomize the foreground using the md5 of the user id, then invert it
// for the background color so there's enough contrast to read the text.
$parts = array_map('hexdec', str_split(md5($account->uid), 2));
$fg = imagecolorallocate($im, $parts[1], $parts[3], $parts[5]);
$bg = imagecolorallocate($im, 255 - $parts[0], 255 - $parts[1], 255 - $parts[2]);
// Fill the background then print their user info.
imagefill($im, 0, 0, $bg);
imagestring($im, 5, 5, 5, "#" . $account->uid, $fg);
imagestring($im, 5, 5, 25, $account->name, $fg);
// Create an empty, managed file where we want the user's picture to
// be so we can have GD overwrite it with the image.
$picture_directory = variable_get('file_default_scheme', 'public') . '://' . variable_get('user_picture_path', 'pictures');
file_prepare_directory($picture_directory, FILE_CREATE_DIRECTORY);
$destination = file_stream_wrapper_uri_normalize($picture_directory . '/picture-' . $account->uid . '.png');
$file = file_save_data('', $destination);
// GD doesn't like stream wrapped paths so convert the URI to a normal
// file system path.
if (isset($file) && $wrapper = file_stream_wrapper_get_instance_by_uri($file->uri)) {
imagepng($im, $wrapper->realpath());
}
imagedestroy($im);
// Clear the cached filesize, set the owner and MIME-type then re-save
// the file.
clearstatcache();
$file->uid = $account->uid;
$file->filemime = 'image/png';
$file = file_save($file);
// Save the user record with the new picture.
$edit = (array) $account;
$edit['picture'] = $file;
user_save($account, $edit);
}
}
}
drupal_set_message(t('!num_users created.', array('!num_users' => format_plural($num, '1 user', '@count users'))));
}
/**
* The main API function for creating content.
*
* See devel_generate_content_form() for the supported keys in $form_state['values'].
* Other modules may participate by form_alter() on that form and then handling their data during hook_nodeapi('pre_save') or in own submit handler for the form.
*
* @param string $form_state
* @return void
*/
function devel_generate_content($form_state) {
if (!empty($form_state['values']['kill_content'])) {
devel_generate_content_kill($form_state['values']);
}
if (count($form_state['values']['node_types'])) {
// Generate nodes.
devel_generate_content_pre_node($form_state['values']);
$start = time();
for ($i = 1; $i <= $form_state['values']['num_nodes']; $i++) {
devel_generate_content_add_node($form_state['values']);
if (function_exists('drush_log') && $i % drush_get_option('feedback', 1000) == 0) {
$now = time();
drush_log(dt('Completed !feedback nodes (!rate nodes/min)', array('!feedback' => drush_get_option('feedback', 1000), '!rate' => (drush_get_option('feedback', 1000)*60)/($now-$start))), 'ok');
$start = $now;
}
}
}
devel_generate_set_message(format_plural($form_state['values']['num_nodes'], '1 node created.', 'Finished creating @count nodes'));
}
function devel_generate_add_comments($node, $users, $max_comments, $title_length = 8) {
$num_comments = mt_rand(1, $max_comments);
for ($i = 1; $i <= $num_comments; $i++) {
$comment = new stdClass;
$comment->nid = $node->nid;
$comment->cid = NULL;
$comment->name = 'devel generate';
$comment->mail = 'devel_generate@example.com';
$comment->timestamp = mt_rand($node->created, REQUEST_TIME);
switch ($i % 3) {
case 1:
$comment->pid = db_query_range("SELECT cid FROM {comment} WHERE pid = 0 AND nid = :nid ORDER BY RAND()", 0, 1, array(':nid' => $comment->nid))->fetchField();
break;
case 2:
$comment->pid = db_query_range("SELECT cid FROM {comment} WHERE pid > 0 AND nid = :nid ORDER BY RAND()", 0, 1, array(':nid' => $comment->nid))->fetchField();
break;
default:
$comment->pid = 0;
}
// The subject column has a max character length of 64
// See bug: http://drupal.org/node/1024340
$comment->subject = substr(devel_create_greeking(mt_rand(2, $title_length), TRUE), 0, 63);
$comment->uid = $users[array_rand($users)];
$comment->language = LANGUAGE_NONE;
// Populate all core fields on behalf of field.module
module_load_include('inc', 'devel_generate', 'devel_generate.fields');
devel_generate_fields($comment, 'comment', 'comment_node_' . $node->type);
comment_save($comment);
}
}
function devel_generate_vocabs($records, $maxlength = 12, $types = array('page', 'article')) {
$vocs = array();
// Insert new data:
for ($i = 1; $i <= $records; $i++) {
$voc = new stdClass();
$voc->name = devel_generate_word(mt_rand(2, $maxlength));
$voc->machine_name = drupal_strtolower($voc->name);
$voc->description = "description of ". $voc->name;
// TODO: not working
$voc->nodes = array_flip(array($types[array_rand($types)]));
foreach ($voc->nodes as $key => $value) {
$voc->nodes[$key] = $key;
}
$voc->multiple = 1;
$voc->required = 0;
$voc->relations = 1;
$voc->hierarchy = 1;
$voc->weight = mt_rand(0,10);
$voc->language = LANGUAGE_NONE;
taxonomy_vocabulary_save($voc);
$vocs[] = $voc->name;
unset($voc);
}
return $vocs;
}
function devel_generate_terms($records, $vocabs, $maxlength = 12) {
$terms = array();
// Insert new data:
$max = db_query('SELECT MAX(tid) FROM {taxonomy_term_data}')->fetchField();
$start = time();
for ($i = 1; $i <= $records; $i++) {
switch ($i % 2) {
case 1:
// Set vid and vocabulary_machine_name properties.
$vocab = $vocabs[array_rand($vocabs)];
$term->vid = $vocab->vid;
$term->vocabulary_machine_name = $vocab->machine_name;
// Don't set a parent. Handled by taxonomy_save_term()
// $term->parent = 0;
break;
default:
while (TRUE) {
// Keep trying to find a random parent.
$candidate = mt_rand(1, $max);
$query = db_select('taxonomy_term_data', 't');
$query->innerJoin('taxonomy_vocabulary', 'v', 't.vid = v.vid');
$parent = $query
->fields('t', array('tid', 'vid'))
->fields('v', array('machine_name'))
->condition('v.vid', array_keys($vocabs), 'IN')
->condition('t.tid', $candidate, '>=')
->range(0,1)
->execute()
->fetchAssoc();
if ($parent['tid']) {
break;
}
}
$term->parent = $parent['tid'];
// Slight speedup due to this property being set.
$term->vocabulary_machine_name = $parent['machine_name'];
$term->vid = $parent['vid'];
break;
}
$term->name = devel_generate_word(mt_rand(2, $maxlength));
$term->description = "description of ". $term->name;
$term->format = filter_fallback_format();
$term->weight = mt_rand(0, 10);
$term->language = LANGUAGE_NONE;
// Populate all core fields on behalf of field.module
module_load_include('inc', 'devel_generate', 'devel_generate.fields');
devel_generate_fields($term, 'term', $term->vocabulary_machine_name);
if ($status = taxonomy_term_save($term)) {
$max += 1;
if (function_exists('drush_log')) {
$feedback = drush_get_option('feedback', 1000);
if ($i % $feedback == 0) {
$now = time();
drush_log(dt('Completed !feedback terms (!rate terms/min)', array('!feedback' => $feedback, '!rate' => $feedback*60 / ($now-$start) )), 'ok');
$start = $now;
}
}
// Limit memory usage. Only report first 20 created terms.
if ($i < 20) {
$terms[] = $term->name;
}
unset($term);
}
}
return $terms;
}
// TODO: use taxonomy_get_entries once that exists.
function devel_generate_get_terms($vids) {
return db_select('taxonomy_term_data', 'td')
->fields('td', array('tid'))
->condition('vid', $vids, 'IN')
->orderBy('tid', 'ASC')
->execute()
->fetchCol('tid');
}
function devel_generate_term_data($vocabs, $num_terms, $title_length, $kill) {
if ($kill) {
foreach (devel_generate_get_terms(array_keys($vocabs)) as $tid) {
taxonomy_term_delete($tid);
}
drupal_set_message(t('Deleted existing terms.'));
}
$new_terms = devel_generate_terms($num_terms, $vocabs, $title_length);
if (!empty($new_terms)) {
drupal_set_message(t('Created the following new terms: !terms', array('!terms' => theme('item_list', array('items' => $new_terms)))));
}
}
function devel_generate_vocab_data($num_vocab, $title_length, $kill) {
if ($kill) {
foreach (taxonomy_get_vocabularies() as $vid => $vocab) {
taxonomy_vocabulary_delete($vid);
}
drupal_set_message(t('Deleted existing vocabularies.'));
}
$new_vocs = devel_generate_vocabs($num_vocab, $title_length);
if (!empty($new_vocs)) {
drupal_set_message(t('Created the following new vocabularies: !vocs', array('!vocs' => theme('item_list', array('items' => $new_vocs)))));
}
}
function devel_generate_menu_data($num_menus, $existing_menus, $num_links, $title_length, $link_types, $max_depth, $max_width, $kill) {
// Delete menus and menu links.
if ($kill) {
if (module_exists('menu')) {
foreach (menu_get_menus(FALSE) as $menu => $menu_title) {
if (strpos($menu, 'devel-') === 0) {
$menu = menu_load($menu);
menu_delete($menu);
}
}
}
// Delete menu links generated by devel.
$result = db_select('menu_links', 'm')
->fields('m', array('mlid'))
->condition('m.menu_name', 'devel', '<>')
// Look for the serialized version of 'devel' => TRUE.
->condition('m.options', '%' . db_like('s:5:"devel";b:1') . '%', 'LIKE')
->execute();
foreach ($result as $link) {
menu_link_delete($link->mlid);
}
drupal_set_message(t('Deleted existing menus and links.'));
}
// Generate new menus.
$new_menus = devel_generate_menus($num_menus, $title_length);
if (!empty($new_menus)) {
drupal_set_message(t('Created the following new menus: !menus', array('!menus' => theme('item_list', array('items' => $new_menus)))));
}
// Generate new menu links.
$menus = $new_menus + $existing_menus;
$new_links = devel_generate_links($num_links, $menus, $title_length, $link_types, $max_depth, $max_width);
drupal_set_message(t('Created @count new menu links.', array('@count' => count($new_links))));
}
/**
* Generates new menus.
*/
function devel_generate_menus($num_menus, $title_length = 12) {
$menus = array();
if (!module_exists('menu')) {
$num_menus = 0;
}
for ($i = 1; $i <= $num_menus; $i++) {
$menu = array();
$menu['title'] = devel_generate_word(mt_rand(2, $title_length));
$menu['menu_name'] = 'devel-' . drupal_strtolower($menu['title']);
$menu['description'] = t('Description of @name', array('@name' => $menu['title']));
menu_save($menu);
$menus[$menu['menu_name']] = $menu['title'];
}
return $menus;
}
/**
* Generates menu links in a tree structure.
*/
function devel_generate_links($num_links, $menus, $title_length, $link_types, $max_depth, $max_width) {
$links = array();
$menus = array_keys(array_filter($menus));
$link_types = array_keys(array_filter($link_types));
$nids = array();
for ($i = 1; $i <= $num_links; $i++) {
// Pick a random menu.
$menu_name = $menus[array_rand($menus)];
// Build up our link.
$link = array(
'menu_name' => $menu_name,
'options' => array('devel' => TRUE),
'weight' => mt_rand(-50, 50),
'mlid' => 0,
'link_title' => devel_generate_word(mt_rand(2, $title_length)),
);
$link['options']['attributes']['title'] = t('Description of @title.', array('@title' => $link['link_title']));
// For the first $max_width items, make first level links.
if ($i <= $max_width) {
$depth = 0;
}
else {
// Otherwise, get a random parent menu depth.
$depth = mt_rand(1, $max_depth - 1);
}
// Get a random parent link from the proper depth.
do {
$link['plid'] = db_select('menu_links', 'm')
->fields('m', array('mlid'))
->condition('m.menu_name', $menus, 'IN')
->condition('m.depth', $depth)
->range(0, 1)
->orderRandom()
->execute()
->fetchField();
$depth--;
} while (!$link['plid'] && $depth > 0);
if (!$link['plid']) {
$link['plid'] = 0;
}
$link_type = array_rand($link_types);
switch ($link_types[$link_type]) {
case 'node':
// Grab a random node ID.
$select = db_select('node', 'n')
->fields('n', array('nid', 'title'))
->condition('n.status', 1)
->range(0, 1)
->orderRandom();
// Don't put a node into the menu twice.
if (!empty($nids[$menu_name])) {
$select->condition('n.nid', $nids[$menu_name], 'NOT IN');
}
$node = $select->execute()->fetchAssoc();
if (isset($node['nid'])) {
$nids[$menu_name][] = $node['nid'];
$link['link_path'] = $link['router_path'] = 'node/' . $node['nid'];
$link['link_title'] = $node['title'];
break;
}
case 'external':
$link['link_path'] = 'http://www.example.com/';
break;
case 'front':
$link['link_path'] = $link['router_path'] = '<front>';
break;
default:
$link['devel_link_type'] = $link_type;
break;
}
menu_link_save($link);
$links[$link['mlid']] = $link['link_title'];
}
return $links;
}
function devel_generate_word($length){
mt_srand((double)microtime()*1000000);
$vowels = array("a", "e", "i", "o", "u");
$cons = array("b", "c", "d", "g", "h", "j", "k", "l", "m", "n", "p", "r", "s", "t", "u", "v", "w", "tr",
"cr", "br", "fr", "th", "dr", "ch", "ph", "wr", "st", "sp", "sw", "pr", "sl", "cl", "sh");
$num_vowels = count($vowels);
$num_cons = count($cons);
$word = '';
while(strlen($word) < $length){
$word .= $cons[mt_rand(0, $num_cons - 1)] . $vowels[mt_rand(0, $num_vowels - 1)];
}
return substr($word, 0, $length);
}
function devel_create_content($type = NULL) {
$nparas = mt_rand(1,12);
$type = empty($type) ? mt_rand(0,3) : $type;
$output = "";
switch($type % 3) {
// MW: This appears undesireable. Was giving <p> in text fields
// case 1: // html
// for ($i = 1; $i <= $nparas; $i++) {
// $output .= devel_create_para(mt_rand(10,60),1);
// }
// break;
//
// case 2: // brs only
// for ($i = 1; $i <= $nparas; $i++) {
// $output .= devel_create_para(mt_rand(10,60),2);
// }
// break;
default: // plain text
for ($i = 1; $i <= $nparas; $i++) {
$output .= devel_create_para(mt_rand(10,60)) ."\n\n";
}
}
return $output;
}
function devel_create_para($words, $type = 0) {
$output = '';
switch ($type) {
case 1:
$output .= "<p>" . devel_create_greeking($words) . "</p>";
break;
case 2:
$output .= devel_create_greeking($words) . "<br />";
break;
default:
$output .= devel_create_greeking($words);
}
return $output;
}
function devel_create_greeking($word_count, $title = FALSE) {
$dictionary = array("abbas", "abdo", "abico", "abigo", "abluo", "accumsan",
"acsi", "ad", "adipiscing", "aliquam", "aliquip", "amet", "antehabeo",
"appellatio", "aptent", "at", "augue", "autem", "bene", "blandit",
"brevitas", "caecus", "camur", "capto", "causa", "cogo", "comis",
"commodo", "commoveo", "consectetuer", "consequat", "conventio", "cui",
"damnum", "decet", "defui", "diam", "dignissim", "distineo", "dolor",
"dolore", "dolus", "duis", "ea", "eligo", "elit", "enim", "erat",
"eros", "esca", "esse", "et", "eu", "euismod", "eum", "ex", "exerci",
"exputo", "facilisi", "facilisis", "fere", "feugiat", "gemino",
"genitus", "gilvus", "gravis", "haero", "hendrerit", "hos", "huic",
"humo", "iaceo", "ibidem", "ideo", "ille", "illum", "immitto",
"importunus", "imputo", "in", "incassum", "inhibeo", "interdico",
"iriure", "iusto", "iustum", "jugis", "jumentum", "jus", "laoreet",
"lenis", "letalis", "lobortis", "loquor", "lucidus", "luctus", "ludus",
"luptatum", "macto", "magna", "mauris", "melior", "metuo", "meus",
"minim", "modo", "molior", "mos", "natu", "neo", "neque", "nibh",
"nimis", "nisl", "nobis", "nostrud", "nulla", "nunc", "nutus", "obruo",
"occuro", "odio", "olim", "oppeto", "os", "pagus", "pala", "paratus",
"patria", "paulatim", "pecus", "persto", "pertineo", "plaga", "pneum",
"populus", "praemitto", "praesent", "premo", "probo", "proprius",
"quadrum", "quae", "qui", "quia", "quibus", "quidem", "quidne", "quis",
"ratis", "refero", "refoveo", "roto", "rusticus", "saepius",
"sagaciter", "saluto", "scisco", "secundum", "sed", "si", "similis",
"singularis", "sino", "sit", "sudo", "suscipere", "suscipit", "tamen",
"tation", "te", "tego", "tincidunt", "torqueo", "tum", "turpis",
"typicus", "ulciscor", "ullamcorper", "usitas", "ut", "utinam",
"utrum", "uxor", "valde", "valetudo", "validus", "vel", "velit",
"veniam", "venio", "vereor", "vero", "verto", "vicis", "vindico",
"virtus", "voco", "volutpat", "vulpes", "vulputate", "wisi", "ymo",
"zelus");
$dictionary_flipped = array_flip($dictionary);
$greeking = '';
if (!$title) {
$words_remaining = $word_count;
while ($words_remaining > 0) {
$sentence_length = mt_rand(3, 10);
$words = array_rand($dictionary_flipped, $sentence_length);
$sentence = implode(' ', $words);
$greeking .= ucfirst($sentence) . '. ';
$words_remaining -= $sentence_length;
}
}
else {
// Use slightly different method for titles.
$words = array_rand($dictionary_flipped, $word_count);
$greeking = ucwords(implode(' ', $words));
}
// Work around possible php garbage collection bug. Without an unset(), this
// function gets very expensive over many calls (php 5.2.11).
unset($dictionary, $dictionary_flipped);
return trim($greeking);
}
function devel_generate_add_terms(&$node) {
$vocabs = taxonomy_get_vocabularies($node->type);
foreach ($vocabs as $vocab) {
$sql = "SELECT tid FROM {taxonomy_term_data} WHERE vid = :vid ORDER BY RAND()";
$result = db_query_range($sql, 0, 5 , array(':vid' => $vocab->vid));
foreach($result as $row) {
$node->taxonomy[] = $row->tid;
if (!$vocab->multiple) {
break;
}
}
}
}
function devel_get_users() {
$users = array();
$result = db_query_range("SELECT uid FROM {users}", 0, 50);
foreach ($result as $record) {
$users[] = $record->uid;
}
return $users;
}
/**
* Generate statistics information for a node.
*
* @param $node
* A node object.
*/
function devel_generate_add_statistics($node) {
$statistic = array(
'nid' => $node->nid,
'totalcount' => mt_rand(0, 500),
'timestamp' => REQUEST_TIME - mt_rand(0, $node->created),
);
$statistic['daycount'] = mt_rand(0, $statistic['totalcount']);
db_insert('node_counter')->fields($statistic)->execute();
}
/**
* Handle the devel_generate_content_form request to kill all of the content.
* This is used by both the batch and non-batch branches of the code.
*
* @param $num
* array of options obtained from devel_generate_content_form.
*/
function devel_generate_content_kill($values) {
$results = db_select('node', 'n')
->fields('n', array('nid'))
->condition('type', $values['node_types'], 'IN')
->execute();
foreach ($results as $result) {
$nids[] = $result->nid;
}
if (!empty($nids)) {
node_delete_multiple($nids);
drupal_set_message(t('Deleted %count nodes.', array('%count' => count($nids))));
}
}
/**
* Pre-process the devel_generate_content_form request. This is needed so
* batch api can get the list of users once. This is used by both the batch
* and non-batch branches of the code.
*
* @param $num
* array of options obtained from devel_generate_content_form.
*/
function devel_generate_content_pre_node(&$results) {
// Get user id.
$users = devel_get_users();
$users = array_merge($users, array('0'));
$results['users'] = $users;
}
/**
* Create one node. Used by both batch and non-batch code branches.
*
* @param $num
* array of options obtained from devel_generate_content_form.
*/
function devel_generate_content_add_node(&$results) {
$node = new stdClass();
$node->nid = NULL;
// Insert new data:
$node->type = array_rand($results['node_types']);
node_object_prepare($node);
$users = $results['users'];
$node->uid = $users[array_rand($users)];
$type = node_type_get_type($node);
$node->title = $type->has_title ? devel_create_greeking(mt_rand(2, $results['title_length']), TRUE) : '';
$node->revision = mt_rand(0,1);
$node->promote = mt_rand(0, 1);
// Avoid NOTICE.
if (!isset($results['time_range'])) {
$results['time_range'] = 0;
}
devel_generate_set_language($results, $node);
$node->created = REQUEST_TIME - mt_rand(0, $results['time_range']);
// A flag to let hook_nodeapi() implementations know that this is a generated node.
$node->devel_generate = $results;
// Populate all core fields on behalf of field.module
module_load_include('inc', 'devel_generate', 'devel_generate.fields');
devel_generate_fields($node, 'node', $node->type);
// See devel_generate_nodeapi() for actions that happen before and after this save.
node_save($node);
}
/*
* Populate $object->language based on $results
*/
function devel_generate_set_language($results, $object) {
if (isset($results['add_language'])) {
$languages = array_keys($results['add_language']);
$object->language = $languages[array_rand($languages)];
}
else {
$default = language_default('language');
$object->language = $default == 'en' ? LANGUAGE_NONE : $default;
}
}
name = Devel generate
description = Generate dummy users, nodes, and taxonomy terms.
package = Development
core = 7.x
dependencies[] = devel
tags[] = developer
<?php
/**
* Implementation of hook_menu().
*/
function devel_generate_menu() {
$items = array();
$items['admin/config/development/generate/user'] = array(
'title' => 'Generate users',
'description' => 'Generate a given number of users. Optionally delete current users.',
'page callback' => 'drupal_get_form',
'page arguments' => array('devel_generate_users_form'),
'access arguments' => array('administer users'),
);
$items['admin/config/development/generate/content'] = array(
'title' => 'Generate content',
'description' => 'Generate a given number of nodes and comments. Optionally delete current items.',
'page callback' => 'drupal_get_form',
'page arguments' => array('devel_generate_content_form'),
'access arguments' => array('administer nodes'),
);
if (module_exists('taxonomy')) {
$items['admin/config/development/generate/taxonomy'] = array(
'title' => 'Generate terms',
'description' => 'Generate a given number of terms. Optionally delete current terms.',
'page callback' => 'drupal_get_form',
'page arguments' => array('devel_generate_term_form'),
'access arguments' => array('administer taxonomy'),
);
$items['admin/config/development/generate/vocabs'] = array(
'title' => 'Generate vocabularies',
'description' => 'Generate a given number of vocabularies. Optionally delete current vocabularies.',
'page callback' => 'drupal_get_form',
'page arguments' => array('devel_generate_vocab_form'),
'access arguments' => array('administer taxonomy'),
);
}
$items['admin/config/development/generate/menu'] = array(
'title' => 'Generate menus',
'description' => 'Generate a given number of menus and menu links. Optionally delete current menus.',
'page callback' => 'drupal_get_form',
'page arguments' => array('devel_generate_menu_form'),
'access arguments' => array('administer menu'),
);
return $items;
}
function devel_generate_users_form() {
$form['num'] = array(
'#type' => 'textfield',
'#title' => t('How many users would you like to generate?'),
'#default_value' => 50,
'#size' => 10,
);
$form['kill_users'] = array(
'#type' => 'checkbox',
'#title' => t('Delete all users (except user id 1) before generating new users.'),
'#default_value' => FALSE,
);
$options = user_roles(TRUE);
unset($options[DRUPAL_AUTHENTICATED_RID]);
$form['roles'] = array(
'#type' => 'checkboxes',
'#title' => t('Which roles should the users receive?'),
'#description' => t('Users always receive the <em>authenticated user</em> role.'),
'#options' => $options,
);
$options = array(1 => t('Now'));
foreach (array(3600, 86400, 604800, 2592000, 31536000) as $interval) {
$options[$interval] = format_interval($interval, 1) . ' ' . t('ago');
}
$form['time_range'] = array(
'#type' => 'select',
'#title' => t('How old should user accounts be?'),
'#description' => t('User ages will be distributed randomly from the current time, back to the selected time.'),
'#options' => $options,
'#default_value' => 604800,
);
$form['submit'] = array(
'#type' => 'submit',
'#value' => t('Generate'),
);
return $form;
}
function devel_generate_users_form_submit($form_id, &$form_state) {
module_load_include('inc', 'devel_generate');
$values = $form_state['values'];
devel_create_users($values['num'], $values['kill_users'], $values['time_range'], $values['roles']);
}
function devel_generate_content_form() {
$options = array();
if (module_exists('content')) {
$types = content_types();
foreach ($types as $type) {
$warn = '';
if (count($type['fields'])) {
$warn = t('. This type contains CCK fields which will only be populated by fields that implement the content_generate hook.');
}
$options[$type['type']] = t($type['name']). $warn;
}
}
else {
$types = node_type_get_types();
$suffix = '';
foreach ($types as $type) {
if (module_exists('comment')) {
$default = variable_get('comment_' . $type->type, COMMENT_NODE_OPEN);
$map = array(t('Hidden'), t('Closed'), t('Open'));
$suffix = '<small>. ' . t('Comments: ') . $map[$default]. '</small>';
}
$options[$type->type] = t($type->name) . $suffix;
}
}
// we cannot currently generate valid polls.
unset($options['poll']);
if (empty($options)) {
drupal_set_message(t('You do not have any content types that can be generated. <a href="@create-type">Go create a new content type</a> already!</a>', array('@create-type' => url('admin/structure/types/add'))), 'error', FALSE);
return;
}
$form['node_types'] = array(
'#type' => 'checkboxes',
'#title' => t('Content types'),
'#options' => $options,
'#default_value' => array_keys($options),
);
if (module_exists('checkall')) $form['node_types']['#checkall'] = TRUE;
$form['kill_content'] = array(
'#type' => 'checkbox',
'#title' => t('<strong>Delete all content</strong> in these content types before generating new content.'),
'#default_value' => FALSE,
);
$form['num_nodes'] = array(
'#type' => 'textfield',
'#title' => t('How many nodes would you like to generate?'),
'#default_value' => 50,
'#size' => 10,
);
$options = array(1 => t('Now'));
foreach (array(3600, 86400, 604800, 2592000, 31536000) as $interval) {
$options[$interval] = format_interval($interval, 1) . ' ' . t('ago');
}
$form['time_range'] = array(
'#type' => 'select',
'#title' => t('How far back in time should the nodes be dated?'),
'#description' => t('Node creation dates will be distributed randomly from the current time, back to the selected time.'),
'#options' => $options,
'#default_value' => 604800,
);
$form['max_comments'] = array(
'#type' => module_exists('comment') ? 'textfield' : 'value',
'#title' => t('Maximum number of comments per node.'),
'#description' => t('You must also enable comments for the content types you are generating. Note that some nodes will randomly receive zero comments. Some will receive the max.'),
'#default_value' => 0,
'#size' => 3,
'#access' => module_exists('comment'),
);
$form['title_length'] = array(
'#type' => 'textfield',
'#title' => t('Max word length of titles'),
'#default_value' => 4,
'#size' => 10,
);
$form['add_alias'] = array(
'#type' => 'checkbox',
'#disabled' => !module_exists('path'),
'#description' => t('Requires path.module'),
'#title' => t('Add an url alias for each node.'),
'#default_value' => FALSE,
);
$form['add_statistics'] = array(
'#type' => 'checkbox',
'#title' => t('Add statistics for each node (node_counter table).'),
'#default_value' => TRUE,
'#access' => module_exists('statistics'),
);
unset($options);
$options[LANGUAGE_NONE] = t('Language neutral');
if (module_exists('locale')) {
$options += locale_language_list();
}
$form['add_language'] = array(
'#type' => 'select',
'#title' => t('Set language on nodes'),
'#multiple' => TRUE,
'#disabled' => !module_exists('locale'),
'#description' => t('Requires locale.module'),
'#options' => $options,
'#default_value' => array(LANGUAGE_NONE => LANGUAGE_NONE),
);
$form['submit'] = array(
'#type' => 'submit',
'#value' => t('Generate'),
);
$form['#redirect'] = FALSE;
return $form;
}
function devel_generate_content_form_submit($form_id, &$form_state) {
module_load_include('inc', 'devel_generate', 'devel_generate');
$form_state['values']['node_types'] = array_filter($form_state['values']['node_types']);
if ($form_state['values']['num_nodes'] <= 50 && $form_state['values']['max_comments'] <= 10) {
module_load_include('inc', 'devel_generate');
devel_generate_content($form_state);
}
else {
module_load_include('inc', 'devel_generate', 'devel_generate_batch');
devel_generate_batch_content($form_state);
}
}
function devel_generate_term_form() {
$options = array();
foreach (taxonomy_get_vocabularies() as $vid => $vocab) {
$options[$vid] = $vocab->machine_name;
}
$form['vids'] = array(
'#type' => 'select',
'#multiple' => TRUE,
'#title' => t('Vocabularies'),
'#required' => TRUE,
'#options' => $options,
'#description' => t('Restrict terms to these vocabularies.'),
);
$form['num_terms'] = array(
'#type' => 'textfield',
'#title' => t('Number of terms?'),
'#default_value' => 10,
'#size' => 10,
);
$form['title_length'] = array(
'#type' => 'textfield',
'#title' => t('Max word length of term names'),
'#default_value' => 12,
'#size' => 10,
);
$form['kill_taxonomy'] = array(
'#type' => 'checkbox',
'#title' => t('Delete existing terms in specified vocabularies before generating new terms.'),
'#default_value' => FALSE,
);
$form['submit'] = array(
'#type' => 'submit',
'#value' => t('Generate'),
);
return $form;
}
function devel_generate_vocab_form() {
$form['num_vocabs'] = array(
'#type' => 'textfield',
'#title' => t('Number of vocabularies?'),
'#default_value' => 1,
'#size' => 10,
);
$form['title_length'] = array(
'#type' => 'textfield',
'#title' => t('Max word length of vocabulary names'),
'#default_value' => 12,
'#size' => 10,
);
$form['kill_taxonomy'] = array(
'#type' => 'checkbox',
'#title' => t('Delete existing vocabularies before generating new ones.'),
'#default_value' => FALSE,
);
$form['submit'] = array(
'#type' => 'submit',
'#value' => t('Generate'),
);
return $form;
}
function devel_generate_term_form_submit($form_id, &$form_state) {
module_load_include('inc', 'devel_generate');
$vocabs = taxonomy_vocabulary_load_multiple($form_state['values']['vids']);
devel_generate_term_data($vocabs, $form_state['values']['num_terms'], $form_state['values']['title_length'], $form_state['values']['kill_taxonomy']);
}
function devel_generate_vocab_form_submit($form_id, &$form_state) {
module_load_include('inc', 'devel_generate');
devel_generate_vocab_data($form_state['values']['num_vocabs'], $form_state['values']['title_length'], $form_state['values']['kill_taxonomy']);
}
function devel_generate_node_insert($node) {
if (isset($node->devel_generate)) {
$results = $node->devel_generate;
if (!empty($results['max_comments']) && $node->comment >= COMMENT_NODE_OPEN) {
devel_generate_add_comments($node, $results['users'], $results['max_comments'], $results['title_length']);
}
// Add an url alias. Cannot happen before save because we don't know the nid.
if (!empty($results['add_alias'])) {
$path = array(
'source' => 'node/' . $node->nid,
'alias' => 'node-' . $node->nid . '-' . $node->type,
);
path_save($path);
}
// Add node statistics.
if (!empty($results['add_statistics']) && module_exists('statistics')) {
devel_generate_add_statistics($node);
}
}
}
function devel_generate_set_message($msg, $type = 'status') {
$function = function_exists('drush_log') ? 'drush_log' : 'drupal_set_message';
$function($msg, $type);
}
function devel_generate_menu_form() {
$menu_enabled = module_exists('menu');
if ($menu_enabled) {
$menus = array('__new-menu__' => t('Create new menu(s)')) + menu_get_menus();
}
else {
$menus = menu_list_system_menus();
}
$form['existing_menus'] = array(
'#type' => 'checkboxes',
'#title' => t('Generate links for these menus'),
'#options' => $menus,
'#default_value' => array('__new-menu__'),
'#required' => TRUE,
);
if ($menu_enabled) {
$form['num_menus'] = array(
'#type' => 'textfield',
'#title' => t('Number of new menus to create'),
'#default_value' => 2,
'#size' => 10,
'#states' => array(
'visible' => array(
':input[name=existing_menus[__new-menu__]]' => array('checked' => TRUE),
),
),
);
}
$form['num_links'] = array(
'#type' => 'textfield',
'#title' => t('Number of links to generate'),
'#default_value' => 50,
'#size' => 10,
'#required' => TRUE,
);
$form['title_length'] = array(
'#type' => 'textfield',
'#title' => t('Max word length of menu and menu link names'),
'#default_value' => 12,
'#size' => 10,
'#required' => TRUE,
);
$form['link_types'] = array(
'#type' => 'checkboxes',
'#title' => t('Types of links to generate'),
'#options' => array(
'node' => t('Nodes'),
'front' => t('Front page'),
'external' => t('External'),
),
'#default_value' => array('node', 'front', 'external'),
'#required' => TRUE,
);
$form['max_depth'] = array(
'#type' => 'select',
'#title' => t('Maximum link depth'),
'#options' => range(0, MENU_MAX_DEPTH),
'#default_value' => floor(MENU_MAX_DEPTH / 2),
'#required' => TRUE,
);
unset($form['max_depth']['#options'][0]);
$form['max_width'] = array(
'#type' => 'textfield',
'#title' => t('Maximum menu width'),
'#default_value' => 6,
'#size' => 10,
'#description' => t("Limit the width of the generated menu's first level of links to a certain number of items."),
'#required' => TRUE,
);
$form['kill'] = array(
'#type' => 'checkbox',
'#title' => t('Delete existing custom generated menus and menu links before generating new ones.'),
'#default_value' => FALSE,
);
$form['submit'] = array(
'#type' => 'submit',
'#value' => t('Generate'),
);
return $form;
}
function devel_generate_menu_form_submit($form_id, &$form_state) {
// If the create new menus checkbox is off, set the number of new menus to 0.
if (!isset($form_state['values']['existing_menus']['__new-menu__']) || !$form_state['values']['existing_menus']['__new-menu__']) {
$form_state['values']['num_menus'] = 0;
}
module_load_include('inc', 'devel_generate');
devel_generate_menu_data($form_state['values']['num_menus'], $form_state['values']['existing_menus'], $form_state['values']['num_links'], $form_state['values']['title_length'], $form_state['values']['link_types'], $form_state['values']['max_depth'], $form_state['values']['max_width'], $form_state['values']['kill']);
}
<?php
/**
* Devel Generate batch handling functions using the BatchAPI
*/
/**
* Functions called from FAPI:
*/
function devel_generate_batch_content($form_state) {
$operations = array();
// Setup the batch operations and save the variables.
$operations[] = array('devel_generate_batch_content_pre_node', array($form_state['values']));
// add the kill operation
if ($form_state['values']['kill_content']) {
$operations[] = array('devel_generate_batch_content_kill', array());
}
// add the operations to create the nodes
for ($num = 0; $num < $form_state['values']['num_nodes']; $num ++) {
$operations[] = array('devel_generate_batch_content_add_node', array());
}
// start the batch
$batch = array(
'title' => t('Generating Content'),
'operations' => $operations,
'finished' => 'devel_generate_batch_finished',
'file' => drupal_get_path('module', 'devel_generate') . '/devel_generate_batch.inc',
);
batch_set($batch);
}
/**
* Create Content Batch Functions:
*/
function devel_generate_batch_content_kill(&$context) {
module_load_include('inc', 'devel_generate', 'devel_generate');
devel_generate_content_kill($context['results']);
}
function devel_generate_batch_content_pre_node($vars, &$context) {
$context['results'] = $vars;
$context['results']['num_nids'] = 0;
module_load_include('inc', 'devel_generate', 'devel_generate');
devel_generate_content_pre_node($context['results']);
}
function devel_generate_batch_content_add_node(&$context) {
module_load_include('inc', 'devel_generate', 'devel_generate');
devel_generate_content_add_node($context['results']);
$context['results']['num_nids'] ++;
}
function devel_generate_batch_finished($success, $results, $operations) {
if ($success) {
$message = t('Finished @num_nids nodes created successfully.', array('@num_nids' => $results['num_nids']));
}
else {
$message = t('Finished with an error.');
}
drupal_set_message($message);
}