[ Index ]

PHP Cross Reference of Drupal 6 (gatewave)

title

Body

[close]

/sites/all/modules/sitedoc/ -> sitedoc.module (source)

   1  <?php
   2  // $Id: sitedoc.module,v 1.46.2.2.2.11 2008/09/16 13:23:47 nancyw Exp $
   3  
   4  /**
   5   * Implementation of hook_help().
   6   */
   7  function sitedoc_help($path, $args = array()) {
   8    switch ($path) {
   9      case 'admin/help#sitedoc':
  10        $output = '<p>'. t('The sitedoc module allows a site admin to gather extensive information about their site. It may be printed or archived for configuration/change management purposes. It may also be used to add "fancy" and up-to-date information to other site documentation, such as a succession plan.') .'</p>';
  11        break;
  12        
  13      case 'admin/settings/sitedoc/archive':
  14        $output = '<p>'. t('The Site Documentation module may be run via Cron and the output HTML file archived to disk for future review. The options chosen on the main settings page will govern the data collected and reported.') .'</p>';
  15        break;
  16        
  17      case 'admin/settings/sitedoc':
  18        $output = '<p>'. t("The Site Documentation module's data collection and output is governed by these settings. Indented options are in effect only if the parent item is selected.") .'</p>';
  19    }
  20    return $output;
  21  }
  22  
  23  /**
  24   * Implementation of hook_perm().
  25   */
  26  function sitedoc_perm() {
  27    return array('view site documentation');
  28  }
  29  
  30  /**
  31   * Implementation of hook_menu().
  32   */
  33  function sitedoc_menu() {
  34    $items = array();
  35  
  36    $items['admin/build/sitedoc'] = array(
  37      'title' => 'Site documentation',
  38      'description' => 'Show site documentation',
  39      'page callback' => 'sitedoc_list',
  40      'access arguments' => array('view site documentation'),
  41      );
  42    $items['admin/settings/sitedoc'] = array(
  43      'title' => 'Site documentation',
  44      'description' => 'Set how Site Documentation works',
  45      'page callback' => 'sitedoc_settings_page',
  46      'access arguments' => array('view site documentation'),
  47      'file' => 'sitedoc.admin.inc',
  48      );  
  49    $items['admin/settings/sitedoc/report'] = array(
  50      'title' => 'Report',
  51      'description' => 'Report settings',
  52      'page callback' => 'drupal_get_form',
  53      'page arguments' => array('sitedoc_settings_form'),
  54      'access arguments' => array('view site documentation'),
  55      'type' => MENU_DEFAULT_LOCAL_TASK,
  56      'file' => 'sitedoc.admin.inc',
  57      );  
  58    $items['admin/settings/sitedoc/archive'] = array(
  59      'title' => 'Archive',
  60      'access arguments' => array('view site documentation'),
  61      'page callback' => 'drupal_get_form',
  62      'page arguments' => array('sitedoc_archive_form'),
  63      'description' => 'Set how Site Documentation archive works',
  64      'weight' => 2,
  65      'type' => MENU_LOCAL_TASK,
  66      'file' => 'sitedoc.admin.inc',
  67      );
  68    $items['admin/build/sitedoc_node_access_view'] = array(
  69      'title' => 'Site documentation node access list',
  70      'page callback' => 'sitedoc_node_access_view',
  71      'page arguments' => array(arg(3)),
  72      'access arguments' => array('view site documentation'),
  73      'type' => MENU_CALLBACK,
  74      );
  75    $items['sitedoc/vocabulary'] = array(
  76      'title' => 'Site Documentation Term Count by Type',
  77      'page callback' => 'sitedoc_term_count_by_type',
  78      'access arguments' => array('access content'),
  79      'type' => MENU_CALLBACK
  80      );
  81    $items['sitedoc/phpinfo'] = array(
  82      'title' => 'Site Documentation PHP Information',
  83      'page callback' => 'sitedoc_phpinfo',
  84      'access arguments' => array('view site documentation'),
  85      'type' => MENU_CALLBACK
  86      );
  87    $items['sitedoc/table'] = array(
  88      'title' => 'Table Contents',
  89      'page callback' => 'sitedoc_show_table',
  90      'access arguments' => array('view site documentation'),
  91      'type' => MENU_CALLBACK
  92      );
  93  
  94    return $items; 
  95  }
  96  /**
  97   * Implementation of hook_init().
  98   */
  99  function sitedoc_init() {
 100    drupal_add_css(drupal_get_path('module', 'sitedoc') .'/sitedoc.css');
 101  }
 102  
 103  /**
 104   * Implementation of hook_enable().
 105   *
 106   * Set the default parameters. This is an easy way to reset the deaults - disable and re-enable.
 107   *
 108   *  Parameters: None.
 109   *
 110   *  Return: None.
 111   */
 112  function sitedoc_enable() {
 113    $sitedoc_settings = array(
 114      'drupal_section' => TRUE,
 115      'kill_cron' => 0,                     /* kill long-running cron */
 116      'table_section' => TRUE,
 117      'show_indexes' => FALSE,              /* use SHOW INDEX on the tables */
 118      'optimize_tables' => FALSE,           /* use OPTIMIZE to release overhead */
 119      'node_section' => FALSE,              /* not on because of expense */
 120      'include_comment_count' => FALSE,     /* comment count on nodes */
 121      'include_node_access' => FALSE,       /* node access summary */
 122      'node_show_size' => 99999,            /* show nodes exceeding x KB */
 123      'node_max_size' => 50,                /* warn if nodes exceeding x KB */
 124      'module_section' => TRUE,
 125      'module_suppress' => FALSE,           /* exclude disabled modules */
 126      'module_sort_order' => 0,             /* package, project, module */
 127      'content_section' => TRUE,
 128      'vocabulary_section' => TRUE,
 129      'orphan_term_node' => FALSE,          /* not on to allow control */
 130      'delete_orphan_term_nodes' => FALSE,  /* do not delete orphans  */
 131      'theme_section' => TRUE,
 132      'variables_section' => FALSE,         /* not on because of expense */
 133      'block_section' => TRUE,
 134      'block_warn' => TRUE,                 /* warn on missing theme */
 135      'block_delete' => FALSE,              /* delete those orphan blocks */
 136      'roles_section' => TRUE,
 137      'role_users' => TRUE,                 /* list users in roles */
 138      'role_perms_list' => FALSE,           /* do role permissions as stream */
 139      'contacts_section' => TRUE,
 140      'profile_fields_section' => FALSE,    /* not on because of interest */
 141      'url_alias_section' => FALSE,         /* not on because of expense */
 142      'input_format_section' => FALSE,      /* not on because probably not wanted */
 143      // Archive options
 144      'archive_frequency' => 0,             /* don't run */
 145      'archive_directory' => 'sitedoc',     /* archive directory within file setting */ 
 146      );
 147    variable_set('sitedoc_settings', $sitedoc_settings);
 148  }
 149  
 150  /***********************/
 151  /**   main function   **/
 152  /***********************/
 153  function sitedoc_list() {
 154    // Retrieve the settings and check if they have been settings page.
 155    $sitedoc_settings = variable_get('sitedoc_settings', array());
 156    if ($sitedoc_settings == array()) {
 157      return t('It appears that you have not been to "administer >> site configuration" to set the Site Documentation settings. ') . l(t('Click here to do so.'), 'admin/settings/sitedoc');
 158    }
 159  
 160    // Insert a wrapper division and a link back to the settings page. 
 161    $output = '<div class="site-documentation">'. l(t('Change settings.'), 'admin/settings/sitedoc') .'<br/><br/>';
 162  
 163    // Get basic Drupal info.
 164    if ($sitedoc_settings['drupal_section']) {
 165      $output .= '<div class="sitedoc_drupal">';
 166      $fieldset = array(
 167        '#title' => t('Drupal Section'),
 168        '#collapsible' => TRUE,
 169        '#collapsed' => FALSE,
 170        '#value' => sitedoc_drupal($sitedoc_settings['kill_cron']),
 171        );  
 172      $output .=  theme('fieldset', $fieldset);
 173      $output .= "</div>\n";
 174    }
 175  
 176    // Get database info.
 177    if ($sitedoc_settings['table_section']) {
 178      $output .= '<div class="sitedoc_database_overview">';
 179      $fieldset = array(
 180        '#title' => t('Table Section'),
 181        '#collapsible' => TRUE,
 182        '#collapsed' => FALSE,
 183        '#value' => sitedoc_database_overview($sitedoc_settings['optimize_tables'], $sitedoc_settings['show_indexes']),
 184        );  
 185      $output .=  theme('fieldset', $fieldset);
 186      $output .= "</div>\n";
 187    }
 188   
 189    // Get summary of all nodes (this is VERY expensive!).
 190    if ($sitedoc_settings['node_section']) {
 191      $output .= '<div class="sitedoc_node_summary">';
 192      $fieldset = array(
 193        '#title' => t('Node Summary'),
 194        '#collapsible' => TRUE,
 195        '#collapsed' => FALSE,
 196        '#value' => sitedoc_node_summary($sitedoc_settings['include_comment_count'], $sitedoc_settings['node_show_size'], $sitedoc_settings['node_max_size']),
 197        );
 198      $output .=  theme('fieldset', $fieldset);
 199      $output .= "</div>\n";
 200   
 201      // Get node access summary (this is expensive!).
 202      // Runs only if the node summary section is selected.
 203      if ($sitedoc_settings['include_node_access']) {
 204        $output .= '<div class="sitedoc_node_access">';
 205        $fieldset = array(
 206          '#title' => t('Node Access'),
 207          '#collapsible' => TRUE,
 208          '#collapsed' => FALSE,
 209          '#value' => sitedoc_node_access(),
 210        );  
 211        $output .=  theme('fieldset', $fieldset);
 212        $output .= "</div>\n";
 213      } // end access summary 
 214    }  // end node summary 
 215  
 216    // Get content type info from node module along with workflow options from system variables.
 217    if ($sitedoc_settings['content_section']) {
 218      $output .= '<div class="sitedoc_content_types">';
 219      $fieldset = array(
 220        '#title' => t('Content Types'),
 221        '#collapsible' => TRUE,
 222        '#collapsed' => FALSE,
 223        '#value' => sitedoc_content_types(),
 224        );  
 225      $output .=  theme('fieldset', $fieldset);
 226      $output .= "</div>\n";
 227    }
 228  
 229    // Get modules from the System module functions.
 230    if ($sitedoc_settings['module_section']) {
 231      $output .= '<div class="sitedoc_modules">';
 232      $fieldset = array(
 233        '#title' => t('Modules'),
 234        '#collapsible' => TRUE,
 235        '#collapsed' => FALSE,
 236        '#value' => sitedoc_get_modules($sitedoc_settings['module_suppress'], $sitedoc_settings['module_sort_order']),
 237        );  
 238      $output .=  theme('fieldset', $fieldset);
 239      $output .= "</div>\n";
 240    }
 241  
 242    // Get themes from the System table.
 243    if ($sitedoc_settings['theme_section']) {  /* controls themes */
 244      $output .= '<div class="sitedoc_themes">';
 245      $fieldset = array(
 246        '#title' => t('Themes'),
 247        '#collapsible' => TRUE,
 248        '#collapsed' => FALSE,
 249        '#value' => sitedoc_get_system('theme'),
 250        );  
 251      $output .=  theme('fieldset', $fieldset);
 252      $output .= "</div>\n";
 253    }
 254  
 255    // Get vocabularies and their definitions.
 256    if ($sitedoc_settings['vocabulary_section']) {
 257      $output .= '<div class="sitedoc_vocabularies">';
 258      $fieldset = array(
 259        '#title' => t('Vocabularies'),
 260        '#collapsible' => TRUE,
 261        '#collapsed' => FALSE,
 262        '#value' => sitedoc_get_vocabularies(),
 263        );  
 264      $output .=  theme('fieldset', $fieldset);
 265  
 266       // do we want to check for orphans in the Term_node table?
 267      if ($sitedoc_settings['orphan_term_node']) {
 268        $orphans = sitedoc_check_orphan_term_node($sitedoc_settings['delete_orphan_term_nodes']);
 269        $fieldset = array(
 270          '#title' => t('Orphan Term_nodes'),
 271          '#collapsible' => TRUE,
 272          '#collapsed' => ($orphans == '<p>'. t('No orphan term_nodes found.') .'</p>'),
 273          '#value' => $orphans,
 274        );  
 275      $output .=  theme('fieldset', $fieldset);
 276      }
 277      $output .= "</div>\n";
 278    }
 279  
 280    // Get system variable info from cache.
 281    if ($sitedoc_settings['variables_section']) {
 282      $output .= '<div class="sitedoc_variables">';
 283      $fieldset = array(
 284        '#title' => t('Variables'),
 285        '#collapsible' => TRUE,
 286        '#collapsed' => FALSE,
 287        '#value' => sitedoc_get_variables(),
 288        );  
 289      $output .=  theme('fieldset', $fieldset);
 290      $output .= "</div>\n";
 291    }
 292  
 293    // Get blocks info from Blocks table.
 294    if ($sitedoc_settings['block_section']) {  /* controls block and boxes */
 295      $output .= '<div class="sitedoc_blocks">';
 296        // use the setting to decide on a warning for missing themes
 297      $fieldset = array(
 298        '#title' => t('Blocks'),
 299        '#collapsible' => TRUE,
 300        '#collapsed' => FALSE,
 301        '#value' => sitedoc_get_blocks($sitedoc_settings['block_warn'], $sitedoc_settings['block_delete']),
 302      );  
 303      $output .=  theme('fieldset', $fieldset);
 304      $output .= "</div>\n";
 305  
 306      // Get boxes (custom blocks) info from Boxes table.
 307      // Runs only if the blocks section is selected.
 308      $output .= '<div class="sitedoc_boxes">';
 309      $boxes = sitedoc_get_boxes($sitedoc_settings['block_warn'], $sitedoc_settings['block_delete']);
 310      $fieldset = array(
 311        '#title' => t('Custom Blocks (Boxes)'),
 312        '#collapsible' => TRUE,
 313        '#collapsed' => FALSE,
 314        '#value' => $boxes ? $boxes : ('<p>'. t('No boxes found.') .'</p>'),
 315        );
 316      $output .=  theme('fieldset', $fieldset);
 317      $output .= "</div>\n";
 318    }
 319  
 320    // Get roles info from Roles table  (how can we get a description of the roles?).
 321    if ($sitedoc_settings['roles_section']) {
 322      $output .= '<div class="sitedoc_roles">';
 323      $rpt .= sitedoc_get_roles($sitedoc_settings['role_perms_list'], $sitedoc_settings['role_users']);
 324      $fieldset = array(
 325        '#title' => t('Roles'),
 326        '#collapsible' => TRUE,
 327        '#collapsed' => FALSE,
 328        '#value' => $rpt ? $rpt : (t('No roles found.') .' '. _sitedoc_img_error()),
 329        );
 330      $output .=  theme('fieldset', $fieldset);
 331      $output .= "</div>\n";
 332    }
 333  
 334    // Get contacts info from Contacts table.
 335    if ($sitedoc_settings['contacts_section']) {
 336      $output .= '<div class="sitedoc_contacts">';
 337      $rpt = sitedoc_get_contacts();
 338      $fieldset = array(
 339        '#title' => t('Contacts'),
 340        '#collapsible' => TRUE,
 341        '#collapsed' => FALSE,
 342        '#value' => $rpt ? $rpt : t('No contacts found.'),
 343        );
 344      $output .=  theme('fieldset', $fieldset);
 345      $output .= "</div>\n";
 346    }
 347  
 348    // Get info from Profile_fields table.
 349    if ($sitedoc_settings['profile_fields_section']) {
 350      $output .= '<div class="sitedoc_profile_fields">';
 351      $rpt = sitedoc_profile_fields();
 352      $fieldset = array(
 353        '#title' => t('Profile Fields'),
 354        '#collapsible' => TRUE,
 355        '#collapsed' => FALSE,
 356        '#value' => $rpt ? $rpt : ('<p>'. t('No profile fields found.') .'</p>'),
 357        );
 358      $output .=  theme('fieldset', $fieldset);
 359      $output .= "</div>\n";
 360    }
 361  
 362    // Get info from URL Alias table and cross-check it.
 363    if ($sitedoc_settings['url_alias_section']) {
 364      $output .= '<div class="sitedoc_url_alias">';
 365      $rpt = sitedoc_url_alias();
 366      $fieldset = array(
 367        '#title' => t('URL Aliases'),
 368        '#collapsible' => TRUE,
 369        '#collapsed' => FALSE,
 370        '#value' => $rpt ? $rpt : t('No aliases found.'),
 371        );
 372      $output .=  theme('fieldset', $fieldset);
 373      $output .= "</div>\n";
 374    }
 375  
 376    // Get info from Filter and Filter_format tables.
 377    if ($sitedoc_settings['input_format_section']) {
 378      $output .= '<div class="sitedoc_input_format">';
 379      $fieldset = array(
 380        '#title' => t('Input Formats and Filters'),
 381        '#collapsible' => TRUE,
 382        '#collapsed' => FALSE,
 383        '#value' => sitedoc_filters(),
 384        );
 385      $output .=  theme('fieldset', $fieldset);
 386      $output .= "</div>\n";
 387    }
 388  
 389    $output .= "</div>\n";
 390    return $output;
 391  
 392  }
 393  /**
 394   *  Implementation of hook_cron
 395   *
 396   *  Runs the report and saves it to disk.
 397   */
 398  function sitedoc_cron() {
 399    // Get the module settings and verify they've been set.
 400    $sitedoc_settings = variable_get('sitedoc_settings', array());
 401    if ($sitedoc_settings == array()) {
 402      watchdog('Site Doc', 'It appears that you have not been to "administer >> site configuration" to set the Site Documentation settings.', null, WATCHDOG_ERROR);
 403    }
 404  
 405    // If set up for manual run, then just quit now.
 406    $frequency = $sitedoc_settings['archive_frequency'];
 407    if ($frequency == 0) {
 408      return;
 409    }
 410  
 411    // If set up for debugging, then -1 will let us run every time.
 412    if ($frequency == '999999') {
 413      $frequency = -1;
 414    }
 415  
 416    // Determine if it's time to make another run.
 417    $how_long = time() - $sitedoc_settings['archive_last_run'];
 418  
 419    if ($how_long > $frequency) { /* longer than how long between saves? */
 420      // Get the file path and append a file name.
 421      $filename = file_directory_path() .'/' 
 422        . $sitedoc_settings['archive_directory'] .'/sitedoc_'. date('Ymd_Hi') .'.html';
 423      $path = $filename;
 424      // make sure we can write to it        
 425      $writable = fopen($path, 'w');
 426      if ($writable == FALSE) {
 427        watchdog('Site Doc', 'Cannot write "!file" to archive directory.', array('!file' => $filename), WATCHDOG_ERROR);
 428        return;
 429      }
 430  
 431      // Call the main list, format the output as a full page (without blocks),
 432      //  change all the relative paths to full paths, fix the page title.
 433      $output = theme('page', sitedoc_list(), FALSE);
 434      global $base_url;
 435      $output = str_replace('@import "/', '@import "'. $base_url .'/', $output);
 436      $output = str_replace('src="/', 'src="'. $base_url .'/', $output);
 437      $output = str_replace('<title>Run cron', '<title>Site Documentation', $output);
 438  
 439      // Save the file and write a message indicating whether it worked.
 440      $saved_as = fwrite($writable, $output);
 441      if ($saved_as === 0) {
 442        watchdog('Site Doc', 'Documentation file save failed.', null, WATCHDOG_ERROR);
 443      }
 444      else {
 445        watchdog('Site Doc', 'Documentation file saved as !name', array('!name' => $filename), WATCHDOG_NOTICE);
 446      }
 447      fclose($writable);
 448  
 449      // update last run time
 450      $sitedoc_settings['archive_last_run'] = time();
 451      variable_set('sitedoc_settings', $sitedoc_settings); 
 452  
 453    } /* end if how long */
 454  }
 455  
 456  /**
 457   * Helper functions to generate proper img tags.
 458   */
 459  function _sitedoc_img_ok() {
 460    return theme_image('misc/watchdog-ok.png', 'ok', 'ok');
 461  }
 462  function _sitedoc_img_warning() {
 463    return theme_image('misc/watchdog-warning.png', 'warning', 'warning');
 464  }
 465  
 466  function _sitedoc_img_error() {
 467    return theme_image('misc/watchdog-error.png', 'error', 'error');
 468  }
 469  
 470  /****************************/
 471  /**   Callable functions   **/
 472  /****************************/
 473  
 474  /**
 475   *   Produce the basic system overview.
 476   *
 477   *  Parameters:
 478   *    TRUE/FALSE - whether to fix Cron stall problem if detected.
 479   *
 480   *  Return: HTML string.
 481   */
 482  function sitedoc_drupal($kill_cron=0) {
 483    global $base_url;
 484    $dest = drupal_get_destination();
 485    $_abled = array(t('Disabled'), t('Enabled'));
 486    $_bool = array(t('False'), t('True'));
 487    $_noyes = array(t('No'), t('Yes'));
 488  
 489    $header = array(t('Item'), t('Value'), t('Operation'));
 490    $setting = t('Settings');
 491    $rows = array();
 492  
 493    $rows[] = array(t('Site Name'), variable_get('site_name', 'none'), l($setting, 'admin/settings/site-information', array('query' => $dest)));
 494    $rows[] = array(t('Version'), VERSION, NULL);
 495    $rows[] = array(t('Configuration file'), conf_path() .'/settings.php', NULL);
 496    $rows[] = array(t('Install profile'), variable_get('install_profile', 'default'), NULL);
 497    $rows[] = array(t('Base URL'), $base_url, NULL);
 498  
 499    $cache = variable_get('cache', 0);
 500    $cache_type = array(t('Disabled'), t('Normal'), t('Agressive'));
 501    $rows[] = array(t('Cache'), $cache_type[$cache], l($setting, 'admin/settings/performance', array('query' => $dest)));
 502    $rows[] = array(t('Minimum cache lifetime'), format_interval(variable_get('cache_lifetime', 0), 1), l($setting, 'admin/settings/performance', array('query' => $dest)));
 503    $rows[] = array(t('CSS preprocess'), variable_get('preprocess_css', FALSE) ? t('Enabled') : t('Disabled'), l($setting, 'admin/settings/performance', array('query' => $dest)));
 504  
 505    $clean_urls = variable_get('clean_url', FALSE);
 506    $rows[] = array(t('Clean URLS'), $_abled[$clean_urls], l($setting, 'admin/settings/clean-urls', array('query' => $dest)));
 507  
 508    $cron_last = variable_get('cron_last', NULL);
 509    $cron_when = isset($cron_last) ? t('Last run !time ago', array('!time' => format_interval(time() - $cron_last))) : t('Not run yet');
 510    // If it last ran more than an hour ago, add "Run now" link.
 511    if (time() - $cron_last > 3600) {
 512      $cron_op .= l(t('Run now'), 'admin/reportsstatus/run-cron', array('query' => $dest));
 513    }
 514    else {
 515      $cron_op = NULL;
 516    } 
 517    $cron_semaphore = variable_get('cron_semaphore', NULL);  /* is it running now? */
 518    if (isset($cron_semaphore)) { 
 519      $how_long = time() - $cron_semaphore;  /* how long it's been running (secs) */
 520      $cron_when .= ' - '. t('Running for !time', array('!time' => format_interval($how_long)))
 521                    ._sitedoc_img_warning();
 522      if ($kill_cron) {
 523        if ($kill_cron <= $how_long) {
 524          variable_del('cron_semaphore');  /* turn off "cron started" flag */
 525          variable_del('cron_last');       /** I don't think this is actually needed **/
 526        } /* end if how long */
 527      }  /* end if kill_cron */
 528    }  /* end if semaphore */
 529    $rows[] = array(t('Cron'), $cron_when, $cron_op);
 530  
 531    if (module_exists('search')) {
 532      $rows[] = array(t('Search Cron limit'), variable_get("search_cron_limit", null), l($setting, 'admin/settings/search', array('query' => $dest)));
 533      $remaining = 0;
 534      $total = 0;
 535      foreach (module_list() as $module) {
 536        if (module_hook($module, 'search')) {
 537          $status = module_invoke($module, 'search', 'status');
 538          $remaining += $status['remaining'];
 539          $total += $status['total'];
 540        }
 541      }
 542      $count = format_plural($remaining, 'There is 1 item left to index.', 'There are @count items left to index.');
 543      $percentage = ((int)min(100, 100 * ($total - $remaining) / max(1, $total))) .'%';
 544      $status = t('%percentage of the site has been indexed.', array('%percentage' => $percentage)) .' '. $count;
 545      $link = l($setting, 'admin/settings/search', array('query' => $dest));
 546      if ($remaining) {
 547        $link .= ', '. l(t('Run Cron'), 'admin/reports/status/run-cron', array('query' => $dest));
 548      }
 549      $rows[] = array(t('Search status'), $status, $link);
 550    }
 551  
 552    $rows[] = array(t('Anonymous'), variable_get('anonymous', 'anonymous'), l($setting, 'admin/settings/site-information', array('query' => $dest))); 
 553    $rows[] = array(t('File directory'), file_directory_path(), l($setting, 'admin/settings/file-system', array('query' => $dest)));
 554  
 555    if (variable_get('file_downloads', FILE_DOWNLOADS_PUBLIC) == FILE_DOWNLOADS_PUBLIC) {
 556      $downloads = t('Public'); 
 557      $download_op = NULL;
 558    }
 559    else {
 560      $downloads = t('Private') ._sitedoc_img_warning(); 
 561      $download_op = l($setting, 'admin/settings/file-system', array('query' => $dest));
 562    }  
 563  
 564    $rows[] = array(t('Download method'), $downloads, $download_op);
 565  
 566    // Is the menu choice restricted?
 567    $menu_restrict = variable_get('menu_parent_items', NULL);  
 568    // "Show all menus" is Zero. Unless the setting has been changed it won't be present (NULL)
 569    if ($menu_restrict) {
 570      $menu_msg = t('Restricted') .' ('. $menu_restrict .') '. _sitedoc_img_warning();
 571    }
 572    else {
 573      $menu_msg = t('Show all menus') ._sitedoc_img_ok();
 574    }
 575    $rows[] = array(t('Content menu links'), $menu_msg, l($setting, 'admin/build/menu/settings', array('query' => $dest)));
 576  
 577    $rows[] = array(t('Default theme'), variable_get('theme_default', 'standard'), l($setting, 'admin/build/themes', array('query' => $dest)));
 578    
 579    $teaser_len = variable_get('teaser_length', 300);
 580    $rows[] = array(t('Teaser length'), $teaser_len ? $teaser_len : t('unlimited'), l($setting, 'admin/content/node-settings', array('query' => $dest)));
 581    
 582    // Get default filter and then look up its name.
 583    // Using 0 here to identify that it hasn't been set.
 584    $filter = $filter_default = variable_get('filter_default_format', 0);
 585    if ($filter_default == 0) {
 586      $filter = 1;
 587    }
 588    $filter_name = db_fetch_object(db_query_range("SELECT f.name FROM {filter_formats} f WHERE f.format='%d'", $filter, 0, 1));
 589    if ($filter_default == 1) {
 590      $filter_warn = NULL;
 591      $filter_op = NULL;
 592    }
 593    else {
 594      $filter_warn = ' <em>('. t('not set') .')</em>'. _sitedoc_img_warning() .' ';
 595      $filter_op = l($setting, 'admin/settings/filters', array('query' => $dest));
 596    }  
 597    $rows[] = array(t('Default filter format'), $filter_name->name . $filter_warn, $filter_op);
 598  
 599    $rows[] = array(t('Operating system'), php_uname('s'), NULL);
 600  
 601    $fieldset = array(
 602      '#title' => t('Drupal'),
 603      '#collapsible' => TRUE,
 604      '#collapsed' => FALSE,
 605      '#value' => theme('table', $header, $rows) ."\n",
 606      );
 607    $output .=  theme('fieldset', $fieldset);
 608  
 609    // Webserver information.
 610    $rows = array();
 611  
 612    $rows[] = array(t('Web server'), $_SERVER['SERVER_SOFTWARE'], NULL);
 613    if (strpos($_SERVER['SERVER_SOFTWARE'], 'Apache') !== FALSE) { /* is it Apache? */
 614      $rows[] = array(t('Server name'), $_SERVER['SERVER_NAME'], NULL);
 615      if (function_exists('apache_get_modules')) { /* can we get a list of modules ? */
 616        $rows[] = array(t('Apache modules'), implode(', ', apache_get_modules()), NULL);
 617      } /* end if function */
 618    } /* end if apache */
 619  
 620    $fieldset = array(
 621      '#title' => t('Webserver Information'),
 622      '#collapsible' => TRUE,
 623      '#collapsed' => TRUE,
 624      '#value' => theme('table', $header, $rows) ."\n",
 625    );
 626    $output .=  theme('fieldset', $fieldset);
 627    
 628    // Php info.
 629    $rows = array();
 630    $error_names = array(
 631      'E_ERROR' => 1,
 632      'E_WARNING' => 2,
 633      'E_PARSE' => 4,
 634      'E_NOTICE' => 8,
 635      'E_CORE_ERROR' => 16,
 636      'E_CORE_WARNING' => 32,
 637      'E_COMPILE_ERROR' => 64,
 638      'E_COMPILE_WARNING' => 128,
 639      'E_USER_ERROR' => 256,
 640      'E_USER_WARNING' => 512,
 641      'E_USER_NOTICE' => 1024,
 642      'E_STRICT' => 2048,
 643      'E_RECOVERABLE_ERROR' => 4096,
 644      );
 645    
 646    list($php_vers, $php_rel, $php_fix) = explode('.', phpversion());
 647  
 648    $rows[] = array(t('Php version'), phpversion(), l(t('View phpinfo'), 'sitedoc/phpinfo'));
 649    if ($php_vers < 6) {
 650      $safe_mode = ini_get('safe_mode');
 651      if (is_bool($safe_mode)) {
 652        $safe_mode = $_abled[$safe_mode];
 653      }
 654      if (empty($safe_mode)) {
 655        $safe_mode = $_abled[0];
 656      }
 657      $rows[] = array(t('Safe mode'), $safe_mode, NULL);
 658    }
 659  
 660    $disperr = ini_get('display_errors');
 661    if (is_bool($disperr)) {
 662      $disperr = $_noyes[$disperr];
 663    }
 664    if (empty($disperr)) {
 665      $disperr = $_noyes[0];
 666    }
 667    $rows[] = array(t('Display errors'), $disperr, NULL);
 668  
 669    $mem_limit = ini_get('memory_limit');
 670    $rows[] = array(t('Memory limit'), $mem_limit ? $mem_limit : t('default') .' '. _sitedoc_img_warning(), NULL);
 671  
 672    // Changing the error level returns the previous level, so we'll turn errors off then set it back as it was.
 673    $current = ini_get('error_reporting');
 674  
 675    $levels = array();
 676    if ($current == E_ALL) {
 677      $levels[] = 'E_ALL';
 678    }
 679    else {
 680      foreach ($error_names as $name => $bit) {
 681        if ($current && $bit) {
 682          $levels[] = $name;
 683        }
 684      }
 685    }
 686    $rows[] = array(t('Error reporting'), implode(' + ', $levels), NULL);
 687  
 688    $rows[] = array(t('Upload max filesize'), ini_get('upload_max_filesize'), NULL);
 689    $rows[] = array(t('Magic quotes GPC'), $_abled[ini_get('magic_quotes_gpc')], NULL);
 690    $rows[] = array(t('Magic quotes runtime'), $_abled[ini_get('magic_quotes_runtime')], NULL);
 691    $rows[] = array(t('Precision'), ini_get('precision'), NULL);
 692    $rows[] = array(t('Register globals'), $_abled[ini_get('register_globals')], NULL);
 693    $rows[] = array(t('Session cache limiter'), ini_get('session.cache_limiter'), NULL);
 694    $cookie_params = session_get_cookie_params();
 695    $rows[] = array(t('Session cookie domain'), !empty($cookie_params['domain']) ? $cookie_params['domain'] : t('- none -'), NULL);
 696    $rows[] = array(t('Session name'), session_name(), NULL);
 697    $rows[] = array(t('Session save handler'), ini_get('session.save_handler'), NULL);
 698  
 699     /* php extensions */
 700    if (extension_loaded('gd')) {
 701      $gd = gd_info();
 702      $rows[] = array(
 703        t('GD version'),
 704        $gd['GD Version'] .'<br />'
 705          . t('FreeType support') .' ('. $_abled[$gd['FreeType Support']] .'), '
 706          . t('Jpg support') .' ('. $_abled[$gd['JPG Support']] .'), '
 707          . t('Png support') .' ('. $_abled[$gd['PNG Support']] .')',
 708        NULL,
 709        );
 710    }
 711    else {
 712      $rows[] = array(t('GD support'), t('Disabled'));
 713    }
 714  
 715    if (extension_loaded('curl')) {
 716      $curl = curl_version();
 717      $rows[] = array(t('CURL version'), $curl['version'], NULL);
 718    }
 719    else {
 720      $rows[] = array(t('CURL support'), t('Disabled'), NULL);
 721    }
 722  
 723    $rows[] = array(t('Multibyte support'), $_abled[extension_loaded('mbstring')], NULL);
 724    $overload = ini_get('mbstring.func_overload');
 725    $oload = array();
 726    if ($overload & 1) {
 727      $oload[] = t('mail');
 728    }
 729    if ($overload & 2) {
 730      $oload[] = t('string');
 731    }
 732    if ($overload & 4) {
 733      $oload[] = t('regex');
 734    }
 735    $rows[] = array('Mbstring.func_overload', (empty($oload) ? t('disabled') : implode(', ', $oload)), NULL);
 736    $rows[] = array(t('XML support'), $_abled[extension_loaded('xml')], NULL);
 737    $rows[] = array(t('Zip support'), $_abled[extension_loaded('zip')], NULL);
 738    $rows[] = array(t('Zlib support'), $_abled[extension_loaded('zlib')], NULL);
 739  
 740    $fieldset = array(
 741      '#title' => t('PHP Information'),
 742      '#collapsible' => TRUE,
 743      '#collapsed' => TRUE,
 744      '#value' => theme('table', $header, $rows) ."\n",
 745    );
 746    $output .=  theme('fieldset', $fieldset);
 747  
 748    return $output;
 749  }
 750  
 751  /**
 752   *   Menu callback to produce PHPINFO page.
 753   */
 754  function sitedoc_phpinfo() {
 755    phpinfo(INFO_GENERAL | INFO_CONFIGURATION | INFO_ENVIRONMENT);
 756    return;
 757  }
 758  
 759  /**
 760   *   Produce the database tables overview and give overall size info
 761   *
 762   *  Parameters:
 763   *    none
 764   *
 765   *  Return: HTML string
 766   */
 767  function sitedoc_database_overview($optimize_tables=FALSE, $show_index=FALSE) {
 768    global $base_url, $db_prefix, $db_url;
 769  
 770  // $db_url may be an array if using multiple databases.
 771    if (is_array($db_url)) {
 772      foreach ($db_url as $key => $value) {
 773      $dbname = ucfirst($key);
 774         // Switch databases
 775      db_set_active($key);
 776      $fieldset = array(
 777        '#title' => $dbname,
 778        '#collapsible' => TRUE,
 779        '#collapsed' => TRUE,
 780        '#value' => _sitedoc_database($dbname, $value, $optimize_tables, $show_index),
 781        );
 782      $output .=  theme('fieldset', $fieldset);
 783      }
 784       // Return to default database
 785      db_set_active('default');
 786    }
 787    else {
 788      $fieldset = array(
 789        '#title' => t('Database Information'),
 790        '#collapsible' => TRUE,
 791        '#collapsed' => FALSE,
 792        '#value' => _sitedoc_database('database', $db_url, $optimize_tables, $show_index),
 793        );
 794      $output .=  theme('fieldset', $fieldset);
 795    }
 796    return $output;
 797  }
 798  
 799  // Format the database information.
 800  // This was split out when I was informed that the $db_url variable could be an array.
 801  function _sitedoc_database($actdb, $db_url, $optimize_tables=FALSE, $show_index=FALSE) {
 802    global $base_url, $db_prefix;
 803    $rows = array();
 804    $header = array();
 805  
 806    $db_types = array('mysql' => 'MySQL',
 807                      'mysqli' => 'MySQLi',
 808                      'postgres' => 'Postgres',
 809                  );
 810          
 811    $output .= '<table cellpadding="5"><tr>'."\n";        
 812  
 813    $output .= '<td valign="top">'."\n";
 814    $output .= '<h5>'. t('Overview') ."</h5>\n";
 815    $database_type = $db_types[$GLOBALS['db_type']];
 816    $database_version = db_version();
 817    $rows[] = array(t('Database type'), isset($database_type) ? $database_type : t('Unknown'));
 818    $vers = isset($database_version) ? $database_version : t('Unknown');
 819    $rows[] = array(t('Version'), l($vers, 'admin/reportsstatus/sql'));
 820  
 821    $db = parse_url($db_url);
 822  // Don't show password
 823    $db['pass'] = str_repeat('&bull;', strlen($db['pass']));
 824    $dbrows = array();
 825    foreach ($db as $key => $value) {
 826      $dbrows[] = array($key, $value);
 827    }
 828    $rows[] = array(t('Database URL'), theme('table', NULL, $dbrows));
 829  
 830    $rows[] = array(t('Base URL'), $base_url);
 831    $rows[] = array(t('Database prefix'), empty($db_prefix) ? '- none -' : $db_prefix);
 832    $grants = db_result(db_query('SHOW GRANTS'));
 833    $privileges = trim(str_replace('GRANT', ' ', substr($grants, 0, strpos($grants, ' ON'))));
 834    $rows[] = array(t('Privileges'), $privileges);
 835  
 836    $output .= theme('table', $header, $rows) ."\n";
 837    $output .= "</td>\n";
 838  
 839  // Check if we have a version of MySql that supports the variable and status checks
 840  // TODO: Can Postgres do something similar?
 841    if (('MySQL' == $database_type && version_compare($database_version, '4.1.0', '>=')) || 'MySQLi' == $database_type) {
 842    
 843      // The next two things are done as lists to 
 844      //   a) simplify building the query,
 845      //   b) create the possibility of future settings page selection.
 846  
 847      // Get selected variables from the database.
 848      $variables_list = array(
 849        'character_set_database',
 850        'character_set_results',
 851        'collation_connection',
 852        'collation_database',
 853        'collation_server',
 854        'have_dynamic_loading',
 855        'have_innodb',
 856        'have_isam',
 857        'have_query_cache',
 858        'have_raid',
 859        'max_connections',
 860        'query_cache_size',
 861        'query_cache_type',
 862        );
 863      $output .= _sitedoc_dbvars_list('VARIABLES', t('Selected Variables'), $variables_list, $database_version);
 864    
 865      // Get selected status information from the database.
 866      $variables_list = array(
 867        'Qcache_free_memory',
 868        'Qcache_hits',
 869        'Qcache_not_cached',
 870        'Qcache_queries_in_cache',
 871        'Max_used_connections',
 872        'Threads_running',
 873        'Threads_cached',
 874        'Threads_connected',
 875        'Threads_created',
 876        );
 877      $output .= _sitedoc_dbvars_list('STATUS', t('Selected Status'), $variables_list, $database_version);
 878  
 879      $output .= "</tr></table>\n";
 880    } /* end MySql check */
 881    
 882  //  $output .= '<h5>'. t('Table Status') ."</h5>\n";
 883  
 884    // Do the table status section.
 885    $result = db_query("SHOW TABLE STATUS");
 886  
 887   // I used a class to align fields rather than 'align' because the some themes override 'align' in headers.
 888    $statrpt = "\n<table><thead><tr>";
 889    $statrpt .= '<th>'. t('Table') .'</th>'
 890      .'<th>'. t('Engine') .'</th>'
 891      .'<th>'. t('Version') .'</th>'
 892      .'<th>'. t('Row Format') .'</th>'
 893      .'<th class="sitedoc_right">'. t('Rows') .'</th>'
 894      .'<th class="sitedoc_right">'. t('Data Length') .'</th>'
 895      .'<th class="sitedoc_right">'. t('Index Length') .'</th>'
 896      .'<th class="sitedoc_right">'. t('Overhead') .'</th>'
 897      .'<th class="sitedoc_right">'. t('Operation') .'</th>'
 898      .'</tr></thead><tbody>';
 899    $datalen = 0;
 900    $indexlen = 0;
 901    $overhead = 0;
 902    $row_count = 0;
 903    $rows = 0;
 904    $row_classes = array('even', 'odd');
 905    while ($table = db_fetch_array($result)) {
 906      ++$rows;
 907      $row_class = $row_classes[$rows & 1];
 908      $row_count += $table['Rows'];
 909      $datalen += $table['Data_length'];
 910      $indexlen += $table['Index_length'];
 911      $overhead += $table['Data_free'];
 912      $r = number_format($table['Rows']);       
 913      $d = number_format($table['Data_length']);
 914      $i = number_format($table['Index_length']);
 915      // Is there some overhead?
 916      if ($table['Data_free']) { 
 917        $o = number_format($table['Data_free']);
 918        // Do we want to release it?
 919        if ($optimize_tables) { 
 920          $worked = db_query('OPTIMIZE TABLE {%s}', $table['Name']);
 921          if ($worked) {
 922            $o .= '<br />'. t('released');
 923          }
 924        }
 925      }
 926      else { $o = NULL; }
 927      $statrpt .= "\n".'<tr class="'. $row_class .'">'
 928        .'<td>'. $table['Name'] .'</td>'
 929        .'<td>'. $table['Engine'] .'</td>'
 930        .'<td align="center">'. $table['Version'] .'</td>'
 931        .'<td>'. $table['Row_format'] .'</td>'
 932        .'<td class="sitedoc_right">'. $r .'</td>'
 933        .'<td class="sitedoc_right">'. $d .'</td>'
 934        .'<td class="sitedoc_right">'. $i .'</td>'
 935        .'<td class="sitedoc_right">'. ($table['Data_free'] ? $o : '') .'</td>'
 936        .'<td>'. l(t('Show contents'), 'sitedoc/table/'. $table['Name'], array('title' => t("Display the contents of the '@table' table.", array('@table' => $table['Name'])))) .'</td>'
 937        . (empty($table['Comment']) ? NULL : '</tr><tr class="'. $row_class .'"><td></td><td colspan="10"><img src="/misc/menu-collapsed.png" width="7" height="7"> <em>'. $table['Comment'] .'</em></td>');
 938      if ($show_index) {
 939        $statrpt .= '</tr><tr class="'. $row_class .'"><td></td><td valign="top" align="right"><em>'. t('Indexes') .':</em></td><td colspan="10">'. _sitedoc_show_index($table['Name']) .'</td>';
 940      }
 941      $statrpt .= '</tr>';
 942    } /* end while */
 943    $statrpt .= '<tr><td colspan="4">&nbsp;</td>'
 944               .'<td class="sitedoc_right">----------</td>' 
 945               .'<td class="sitedoc_right">----------</td>' 
 946               .'<td class="sitedoc_right">----------</td>' 
 947               .'<td class="sitedoc_right">----------</td>'
 948               .'</tr>';
 949    $statrpt .= '<tr><td colspan="4"><em>total</em></td>'
 950               .'<td class="sitedoc_right">'. number_format($row_count) .'</td>'
 951               .'<td class="sitedoc_right">'. number_format($datalen) .'</td>'
 952               .'<td class="sitedoc_right">'. number_format($indexlen) .'</td>'
 953               .'<td class="sitedoc_right">'. number_format($overhead) .'</td>'
 954               .'</tr>';
 955    $statrpt .=  "\n</table><p>";
 956  
 957    $fieldset = array(
 958      '#title' => t('Table Status'),
 959      '#collapsible' => TRUE,
 960      '#collapsed' => TRUE,
 961      '#value' => $statrpt,
 962      );
 963    $output .=  theme('fieldset', $fieldset);
 964  
 965    $totsize = $datalen + $indexlen;
 966    $kb = $totsize / 1024;
 967    $mb = $kb / 1024;
 968    $gb = $mb / 1024;
 969    $units = 'KB';
 970    $howmany = $kb;
 971    if ($mb >= 1) {
 972      if ($gb >= 1) {
 973        $units = 'GB'; $howmany = $gb;
 974      }
 975      else  {
 976        $units = 'MB'; $howmany = $mb;
 977      }
 978    }
 979   
 980    $output .= t('Total database size is !num !unit', array('!num' => sprintf('%.1f', $howmany), '!unit' => $units)) .'</p>';
 981    drupal_set_message(t('!count tables found in !db.', array('!count' => $rows, '!db' => $actdb)), 'status');
 982  
 983    return $output ."\n";
 984  }
 985  
 986  /*
 987   *  Helper function to show the index structure of a database table.
 988   */ 
 989  function _sitedoc_show_index($table_name) {
 990    $output = null;
 991    $index_list = db_query('SHOW INDEX FROM '. $table_name);
 992    $indices = array();
 993    $header = array(t('Key Name'), t('Columns'), t('Collation'), t('Cardinality'));
 994    while ($index = db_fetch_array($index_list)) {
 995      if ($index['Seq_in_index'] == 1) {        
 996        $indices[$index['Key_name']] = array(
 997          'name' => $index['Column_name'] . ($index['Sub_part'] ? ' ('. $index['Sub_part'] .')' : null),
 998          'collation' => $index['Collation'],
 999          'count' => $index['Cardinality'],
1000          );
1001      }
1002      else {
1003        $indices[$index['Key_name']]['name'] .= ', '. $index['Column_name'] . ($index['Sub_part'] ? ' ('. $index['Sub_part'] .')' : null);
1004      }
1005    }
1006    if (count($indices)) {
1007      $rows = array();
1008      foreach ($indices as $key_name => $values) {
1009        $rows[] = array($key_name,
1010          $values['name'],
1011          array('data' => $values['collation'], 'align' => 'center'),
1012          array('data' => $values['count'], 'align' => 'center')
1013          );
1014      }
1015      return theme('table', $header, $rows);
1016    }
1017    else {
1018      return '<span class="admin-missing">No index found.</span> '. _sitedoc_img_warning(); 
1019    }
1020  }
1021  
1022  /**
1023   *   Helper function for database variable list
1024   */
1025  function _sitedoc_dbvars_list($type, $title, $variables_list, $db_vers) {
1026    if (count($variables_list)) {
1027      $rows = array();
1028      $output .= '<td valign="top">'."\n";
1029      $output .= '<h5>'. $title ."</h5>\n";
1030    $show = 'SHOW '. $type;
1031    $vers = explode('.', $db_vers);
1032    switch ($vers[0]) {
1033      case 4:
1034        foreach ($variables_list as $key => $name) {
1035            $list = $show ." LIKE '". $name ."'";
1036            $result = db_query($list);
1037            while ($data = db_fetch_array($result)) {
1038              $rows[] = array($data['Variable_name'], $data['Value']);
1039            }
1040      } /* end foreach */
1041          $output .= theme('table', $header, $rows) ."\n";
1042          $output .= "</td>\n"; 
1043      break;
1044  
1045      case 5:
1046          $list = $show ." WHERE Variable_Name LIKE '". implode("' OR Variable_Name LIKE '", $variables_list) ."'";
1047          $result = db_query($list);
1048          while ($data = db_fetch_array($result)) {
1049            $rows[] = array($data['Variable_name'], $data['Value']);
1050          }
1051          $output .= theme('table', $header, $rows) ."\n";
1052          $output .= "</td>\n"; 
1053      break;
1054      
1055        default:
1056        drupal_set_message('Site Documentation: Unknown database version'. $vers[0], 'error');    
1057    
1058    } /* end switch */
1059    } /* end count variables */
1060    return $output;
1061  }
1062  
1063  /**
1064   *   Function to display table contents.
1065   *
1066   *   @param:
1067   *     $table - table name to show.
1068   *     $rows_per_page - how many rows to format per page. Defaults to 20.
1069   *
1070   *   @return: HTML string
1071   */
1072  function sitedoc_show_table($table = null, $rows_per_page = 20) {
1073    if (!$table || !db_table_exists($table)) {
1074      drupal_set_message(t('You must supply a valid database table name.'), 'error');
1075      drupal_access_denied();
1076    }
1077  
1078    // We get the first (or only) part of the Primary key to be added to the sort sequence.
1079    $result = db_query("SHOW INDEX FROM {$table}");
1080    $x = db_fetch_array($result);
1081    if ($x === false) {
1082      drupal_set_message(t("The '@table' table has no index defined. This is probably normal.", array('@table' => $table)), 'status');
1083      $first_key = null;
1084    }
1085    else {
1086      $first_key = $x['Column_name'];
1087    } 
1088  
1089    drupal_set_title(t('@table Table Contents', array('@table' => ucwords($table))));
1090    $output = '<p>'. t('Click on a column title to sort by that column.') .'</p><br/>';
1091    $rows = array();
1092  
1093    // Now we get the column names from the table and build the header.
1094    $header = array();
1095    $result = db_query("SHOW COLUMNS FROM {$table}");
1096  
1097    while ($col_desc = db_fetch_array($result)) {
1098      $header[] = array(
1099        'data' => ucwords(str_replace('_', ' ', $col_desc['Field'])),
1100        'field' => '`'. $col_desc['Field'] .'`',
1101        );
1102    }
1103    
1104    // Get the data rows from the table.
1105    $select = "SELECT * FROM {$table}";
1106    // Set it up so that the user can sort on any column, but the primary key will always be the last value to sort on.
1107    $select .= tablesort_sql($header) . ($first_key ? (', '. $first_key .' ASC') : null);
1108    // Do the query so that we can page the data.
1109    $result = pager_query($select, $rows_per_page);
1110  
1111    while ($row = db_fetch_array($result)) {
1112      $line = array();
1113      foreach ($row as $key => $value) {
1114        // We use check_plain for security.
1115        $line[] = check_plain($value);
1116      }
1117      $rows[] = $line;
1118    }
1119  
1120    // Build the displayable table.
1121    $output .= theme('table', $header, $rows);
1122    $output .= theme('pager', $rows_per_page);
1123    return $output;
1124  }
1125  
1126  /**
1127   *   Produce the modules list.
1128   *
1129   *  Parameters:
1130   *    none
1131   *
1132   *  Return: HTML string
1133   */
1134  function sitedoc_get_modules($exclude_disabled=FALSE, $sort_order=0) {
1135    $output = '';
1136    $rows = array();
1137  
1138    $module = array();
1139    $files = module_rebuild_cache();
1140    $sortkey = array();
1141    // Set some default package stuff for Drupal.
1142    $package_list = array('Core - required', 'Core - optional');
1143    $package_path = array('modules/ ', 'modules/ ');
1144  
1145    foreach ($files as $filename => $file) {
1146      $name = $file->info['name'];
1147      $level = NULL;
1148      
1149      $package = empty($file->info['package']) ? t('Other') : ucfirst($file->info['package']);
1150      $project = empty($file->info['project']) ? ucfirst($file->name) : ucfirst($file->info['project']);
1151      $key = array(NULL, NULL, $name);
1152  
1153      // The filename is something like sites/all/modules/package/contrib/project/project.module.
1154      // For the path sorting option, we want the part before "package" as key[0],
1155      // and the part after "package" ("contrib/") in key[1].
1156      // "project.module" is in 'basename' already.
1157      // So let's start by breaking the filename up into pieces.
1158      $pieces = explode('/', $file->filename);
1159      // We don't need the last array element (project.module).
1160      unset($pieces[count($pieces) - 1]);
1161  
1162      // Check for what kind of package it is.
1163      if ($package == t('Other') || $project == 'Drupal') {
1164        // 'Special' packages. Remove the last piece ("project/").
1165        unset($pieces[count($pieces) - 1]);
1166        // We add a blank to the end because 'sort' acts funny.
1167        $key[0] = implode('/', $pieces) .'/ ';
1168      }
1169      else {
1170        // 'Normal' packages.
1171        $where = array_search($package, $pieces);
1172        if ($where === FALSE) {
1173          // If "package" is not part of the name, just pull the "project" part off.
1174          unset($pieces[count($pieces) - 1]);
1175          $key[0] = implode('/', $pieces) .'/ ';
1176        }
1177        else {
1178          // "Package" is part of the name, split the keys up.
1179          $key[0] = implode('/', array_slice($pieces, 0, $where)) .'/ ';
1180          $key[1] = implode('/', array_slice($pieces, $where + 1)) .'/';
1181        }
1182      }
1183  
1184      $where = array_search($package, $package_list);
1185      if ($where === FALSE) {
1186        $package_list[] = $package;
1187        $package_path[] = $key[0];
1188      }
1189      else {
1190        // Package already in list.
1191        if ($package_path[$where] != $key[0] && $package != t('Other')) {
1192          // The path is different from what we found earlier.
1193          // One may be a substring of the other - that's okay, but save the shorter one.
1194          if ((strpos($package_path[$where], rtrim($key[0])) === FALSE && strpos($key[0], rtrim($package_path[$where])) === FALSE) || ($project == 'Drupal')) {
1195            if ($project == 'Drupal') {
1196              $level = 'error';
1197            }
1198            else {
1199              $level = 'status';
1200            }
1201            drupal_set_message(t("'!name' for the '!pkg' package was found in '!found' but a previous module was in '!prior'.", array('!name' => $name, '!pkg' => $package, '!found' => $key[0], '!prior' => $package_path[$where])), $level);
1202          }
1203          else {
1204            // Resave the shorter one.
1205            if (strlen($package_path[$where]) < strlen($key[0])) {
1206              $package_path[$where] = $key[0];
1207            }
1208          }
1209        }
1210      }
1211  
1212      $module[$name] = array(
1213        'version' => $file->info['version'],
1214        'basename' => $file->basename,
1215        'filename' => $file->filename,
1216        'path' => $key[0],
1217        'contrib_path' => $key[1],
1218        'package' => $package,
1219        'project' => $project,
1220        'dependencies' => $file->info['dependencies'],
1221        'dependents' => $file->info['dependents'],
1222        'description' => $file->info['description'],
1223        'status' => $file->status,
1224        'level' => $level,
1225        );
1226  
1227      // Build a parallel array to sort with.
1228      $separator = '|';
1229      switch ($sort_order) {
1230        case 0:  // package, project, module
1231          $key = array($module[$name]['package'], $module[$name]['project'], $name);
1232          break;
1233  
1234        case 1:  // path, module
1235          // Keys already set.
1236          break;
1237  
1238        default:  // This should never happen.
1239          drupal_set_message(t('Unrecognized sort order.'), 'error');
1240          $key = array($module[$name]['package'], $module[$name]['project'], $name);
1241      }
1242      $sortkey[] = implode('|', $key);
1243  
1244      $dependencies = array();
1245      // Check for missing dependencies.
1246      if (is_array($file->info['dependencies'])) {
1247        foreach ($file->info['dependencies'] as $dependency) {
1248          if (!isset($files[$dependency]) || !$files[$dependency]->status) {
1249            if (isset($files[$dependency])) {
1250              $dependencies[] = $files[$dependency]->info['name'] .'<span class="admin-disabled">('. t('disabled') .')</span>';
1251            }
1252            else {
1253              $dependencies[] = drupal_ucfirst($dependency) .'<span class="admin-missing">('. t('missing') .')</span>';
1254              $disabled[] = $filename;
1255            }
1256          } /* end if !isset */
1257          else {
1258            $dependencies[] = $files[$dependency]->info['name'] .'<span class="admin-enabled">('. t('enabled') .')</span>';
1259          }
1260        }  /* end foreach depend */
1261      } /* end if isarray */   
1262  
1263      // Un-array dependencies.
1264      if (!empty($dependencies)) {
1265        $module[$name]['dependencies'] = implode(', ', $dependencies);
1266      }
1267  
1268      // Un-array enabled dependents.
1269      if (!empty($module[$name]['dependents'])) {
1270        $module[$name]['required'] = implode(', ', $module[$name]['dependents']);
1271      }
1272    } /* end foreach files */
1273  
1274    $header = array(
1275      t('Name'),
1276      t('Version'),
1277      t('Status'),
1278      t('Project / Package'),
1279      t('Description'),
1280      t('Required By'),
1281      t('Depends On'),
1282      ); 
1283    drupal_set_message(t('!count modules found.', array('!count' => count($module))), 'status');
1284    $disabled_count = 0;
1285    $previous = NULL;
1286  
1287    // Order it by project, package, module name.
1288    sort($sortkey);
1289  
1290    foreach ($sortkey as $key => $value) {
1291      $keys = explode('|', $value);
1292      $name = $keys[count($keys) - 1];
1293  
1294      if ($keys[0] != $previous) {
1295        if (count($rows) > 0) {
1296          $fieldset = array(
1297            '#title' => $previous,
1298            '#collapsible' => TRUE,
1299            '#collapsed' => TRUE,
1300            '#value' => theme('table', $header, $rows),
1301            );
1302          $output .=  theme('fieldset', $fieldset);
1303        }
1304        $previous = $keys[0];
1305        $rows = array();      
1306      }
1307  
1308      if (!$exclude_disabled || $module[$name]['status']) {
1309        $path = $module[$name]['path'];
1310        if ($module[$name]['level']) {
1311        $path = '<span class="admin-missing">'. $path .'</span>';
1312        }
1313        $rows[] = array(
1314          $name,
1315          $module[$name]['version'],
1316          $module[$name]['status'] ? 'Enabled' : 'Disabled',
1317          '<small>'. $module[$name]['project'] .'<br />'. $module[$name]['package'] .'<br />'. $path .'</small>',
1318          '<small>'. $module[$name]['description'] .'</small>',
1319          $module[$name]['required'],
1320          $module[$name]['dependencies'],
1321          );
1322      } // end if exclude disabled
1323      else {
1324        ++$disabled_count;
1325      }
1326  
1327    } /* end foreach sortkey */
1328    // Flush the last set.
1329    if (count($rows) > 0) {
1330      $fieldset = array(
1331        '#title' => $previous,
1332        '#collapsible' => TRUE,
1333        '#collapsed' => TRUE,
1334        '#value' => theme('table', $header, $rows),
1335        );
1336      $output .=  theme('fieldset', $fieldset);
1337    }
1338  
1339    if ($exclude_disabled) {
1340      $output .= '<p>'. t('!count disabled modules excluded.', array('!count' => $disabled_count)) .'</p>';
1341    }
1342    return $output ."\n";
1343  }
1344  
1345  /**
1346   *   Produce the Themes lists
1347   *
1348   *  Parameters:
1349   *    Which type of row to return
1350   *      theme  - get themes list
1351   *
1352   *  Return: HTML string
1353   */
1354  function sitedoc_get_system($type=null) {
1355    if (!$type) {
1356      return '<p>'. t('No system table type specified.') .'</p>';
1357    }
1358    $status = array('Disabled', 'Enabled');
1359  
1360    $string = ucfirst(strtolower($type));
1361  
1362    // This ORDER BY makes the list come out by directory the entry comes from.
1363    $result = db_query('SELECT s.name, s.filename, s.status FROM {system} s WHERE s.type=\'%s\' ORDER BY s.status DESC, s.name ASC', $type);
1364    $header = array($string, t('Path'), t('Status'));
1365    $rows = array();
1366    while ($mod = db_fetch_array($result)) {
1367      $rows[] = array(
1368        ucfirst($mod['name']),
1369        $mod['filename'],
1370        $status[$mod['status']],
1371        );
1372    }
1373    if (!empty($rows)) {
1374      drupal_set_message(t('!rows !type found.', array('!rows' => count($rows), '!type' => $string)), 'status');
1375      $output .=  theme('table', $header, $rows);
1376    }
1377    else {
1378      $output .= t('No entries found! Very curious.');
1379    }
1380    return $output ."\n";
1381  }
1382  
1383  /**
1384   *   Produce the blocks list
1385   *
1386   *  Parameters:
1387   *    $warn - TRUE / FALSE to produce orphan warning message.
1388   *    $delete - TRUE / FALSE to delete orphans.
1389   *
1390   *  Return: HTML string
1391   */
1392  function sitedoc_get_blocks($warn=TRUE, $delete=FALSE) {
1393    $status = array();
1394    $status = array(t('disabled'), t('enabled'));
1395    $viz = array(t('Show except'), t('Show only'), t('Php'));
1396    $del = NULL;
1397    
1398    // Check if the table is missing an index.
1399    $indexes = db_fetch_array(db_query('SHOW INDEX FROM {blocks}'));
1400    if ($indexes == FALSE) {
1401      $index_msg = ' '. t('Performance may be improved by adding an index. See <a href="!url">this discussion</a>.', array('!url' => 'http://drupal.org/node/164532'));
1402      $msg_level = 'warning';
1403    }
1404    else {
1405      $index_msg = NULL;
1406      $msg_level = 'status';
1407    }
1408  
1409    // Get blocks from the Blocks table.
1410    $result = db_query('SELECT b.module, b.delta, b.theme, b.status, b.weight, b.region, b.custom, b.throttle, b.visibility, b.pages, b.title FROM {blocks} b ORDER BY b.theme, b.module, b.delta');
1411  
1412    $header = array(
1413      t('Theme'), 
1414      t('Module'), 
1415      array('data' => t('Delta'), 'align' => 'center'), 
1416      t('Name') .' /<br/>'. t('Title'),
1417      t('Status'), 
1418      array('data' => t('Weight'), 'class' => 'sitedoc_right'),
1419      t('Region'),
1420      t('Roles'),
1421      array('data' => t('Visibility'), 'align' => 'center'),
1422      t('Pages'),
1423      );
1424    $output = '<h3>'. t('Blocks') .'</h3>';
1425  
1426    $rows = array();
1427    $missing_theme = FALSE;
1428  
1429    while ($blk = db_fetch_object($result)) {
1430      $theme = $blk->theme;
1431      $thmq = db_query_range("SELECT s.status FROM {system} s WHERE s.type='theme' AND s.name='%s'", $theme, 0, 1);
1432      $thm = db_fetch_array($thmq);
1433      if (!empty($thm)) {
1434        $theme .= ' <span class="admin-'. $status[$thm['status']] .'">('. ucfirst($status[$thm['status']]) .')</span>';
1435      }
1436      else {
1437        $theme .= ' <span class="admin-missing">('. t('missing') .')</span>';
1438        $missing_theme = TRUE;
1439        $mod_delta = "module='$blk->module' AND delta='$blk->delta'";
1440        $delblk = "DELETE FROM {blocks} WHERE theme='". $blk->theme ."' AND ". $mod_delta;
1441        if ($has_roles = db_query_range('SELECT rid FROM {blocks_roles} WHERE '. $mod_delta, 0, 1)) {
1442          $delrole = 'DELETE FROM {blocks_roles} WHERE '. $mod_delta;
1443        }
1444        else {
1445          $delrole = null;
1446          drupal_set_message("has_roles query returned $has_roles");
1447        }
1448        if ($delete) { 
1449          $deleted = db_query($delblk) && ($delrole ? db_query($delrole) : true);
1450          if ($deleted == false) {
1451            $theme .= ' <span class="admin-missing">('. t('NOT DELETED') .')</span>';
1452          }
1453          else {
1454            $theme .= ' <span class="admin-disabled">('. t('DELETED') .')</span>';
1455            drupal_set_message(t('Deleted block ') . $blk->theme .'/'. $blk->module .'/'. $blk->delta, 'status');
1456          }
1457        }
1458        else {
1459          $del .= $delblk . $delrole;
1460        }
1461      }
1462       
1463      $blocks = module_invoke($blk->module, 'block', 'list');  // Get block information from module.
1464  
1465      $w = $blk->weight ? $blk->weight : '';    // Don't show 0 weight.
1466      $n = $blocks[$blk->delta]['info'];  // Get block name. 
1467      // If there is one, put title on next line.
1468      if (!empty($blk->title)) {
1469        $n .= ' /<br />'. $blk->title;
1470      } 
1471       // The pages field format depends on the visibility setting
1472       // 2 is php, 0 or 1 is pages list
1473      if ($blk->visibility < 2) { 
1474        $pt = trim(check_plain($blk->pages));
1475        if (strcmp($pt, '') == 0) {
1476          $pgs = NULL;
1477        }  
1478        else {
1479          $pgs = explode("\n", $pt); 
1480          if (is_array($pgs)) {
1481            if (count($pgs) > 1) {
1482              $pgs = theme('item_list', $pgs);
1483            }
1484            else {
1485              $pgs = $pgs[0];
1486            }
1487          }
1488        }
1489      }
1490      // Visibility = 2
1491      else {
1492        $pgs = highlight_string($blk->pages, TRUE);
1493      }
1494      
1495      // Get the roles for the blocks.
1496      $rids = db_query('SELECT br.rid, r.name FROM {blocks_roles} br INNER JOIN {role} r ON r.rid=br.rid WHERE br.module=\'%s\' AND br.delta=\'%d\'', $blk->module, $blk->delta);
1497      $blk_rids = array();
1498      while ($blk_role = db_fetch_array($rids)) {
1499        $blk_rids[] = $blk_role['name'];
1500      }
1501      
1502      $rows[] = array(
1503        $theme,
1504        $blk->module == 'block' ? 'block /<br/><small><em>'. t('see boxes') .'</em></small>' : $blk->module,
1505        array('data' => $blk->delta, 'align' => 'center'),
1506        $n,
1507        '<span class="admin-'. $status[$blk->status] .'">'. ucfirst($status[$blk->status]) .'</span>',
1508        array('data' => $w, 'class' => 'sitedoc_right'),
1509        $blk->status ? $blk->region : '',    /* no region if not enabled */
1510        count($blk_rids) ? '<small>'. implode(', ', $blk_rids) .'</small>' : NULL,
1511        array('data' => $viz[$blk->visibility], 'align' => 'center'),
1512        $pgs,
1513        );
1514    } /* end while */
1515  
1516    $count = count($rows);
1517    if ($count == 0) {
1518      drupal_set_message(t('No Blocks found.'), 'status'); 
1519    }
1520    else {
1521      drupal_set_message(t('!count Blocks found.', array('!count' => $count)) . $index_msg , $msg_level);
1522      if ($index_msg) {
1523        drupal_set_message(t('For example:') .' ALTER TABLE `blocks` ADD PRIMARY KEY(`theme`(32), `module`, `delta`)', $msg_level);
1524      }
1525      $output .=  theme('table', $header, $rows);
1526    }
1527    if ($missing_theme && $warn) {
1528      drupal_set_message(t('Blocks defined for non-existent theme(s)'), 'error');
1529    }
1530  
1531    if ($missing_theme && !$delete) {
1532      $output .= '<p>'. $del .'</p>';
1533    }
1534  
1535    return $output ."\n";
1536  }
1537  
1538  /**
1539   *   Produce the boxes (manual blocks, additional data) list.
1540   *
1541   *  Parameters:
1542   *    $warn - TRUE / FALSE to produce orphan warning message.
1543   *    $delete - TRUE / FALSE to delete orphans.
1544   *
1545   *  Return: HTML string
1546   */
1547  function sitedoc_get_boxes($warn=TRUE, $delete=FALSE) {
1548    $result = db_query('SELECT * FROM {boxes} b');
1549    $output = NULL;
1550    $orphans = FALSE;
1551    $header = array(
1552      array('data' => 'Bid', 'class' => 'sitedoc_right'), 
1553      t('Box Name'),
1554      array('data' => t('Format'), 'class' => 'sitedoc_right'),
1555      t('Body'),
1556      );
1557    $rows = array();
1558    while ($box = db_fetch_array($result)) {
1559      $info = $box['info'];
1560      $check_block = db_result(db_query_range("SELECT COUNT(theme) FROM {blocks} WHERE module='block' AND delta=%d", $box['bid'], 0, 1));
1561      if ($check_block == 0) {
1562        $orphans = TRUE;
1563        $info .= '<br/><span class="admin-missing">('. t('block missing') .')</span>';
1564        if ($delete) {
1565          $delbox = db_query_range('DELETE FROM {boxes} WHERE bid=%d', $box['bid'], 0, 1);
1566          drupal_set_message(t('Box number !bid deleted.', array('!bid' => $box['bid'])), 'status');
1567        }
1568      }
1569  
1570      $rows[] = array(
1571        array('data' => $box['bid'], 'class' => 'sitedoc_right'), 
1572        $info,
1573        array('data' => $box['format'], 'class' => 'sitedoc_right'),
1574        highlight_string($box['body'], TRUE),
1575        );
1576    } // End while.
1577  
1578    $howmany = count($rows);
1579    if ($howmany > 0) {
1580      $output .= theme('table', $header, $rows) ."\n";
1581      drupal_set_message(t('!count boxes found.', array('!count' => $howmany)), 'status');
1582    }
1583    if ($orphans) {
1584      if ($warn) {
1585        drupal_set_message('Orphan boxes found.', 'error');
1586      }
1587    }
1588    return $output;
1589  }
1590  
1591  /**
1592   *   Produce the content types list.
1593   *
1594   *  Parameters:
1595   *    none
1596   *
1597   *  Return: HTML string
1598   */
1599  function sitedoc_content_types() {  
1600    $yesno = array(t('no'), t('yes'));
1601    $com_vals = array(t('disabled'), t('read-only'), t('read/write'));
1602  
1603    // The TRUE here forces the node module to refresh the list.
1604    $node_types = node_get_types('types', NULL, TRUE); 
1605    $header = array(
1606      t('Type'), 
1607      array('data' => t('Count'), 'class' => 'sitedoc_right'),
1608      t('Name') .'/<br/>'. t('Module'),
1609      t('Description'), 
1610      t('Has title') .'/<br/>'. t('Has body') .'/<br/>'. t('Has help'),
1611      array('data' => t('Min words'), 'class' => 'sitedoc_right'),
1612      t('Custom') .'/<br/>'. t('Modified') .'/<br/>'. t('Locked'),
1613      t('Workflow') .'/<br/>'. t('Comments')
1614      );
1615    $num_contents = 0;
1616    $missing = FALSE;
1617  
1618    foreach ($node_types as $type => $info) {
1619      $num_contents ++;    
1620      $workflow = str_replace('status', 'published', implode(', ', variable_get('node_options_'. $info->type, array())));
1621      if (workflow == '') {
1622        $workflow = 'not published';
1623      }
1624      $count = db_result(db_query('SELECT COUNT(n.nid) FROM {node} n WHERE n.type=\'%s\'', $info->type));
1625      if (module_exists($info->module)) {
1626        $mod = $info->module;
1627      }
1628      else {
1629        $mod = $info->module .' <span class="admin-missing">('. t('missing') .')</span>';
1630        $missing = TRUE;
1631      }
1632      $has_help = $info->help ? true : false;
1633      $rows[] = array(
1634        l($info->type, 'admin/content/types/'. $info->type),
1635        array('data' => $count, 'class' => 'sitedoc_right'),
1636        $info->name .'<br/>'. $mod,
1637        $info->description,
1638        $yesno[$info->has_title] .'<br/>'. $yesno[$info->has_body] .'<br/>'. $yesno[$has_help], 
1639        array('data' => $info->min_word_count, 'class' => 'sitedoc_right'),
1640        $yesno[$info->custom] .'<br/>'. $yesno[$info->modified] .'<br/>'. $yesno[$info->locked],
1641        $workflow .'<br/>'. $com_vals[variable_get('comment_'. $info->type, 0)],
1642        ); 
1643    }  /* end foreach */
1644  
1645    if ($missing) {
1646      drupal_set_message(t('!num content types found. Missing modules identified.', array('!num' => $num_contents)), 'error');
1647    }
1648    else {
1649      drupal_set_message(t('!num content types found.', array('!num' => $num_contents)), 'status');
1650    }
1651  
1652    $output .= theme('table', $header, $rows);
1653  
1654    return $output ."\n";
1655  }
1656  
1657  /**
1658   *   Produce the vocabularies and terms list.
1659   *
1660   *  Parameters:
1661   *    none
1662   *
1663   *  Return: HTML string
1664   */
1665  function sitedoc_get_vocabularies() {
1666    if (!module_exists('taxonomy')) {
1667      return '<p>Taxonomy'. t(' module not enabled') .'</p>';
1668    }
1669    $hier = array(t('Disabled'), t('Single'), t('Multiple'));
1670    $vocabularies = taxonomy_get_vocabularies();
1671    $rows = array();
1672    $header = array(
1673      t('Name (vid)'),
1674      t('Content Types'),
1675      t('Description'),
1676      t('Help'),
1677      t('Hierarchy'),
1678      t('Terms'),
1679      t('Module'),
1680      t('Weight'),
1681      );
1682    foreach ($vocabularies as $vocabulary) {
1683      $types = array();
1684      foreach ($vocabulary->nodes as $type) {
1685        $types[] = $type;
1686      }
1687      $t = array();
1688      if ($vocabulary->relations) {
1689        $t[] = t('related');
1690      } 
1691      if ($vocabulary->multiple) {
1692        $t[] = t('multi-select');
1693      } 
1694      if ($vocabulary->required) {
1695        $t[] = t('required');
1696      } 
1697      if ($vocabulary->tags) {
1698        $t[] = t('free-tag');
1699      } 
1700      $rows[] = array(
1701        'name' => check_plain($vocabulary->name) .' ('. $vocabulary->vid .')',
1702        'type' => implode(', ', $types),
1703        $vocabulary->description . _terms_list($vocabulary->vid),  /* subroutine gets terms */
1704        $vocabulary->help ? t('yes') : t('no'),
1705        $hier[$vocabulary->hierarchy],
1706        implode(', ', $t),
1707        $vocabulary->module,
1708        $vocabulary->weight,
1709        );
1710    }
1711     
1712    $output .= theme('table', $header, $rows);
1713    return $output ."\n";
1714  }
1715  
1716  // Helper function to get vocabulary term info.
1717  function _terms_list($vid) {
1718    $items = array();
1719    $thead = array(
1720      t('Term (tid)'), 
1721      array('data' => t('Count'), 'class' => 'sitedoc_right'),
1722      t('Description'),
1723      );
1724    $terms = taxonomy_get_tree($vid);
1725  
1726    foreach ( $terms as $term ) {
1727      // This query should be very fast and includes unpublished nodes.
1728      $count = db_result(db_query('SELECT COUNT(nid) FROM {term_node} WHERE tid=%d', $term->tid));
1729  
1730      // Don't show terms with 0 count.
1731      if ($count) {   
1732        $items[] = array(
1733          $term->name .' ('. $term->tid .')',
1734          array('data' => $count, 'class' => 'sitedoc_right'),
1735          $term->description);
1736      }
1737    }
1738  
1739    return "\n". theme('table', $thead, $items);
1740  }
1741  
1742  /**
1743   *  Produce a table of taxonomy term usage by content type.
1744   *  This function is available only as a callable function;
1745   *   it is not called within this module.
1746   *
1747   *  Parameters:
1748   *    vid - the vocabulary id to show.
1749   *
1750   *  Return: HTML string
1751   */
1752  function sitedoc_term_count_by_type($vid) {
1753    $vocab = taxonomy_vocabulary_load($vid);
1754    if (!$vocab->name) {
1755      drupal_set_message(t('Vocabulary not found.'), 'error');
1756      return t('Vocabulary not found.');
1757    }
1758  
1759    $output = '<h2>Usage of "'. $vocab->name .'" Category by Term and Content Type</h2>';
1760    if ($vocab->description) {
1761      $output .= '<h6>'. $vocab->description .'</h6>';
1762    }
1763  
1764    $header = array(t('Term'));
1765    $rows = array();
1766    $type = array();
1767  
1768    // Build header row with types.
1769    foreach ($vocab->nodes as $key => $value) {
1770      $type[] = $value;
1771      $name = $value;
1772      $header[] =  array('data' => $name, 'align' => 'right') ;
1773    } /* end foreach */
1774  
1775    $terms = taxonomy_get_tree($vid);
1776    foreach ( $terms as $term ) {
1777      $line = array();
1778    if (module_exists('taxonomy_image')) {
1779      $img = taxonomy_image_display($term->tid, array('align' => 'left', 'hspace' => '3'));
1780    }
1781    else { $img = NULL; }
1782      $line[] = $img . l($term->name, 'taxonomy/term/'. $term->tid) ."<br/><small>". $term->description ."</small>";
1783      foreach ($type as $content_type) {
1784        $count = sitedoc_term_count_nodes($term->tid, $content_type);
1785        if ($count) { /* don't show terms with 0 count */
1786          $line[] = array('data' => $count, 'align' => 'right');
1787        } /* end if count */
1788        else {
1789          $line[] = ' ';
1790        }
1791      } /* end each content_type */
1792      $rows[] = $line;
1793    } /* end foreach term */
1794  
1795    $output .= theme('table', $header, $rows);
1796    return $output;
1797  }
1798  
1799  /**
1800   *  Check if there are any term nodes entries that don't belong to nodes.
1801   *
1802   *  Parameters:
1803   *    Delete - TRUE | FALSE - whether or not to delete the orphans.
1804   *
1805   *  Return: HTML string
1806   */
1807  function sitedoc_check_orphan_term_node($delete_orphans=FALSE) {
1808    if (!module_exists('taxonomy')) {
1809      return '<p>Taxonomy'. t(' module not enabled') .'</p>';
1810    }
1811    
1812    $tn = db_result(db_query('SELECT COUNT(DISTINCT(tn.nid)) FROM {term_node} tn'));
1813    $n =  db_result(db_query('SELECT COUNT(DISTINCT(n.nid)) FROM {node} n'));
1814    
1815    if ($tn > $n) {
1816      drupal_set_message(t('There are !tn term_nodes but only !n nodes.', array('!tn' => $tn, '!n' => $n)), 'error');
1817    }
1818    else {
1819      return '<p>'. t('No orphan term_nodes found.') .'</p>';
1820    }
1821  
1822    $header = array('nid', 'tid', 'term', 'vocab');
1823    $rows = array();
1824    $del = NULL;
1825  
1826    $result = db_query('SELECT tn.nid, tn.tid FROM {term_node} tn LEFT JOIN {node} n ON tn.nid=n.nid WHERE ISNULL(n.title) ORDER BY tn.nid, tn.tid');
1827  
1828    while ($tn = db_fetch_array($result)) {
1829      $term = taxonomy_get_term($tn['tid']);
1830      $vocab = taxonomy_vocabulary_load($tn['tid']);
1831      $delsql .= 'DELETE FROM {term_node} WHERE term_node.nid='. $tn['nid'] .' AND term_node.tid='. $tn['tid'];
1832      if ($delete_orphans) {
1833        db_query($delsql);
1834        drupal_set_message(t('Deleted term_node ') .'nid='. $tn['nid'] .', tid='. $tn['tid'], 'status');
1835      }
1836      else {
1837        $del .= $delsql;
1838      }
1839      $rows[] = array($tn['nid'], $tn['tid'], $term->name, $vocab->name, $del);
1840    }
1841     
1842    $output = theme('table', $header, $rows);
1843    if (!$delete_orphans) {
1844      $output .= $del;
1845    }
1846    
1847    return $output ."\n";
1848  }
1849  
1850  /**
1851   *   Produce the node summary.
1852   *
1853   *  Parameters:
1854   *    TRUE - include comments count.
1855   *    FALSE - don't include comments count.
1856   *
1857   *  Return: HTML string
1858   */
1859  function sitedoc_node_summary($comment=FALSE, $show_size=99999, $max_size=99999) {
1860    $sql = "SELECT n.nid, n.title, n.type, n.status, n.promote, n.moderate, n.sticky, length(body) AS node_len";
1861    if ($comment) {
1862      $sql .= ", c.comment_count";
1863    } 
1864    $sql .= " FROM {node} n LEFT JOIN {node_revisions} nr ON nr.vid=n.vid";
1865    if ($comment) {
1866      $sql .= " LEFT JOIN {node_comment_statistics} c ON c.nid=n.nid";
1867    } 
1868    $result = db_query($sql);
1869    $counters = array();
1870    $types = array();
1871    $weight_detected = FALSE;
1872    
1873    $toobig = 0;
1874    $biggies = array();
1875  
1876    while ($node = db_fetch_object($result)) {
1877      $node_kb = $node->node_len / 1024;
1878      if ($node_kb >= $show_size) {
1879        $biggies[] = array(
1880          l($node->title, 'node/'. $node->nid .'/edit'),
1881          array('data' => number_format($node_kb, 2), 'align' => 'center'));
1882      } /* end if show size */
1883      if ($node_kb >= $max_size) {
1884        ++$toobig;
1885      }
1886  
1887      if (!in_array($node->type, $types)) {
1888        $types[] = $node->type;
1889      }
1890      $counters[$node->type]['type'] ++;
1891      if ($node->status) {
1892        ++$counters[$node->type]['published'];
1893      }
1894      if ($node->promote) {
1895        ++$counters[$node->type]['promoted'];
1896      }
1897      if ($node->moderate) {
1898        ++$counters[$node->type]['moderated'];
1899      }
1900      if ($comment) {
1901        $counters[$node->type]['comments'] += $node->comment_count;
1902      }
1903       /* check if the weight module algorithm is in use */
1904      if ($node->sticky > 1 || $node->sticky < 0) { 
1905        _decode_sticky($node);
1906        if (!$weight_detected) {
1907          $weight_detected = TRUE;
1908          drupal_set_message(t('Weight-encoded in sticky field has been detected.'), 'status');
1909        }
1910      }
1911      else {
1912        $node->weight = 0;  /* set unweighted if not */
1913      }
1914      if ($node->sticky) {
1915        ++$counters[$node->type]['sticky'];
1916      }
1917      if ($node->weight <> 0) {
1918        ++$counters[$node->type]['weight'];
1919      }
1920    } /* end while fetch */
1921    ksort($types);
1922    $header = array(
1923      t('Type'),
1924      array('data' => t('Count'), 'class' => 'sitedoc_right'),
1925      array('data' => t('Published'), 'class' => 'sitedoc_right'),
1926      array('data' => t('Promoted'), 'class' => 'sitedoc_right'),
1927      array('data' => t('Sticky'), 'class' => 'sitedoc_right'), 
1928      array('data' => t('Weighted'), 'class' => 'sitedoc_right'),
1929      array('data' => t('Moderated'), 'class' => 'sitedoc_right'),
1930      $comment ? array('data' => t('Comments'), 'class' => 'sitedoc_right') : NULL,
1931      );
1932    $rows = array();
1933    $tot = 0; $pub = 0; $pro = 0; $sti = 0; $mod = 0;
1934    foreach ($types as $type) {
1935      $rows[] = array(
1936        $type,
1937        array('data' => $counters[$type]['type'], 'class' => 'sitedoc_right'),
1938        array('data' => $counters[$type]['published'], 'class' => 'sitedoc_right'),
1939        array('data' => $counters[$type]['promoted'], 'class' => 'sitedoc_right'),
1940        array('data' => $counters[$type]['sticky'], 'class' => 'sitedoc_right'),
1941        array('data' => $counters[$type]['weight'], 'class' => 'sitedoc_right'),
1942        array('data' => $counters[$type]['moderated'], 'class' => 'sitedoc_right'),
1943        $comment ? array('data' => $counters[$type]['comments'], 'class' => 'sitedoc_right') : NULL,
1944        );
1945      $tot += $counters[$type]['type'];
1946      $pub += $counters[$type]['published'];
1947      $pro += $counters[$type]['promoted'];
1948      $sti += $counters[$type]['sticky'];
1949      $wei += $counters[$type]['weight'];
1950      $mod += $counters[$type]['moderated'];
1951      $com += $counters[$type]['comments'];
1952    } /* end foreach type */
1953    $rows[] = array(
1954      '',
1955      array('data' => '------', 'class' => 'sitedoc_right'),
1956      array('data' => '------', 'class' => 'sitedoc_right'),
1957      array('data' => '------', 'class' => 'sitedoc_right'),
1958      array('data' => '------', 'class' => 'sitedoc_right'),
1959      array('data' => '------', 'class' => 'sitedoc_right'),
1960      array('data' => '------', 'class' => 'sitedoc_right'),
1961      $comment ? array('data' => '------', 'class' => 'sitedoc_right') : NULL,
1962      );
1963    $rows[] = array(
1964      '<em>'. t('total') .'</em>',
1965      array('data' => $tot, 'class' => 'sitedoc_right'),
1966      array('data' => $pub, 'class' => 'sitedoc_right'),
1967      array('data' => $pro, 'class' => 'sitedoc_right'),
1968      array('data' => $sti, 'class' => 'sitedoc_right'),
1969      array('data' => $wei, 'class' => 'sitedoc_right'),
1970      array('data' => $mod, 'class' => 'sitedoc_right'),
1971      $comment ? array('data' => $com, 'class' => 'sitedoc_right') : NULL,
1972      );
1973    drupal_set_message(t('!count nodes found.', array('!count' => $tot)), 'status');
1974    $output .= theme('table', $header, $rows);
1975  
1976    if ($toobig) {
1977      $output .= '<p>'. _sitedoc_img_warning() .' '.
1978        t('!count nodes exceed !size KB.', array('!count' => $toobig, '!size' => $max_size)) .'</p>';
1979    }
1980    else {
1981      $output .= '<p>'. _sitedoc_img_ok() .' '
1982        . t('No nodes exceed !size KB.', array('!size' => $max_size)) .'</p>';
1983    }
1984    if (!empty($biggies)) {  
1985      $big_head = array(t('Title'), t('Length (KB)'));
1986      $fieldset = array(
1987        '#title' => t('Large Nodes'),
1988        '#collapsible' => TRUE,
1989        '#collapsed' => TRUE,
1990        '#value' => theme('table', $big_head, $biggies),
1991        );
1992      $output .=  theme('fieldset', $fieldset);
1993    }
1994  
1995    return $output ."\n";
1996  }
1997  
1998  // Helper module in case weight module algorithm is used.
1999  function _decode_sticky(&$node) {
2000    $sticky = $node->sticky;     /* save value */
2001    $node->sticky = ($sticky > 0) ? 1 : 0;
2002    $node->weight = ($sticky > 0) ? 100 - $sticky : -100 - $sticky;
2003    return;
2004  }
2005  
2006  /**
2007   *   Produce the node access summary.
2008   *
2009   *  Parameters:
2010   *    None
2011   *
2012   *  Return: HTML string.
2013   */
2014  function sitedoc_node_access() {
2015    // How many nodes are not represented in the node_access table.
2016    $result = db_fetch_object(db_query('SELECT COUNT(n.nid) as num_nodes FROM {node} n LEFT JOIN {node_access} na ON n.nid = na.nid WHERE na.nid IS NULL'));
2017    if ($num = $result->num_nodes) {
2018      $output .= '<p>'. _sitedoc_img_warning() .' '
2019        . t('You have !num nodes in your node table which are not represented in your node_access table.  If you have an access control module installed, these nodes may be hidden from all users.  This could be caused by publishing nodes before enabling the access control module.  If this is the case, manually updating each node should add it to the node_access table and fix the problem. Click here to !rebuild.', array('!num' => l($num, 'admin/build/sitedoc_node_access_view/'), '!rebuild' => l(t('rebuild permissions'), 'admin/content/node-settings/rebuild'))) ."</p>\n";
2020    }
2021    else {
2022      $output .= '<p>'. _sitedoc_img_ok() . t('All nodes are represented in the node_access table.') ."</p>\n";
2023    } /* end else */
2024  
2025    // Warn user if they have any entries that could grant access to all nodes.
2026    $rows = array();
2027    $result = db_query('SELECT DISTINCT na.realm FROM {node_access} na WHERE na.nid=0 AND na.gid=0');
2028    while ($row = db_fetch_object($result)) {
2029      $rows[] = $row->realm;
2030    }
2031    if (!empty($rows)) {
2032      $output .= '<h5>'. t('Access Granted to All Nodes (All Users)') ."</h5>\n";
2033      $output .= '<p>'. t('Your node_access table contains entries (!r) that may be granting all users access to all nodes.  Depending on which access control module(s) you use, you may want to delete these entries.  If you are not using an access control module, you should probably leave these entries as is.', array('!r' => implode(',  ', $rows))) ."</p>\n";
2034    }
2035  
2036    $headers = array(t('realm'));
2037    $rows = array();
2038   // A similar warning to the one above, but slightly more specific.
2039    $result = db_query('SELECT DISTINCT(na.realm) FROM {node_access} na WHERE na.nid=0 AND na.gid<>0');
2040    while ($row = db_fetch_object($result)) {
2041      $rows[] = array($row->realm);
2042    } /* end while */
2043    if (!empty($rows)) {
2044      $output .= '<h5>'. t('Access Granted to All Nodes (Some Users)') ."</h5>\n";
2045      $output .= '<p>'. t('Your node_access table contains entries that may be granting some users access to all nodes.  This may be normal, depending on which access control module(s) you use.') ."</p>\n";
2046      $output .= theme('table', $header, $rows);
2047    } /* end all nodes */
2048  
2049    $header = array(t('Realm'), t('Nodes'), t('Notes'));
2050    $rows = array();
2051  
2052    // Count nodes by realm.
2053    $result = db_query('SELECT DISTINCT na.realm, COUNT(DISTINCT na.nid) as node_count FROM {node_access} na GROUP BY na.realm');
2054    $all = FALSE;
2055  
2056    while ($row = db_fetch_object($result)) {
2057      if ($row->realm == 'all') {
2058        $all = TRUE;
2059        $realm_type = t('Public nodes');
2060        $caption = t('This realm grants all users access to some specific nodes. If some of your content is available to the public, this may be normal.');
2061      }
2062      else {
2063        $realm_type = t('Private nodes');
2064        $caption = t('This realm grants limited access to some specific nodes.');
2065      }
2066      $rows[] = array($row->realm, l($row->node_count, 'admin/build/sitedoc_node_access_view/'. $row->realm) .' '. $realm_type, $caption);
2067    } /* end while */
2068    if ($all && !empty($rows)) {
2069      $output .= theme('table', $header, $rows);
2070    }
2071  
2072    return $output ."\n";
2073  }
2074  
2075  /** 
2076   *  Menu call back to produce a list of nodes within a given access realm.
2077   *
2078   *   parameters:
2079   *     realm name
2080   *
2081   *   returns:
2082   *     HTML string
2083   */
2084  function sitedoc_node_access_view($realm=NULL) {
2085    $rows = array();
2086    $header = array(
2087      t('Type'),
2088      t('Title') .' &nbsp;&nbsp;&nbsp;<small> '. t('click to edit') .'</small>',
2089      );
2090  
2091    $sql = 'SELECT DISTINCT(n.nid), n.type, n.title FROM {node} n LEFT JOIN {node_access} na ON n.nid = na.nid WHERE na.realm ';
2092    $count = 'SELECT COUNT(DISTINCT(n.nid)) FROM {node} n LEFT JOIN {node_access} na ON n.nid = na.nid WHERE na.realm ';
2093    if (is_null($realm)) {
2094      $output .= '<h3>'. t('Unprotected Nodes') .'</h3>';
2095      $sql .= 'IS NULL';
2096      $count .= 'IS NULL';
2097    }
2098    else { 
2099      $output .= '<h3>'. t('Nodes in Realm: ') . ucfirst($realm) .'</h3>';
2100      $sql .= "= '". $realm ."'";
2101      $count .= "= '". $realm ."'";
2102    }
2103  
2104    $result = pager_query($sql, 50, 0, $count);
2105    while ($node = db_fetch_object($result)) {
2106      $rows[] = array($node->type, l($node->title, 'node/'. $node->nid .'/edit'));
2107    } /* end while node */
2108    $output .= theme('table', $header, $rows);
2109    $output .= theme('pager', array(), 50);
2110  
2111    return $output ."\n";
2112  }
2113  
2114  /**
2115   *  Produce the user roles list.
2116   *
2117   *  Parameters:
2118   *    1) TRUE - permissions listed as unordered list
2119   *       FALSE - permissions listed as stream (default)
2120   *    2) TRUE - include users in roles (default)
2121   *       FALSE - do not include users 
2122   *
2123   *  Return: HTML string
2124   */
2125  function sitedoc_get_roles($list = FALSE, $users = FALSE) {
2126    $sql = 'SELECT r.rid, r.name, p.perm, p.tid FROM {role} r INNER JOIN {permission} p ON p.rid = r.rid';
2127    $result = db_query($sql);
2128    $output = NULL;
2129    $header = array(
2130      array('data' => 'Rid', 'class' => 'sitedoc_right'),
2131      t('Name'),
2132      t('Permissions'),
2133      t('Blocks'),
2134      $users ? t('Users') : NULL,
2135      array('data' => 'Tid', 'class' => 'sitedoc_right'),
2136      );
2137  
2138    while ($role = db_fetch_object($result)) {
2139      // Get which blocks they can see.
2140      $bresult = db_query('SELECT b.module, b.delta FROM {blocks_roles} b WHERE b.rid = %d', $role->rid);
2141      $blk_list = array();
2142      while ($block = db_fetch_object($bresult)) {
2143        $blk_list[] = $block->module .'/'. $block->delta;
2144      } /* end while block */
2145  
2146      // Get which users have this role.
2147      $uresult = db_query('SELECT u.name, u.uid FROM {users_roles} r INNER JOIN {users} u ON u.uid = r.uid WHERE r.rid = %d', $role->rid);
2148      $user_list = array();
2149      while ($user = db_fetch_object($uresult)) {
2150        // Make the user a link.
2151        $user_list[] = l($user->name .'('. $user->uid .')', 'user/'. $user->uid);
2152      } 
2153  
2154      $rows[] = array(
2155        array('data' => $role->rid, 'class' => 'sitedoc_right'),
2156        $role->name,
2157        $list ? theme('item_list', explode(', ', $role->perm)) : $role->perm,
2158        $list ? theme('item_list', $blk_list) : implode(', ', $blk_list),
2159        $users ? ($list ? theme('item_list', $user_list) : implode(', ', $user_list)) : NULL,
2160        array('data' => $role->tid, 'class' => 'sitedoc_right'),
2161        );
2162    } /* end while role */
2163    
2164    $howmany = count($rows);
2165    if ($howmany > 0) {
2166      drupal_set_message(t('!count Roles found.', array('!count' => $howmany)), 'status');
2167      $output = theme('table', $header, $rows);
2168    }
2169    return $output ."\n";
2170  }
2171  
2172  /**
2173   *  Produce the system variables list.
2174   *
2175   *  Parameters:
2176   *    None
2177   *
2178   *  Return: HTML string
2179   */
2180  function sitedoc_get_variables() {
2181    // $conf is the cached array of system variables 
2182    global $conf;   
2183  
2184    // Save it so we can sort it
2185    $vars = $conf;  
2186    ksort($vars);
2187  
2188    drupal_set_message(count($vars) . t(' system variables found.'), 'status');
2189  
2190    $header = array(t('Name'), t('Value'));
2191    $rows = array();
2192  
2193    foreach ($vars as $name => $value) {
2194      $rows[] = array($name, _sitedoc_handle_data($value));
2195    } /* end foreach */
2196  
2197    $output .= theme('table', $header, $rows);
2198    return $output ."\n";
2199  }
2200  
2201  // Helper function for traversing an array.
2202  // Since some arrays may contain arrays, this function can be called recursively.
2203  // Also handles objects.
2204  function _sitedoc_handle_data($text) {
2205    // Take care of simple data types first.
2206    if (is_string($text) || is_numeric($text) || is_float($text)) {
2207  //    return filter_xss($text);
2208      return check_plain($text);
2209    }
2210  
2211    if (is_null($text)) {
2212      return t('NULL');
2213    }
2214  
2215    if (is_bool($text)) {
2216      return $text ? t('True') : t('False');
2217    }
2218  
2219    // That leaves arrays and objects.
2220    if (is_array($text)) {
2221      $rows = array();
2222      foreach ($text as $key => $value) {
2223        $rows[] = array($key, _sitedoc_handle_data($value));
2224      }
2225      return theme('table', array(t('Key'), t('Value')), $rows);
2226    }
2227  
2228    if (is_object($text)) {
2229      $rows = array();
2230      // turn it into an array and handle like one.
2231      $data = (array) $text;
2232      foreach ($data as $key => $value) {
2233        $rows[] = array($key, _sitedoc_handle_data($value));
2234      }
2235      return theme('table', array(t('Method'), t('Value')), $rows);
2236    }
2237  
2238  //  return gettype($text);
2239  
2240    // Hmm, what could it be?
2241    $value = '<strong>'. gettype($array[$name]) .'</strong>';
2242    drupal_set_message('Tell Nancy I found a '. gettype($array[$name]) .' in the variables.', 'error');
2243    return t('Unknown data type');
2244  }
2245  
2246  /**
2247   * Produce the site-wide contacts (email) list.
2248   *
2249   *  Parameters:
2250   *    None
2251   *
2252   *  Return: HTML string
2253   */
2254  function sitedoc_get_contacts() {
2255    $output = NULL;
2256    if (!module_exists('contact')) {
2257      return '<p>Contact'. t(' module not enabled') .'</p>';
2258    }
2259    $sql = 'SELECT * FROM {contact} c ORDER BY c.selected DESC, c.cid ASC';
2260    $result = db_query($sql);
2261    $header = array(
2262      array('data' => 'Cid', 'class' => 'sitedoc_right'),
2263      t('Category'), 
2264      t('Recipients'), 
2265      array('data' => t('Weight'), 'class' => 'sitedoc_right'),
2266      array('data' => t('Selected'), 'align' => 'center'),
2267      );
2268    $rows = array();
2269    while ($contact = db_fetch_object($result)) {
2270      $recips = explode(',', $contact->recipients);
2271      if (count($recips) > 1) {
2272        $recip_list = theme('item_list', $recips);
2273      }
2274      else { $recip_list = $contact->recipients; }
2275      $rows[] = array(
2276        array('data' => $contact->cid, 'class' => 'sitedoc_right'),
2277        $contact->category,
2278        $recip_list,
2279        array('data' => $contact->weight, 'class' => 'sitedoc_right'),
2280        array('data' => $contact->selected ? 'yes' : 'no', 'align' => 'center'),
2281        );
2282    } /* end while role */
2283    
2284    $howmany = count($rows);
2285    if ($howmany > 0) {
2286      drupal_set_message(t('!count Contacts found.', array('!count' => $howmany)), 'status');
2287      $output = theme('table', $header, $rows);
2288    }
2289    return $output ."\n";
2290  }
2291  
2292  /**
2293   * Produce the user profile fields list.
2294   *
2295   *  Parameters:
2296   *    string - limits the list to only this category
2297   *
2298   *  Return: HTML string
2299   */
2300  function sitedoc_profile_fields() {
2301    if (!module_exists('profile')) {
2302      return '<p>Profile '. t('module not enabled') .'</p>';
2303    }
2304    $output = NULL;
2305  
2306    // Allow an optional param to limit the display to a specific category.
2307    if (func_num_args() > 0) {
2308      $category = func_get_arg(0);
2309    }
2310  
2311    $no_yes = array(t('No'), t('Yes')); 
2312    $viz = array(t('Hidden'), t('Private'), t('Profile'), t('Public'));
2313    // This is to save display space.
2314    $short_type = array(
2315      'textfield' => t('Text'),
2316      'textarea' => t('Area'),
2317      'URL' => t('URL'),
2318      'checkbox' => t('Check'),
2319      'selection' => t('select'),
2320      'list' => t('Free'),
2321      'date' => t('Date'),
2322      );
2323  
2324    $header = array(
2325      array('data' => t('Fid'), 'class' => 'sitedoc_right'),
2326      t('Title'),
2327      t('Name'),
2328      t('Explanation'),
2329      t('Page'),
2330      t('Type'),
2331      array('data' => t('Weight'), 'class' => 'sitedoc_right'),
2332      array('data' => t('Visiblity'), 'align' => 'center'),
2333      array('data' => t('Required'), 'align' => 'center'),
2334      array('data' => t('Register'), 'align' => 'center'),
2335      array('data' => t('Auto complete'), 'align' => 'center'),
2336      t('Options'),
2337      ); 
2338    if ($category) {
2339      $where = " WHERE p.category='$category'";
2340    }
2341    else {
2342      $where = ' ';
2343    }
2344    $result = db_query('SELECT * FROM {profile_fields} p'. $where .' ORDER BY p.category, p.weight, p.name');
2345    $save_cat = '';
2346  
2347    while ($field = db_fetch_object($result)) {
2348      if ($field->category <> $save_cat) {
2349        // Flush previous rows, if any.
2350        if (!empty($save_cat)) { 
2351          $output .= theme('table', $header, $rows);
2352        }
2353        $output .= '<h4>'. t('Category: ') . $field->category .'</h4>';
2354        $save_cat = $field->category;
2355        $rows = array();
2356      }
2357      $rows[] = array(
2358        array('data' => $field->fid, 'class' => 'sitedoc_right'),
2359        $field->title,
2360        $field->name,
2361        $field->explanation,
2362        $field->page,
2363        $short_type[$field->type],
2364        array('data' => $field->weight, 'class' => 'sitedoc_right'), 
2365        array('data' => $viz[$field->visibility], 'align' => 'center'), 
2366        array('data' => $no_yes[$field->required], 'align' => 'center'),
2367        array('data' => $no_yes[$field->register], 'align' => 'center'),
2368        array('data' => $no_yes[$field->autocomplete], 'align' => 'center'),
2369        $field->options,
2370        );
2371    } /* end while field */
2372    
2373    $howmany = count($rows);
2374    drupal_set_message(t('!count Profile fields found.', array('!count' => $howmany)), 'status');
2375    if ($howmany > 0) {
2376      $output .= theme('table', $header, $rows) ."\n";
2377    }
2378    return $output;
2379  }
2380  
2381  /**
2382   * Produce the URL Alias list and checks for orphans.
2383   *
2384   *  Parameters:
2385   *    string - limits the list to only this category
2386   *
2387   *  Return: HTML string
2388   */
2389  function sitedoc_url_alias() {
2390    if (!module_exists('path')) {
2391      return '<p>Path '. t('module not enabled') .'</p>';
2392    }
2393    $output = NULL;
2394    $rows = array();
2395    $last_src = NULL;
2396    $problems = 0;
2397    // For reverse checking.
2398    $node_alias = array();
2399  
2400    $header = array(
2401      array('data' => t('pid'), 'class' => 'sitedoc_right'),
2402      t('Source'),
2403      t('Destination'),
2404      t('Notes'),
2405      t('Operation'),
2406      ); 
2407    $result = db_query('SELECT u.pid, u.src, u.dst FROM {url_alias} u ORDER BY u.src, u.dst');
2408  
2409    while ($path = db_fetch_object($result)) {
2410      $notes = array();
2411      $ops = array();
2412      if ($last_src == $path->src) {
2413        $notes[] = t('duplicate source');
2414      } 
2415      else { $last_src = $path->src; }
2416    
2417      // Ignore "special" paths.
2418      if (substr($path->src, 0, 1) != '<') { 
2419        $p = explode('/', $path->src);
2420        $edit = 'edit';
2421      // So pseudo HTML shows (like <front>)
2422        $source = check_plain($path->src);   
2423   
2424        // Check first part of path.
2425        switch ($p[0]) {  
2426  
2427          case 'node':
2428            // Missing nid?
2429            if (empty($p[1])) { 
2430              $notes[] = '&lt;front&gt;?';
2431              break;
2432            }
2433            $node_exists = db_result(db_query('SELECT COUNT(n.nid) FROM {node} n WHERE n.nid = %d', $p[1]));
2434            if ($node_exists) { 
2435              $source = $path->src;
2436              $ops[] = l(t('edit node'), $path->src .'/edit');
2437            }
2438            else {
2439              $notes[] = '<span class="admin-missing">'. t('missing node') .'</span>'; 
2440              $edit = 'delete';
2441            }
2442            $node_alias[$p[1]] = TRUE;
2443            break;
2444  
2445          case 'taxonomy':
2446            $taxo_exists = db_result(db_query('SELECT COUNT(td.name) FROM {term_data} td WHERE td.tid = %d', $p[2]));
2447            if (!$taxo_exists) {
2448              $notes[] = '<span class="admin-missing">'. t('missing taxonomy term') .'</span>';
2449            }
2450            break;
2451  
2452          case 'faq':   /* same as taxo/term */
2453            $taxo_exists = db_result(db_query('SELECT COUNT(td.name) FROM {term_data} td WHERE td.tid = %d', $p[1]));
2454            if (!$taxo_exists) {
2455              $notes[] = '<span class="admin-missing">'. t('missing faq taxonomy term') .'</span>';
2456            }
2457            break;
2458  
2459          case 'user': 
2460            $node_exists = db_result(db_query('SELECT COUNT(u.uid) FROM {users} u WHERE u.uid = %d', $p[1]));
2461            if (!$node_exists) {
2462              $notes[] = '<span class="admin-missing">'. t('missing user') .'</span>';
2463            }
2464            break;
2465  
2466          case 'contact':  /* nothing to do */
2467            break;
2468  
2469          default:
2470            if (module_exists($p[0])) {
2471              $notes[] = '<span class="admin-enabled">'. t('ask the !name module', array('!name' => $p[0])) .'</span>'; 
2472            }
2473            else {
2474              $notes[] = '<span class="admin-missing">'. t('unhandled path type') .'</span>'; 
2475            }
2476  
2477        } /* end switch $p(0) */
2478      } /* end if not < */
2479      
2480      $problems += count($notes);
2481      $ops[] = l($edit .' '. t('path'), 'admin/build/path/'. $edit .'/'. $path->pid);
2482      $rows[] = array(array('data' => $path->pid, 'class' => 'sitedoc_right'),
2483                      $source,
2484                      $path->dst,
2485                      implode(', ', $notes),
2486                      implode(', ', $ops),
2487                  );
2488    } /* end while path */
2489    
2490    $howmany = count($rows);
2491    if ($howmany > 0) {
2492      $output .= theme('table', $header, $rows);
2493    }
2494    
2495    //  Now run through the nodes to see if they have an alias.
2496    $rows = array();
2497  
2498    $result = db_query('SELECT n.nid, n.title FROM {node} n ORDER BY n.nid');
2499    while ($node = db_fetch_object($result)) {
2500      // Does node NOT have an alias?
2501      if (!isset($node_alias[$node->nid])) { 
2502        $rows[] = array($node->title, l(t('edit'), 'node/'. $node->nid .'/edit'));
2503      }
2504    }
2505  
2506    if (count($rows) > 0) {
2507      $fieldset = array(
2508        '#title' => t('Nodes without URL Alias'),
2509        '#collapsible' => TRUE,
2510        '#collapsed' => count($rows) > 10,
2511        '#value' => theme('table', array(t('Title'), t('Operation')), $rows),
2512        );
2513      $output .=  '<br/>'. theme('fieldset', $fieldset);
2514    }
2515  
2516    $msg = t('!count URL Aliases found', array('!count' => $howmany));
2517    if ($problems) {
2518      $msg .= ' '. t('with !problems notes.', array('!problems' => $problems)) .' ';
2519    }
2520    else {
2521      $msg .= '.';
2522    }
2523    if (count($rows)) {
2524      $msg .= ' '. t('!count nodes without aliases found.', array('!count' => count($rows)));
2525    }
2526    drupal_set_message($msg, 'status');
2527  
2528    return $output ."\n";
2529  }
2530  
2531  function sitedoc_term_count_nodes($tid = 0, $type = NULL) {
2532    if ($type) {
2533      $count = db_result(db_query(db_rewrite_sql("SELECT COUNT(n.nid) FROM {term_node} t INNER JOIN {node} n ON t.nid = n.nid WHERE t.tid = %d AND n.status = 1 AND n.type = '%s'"), $tid, $type));
2534    }
2535    else {
2536      $count = db_result(db_query(db_rewrite_sql('SELECT COUNT(n.nid) FROM {term_node} t INNER JOIN {node} n ON t.nid = n.nid WHERE t.tid = %d AND n.status = 1'), $tid));
2537    }
2538    return $count;
2539  }
2540  
2541  /*
2542   *  This function gets the Input Format and Filter information.
2543   */
2544  function sitedoc_filters() {
2545    $output = "\n<h2>". t('Filters and Input Formats') .'</h2>';
2546    $filter_list = array();
2547    $default_format = variable_get('filter_default_format', 1);
2548  
2549    // Find out which modules provide filters.
2550    $filter_modules = module_implements('filter', TRUE);
2551    foreach ($filter_modules as $module) {
2552      $list = module_invoke($module, 'filter', 'list');
2553      foreach ($list as $delta => $name) {
2554        $filter_list[$module][$delta] = array(
2555          'name' => $name,
2556          'used' => array(),
2557          );
2558      }
2559    }
2560  
2561    $output .= "\n<h4>". t('Input Formats') ."</h4>\n";
2562  
2563    $rows = array();
2564    $filter_names = array();
2565    $count = 0;
2566    $output .= "\n<table>\n<tr>\n";
2567    $output .= "\n<th>". t('Format') .'</th>'
2568      .'<th>'. t('Name') .'</th>'
2569      .'<th>'. t('Roles') .'</th>'
2570      .'<th>'. t('Cache') .'</th>'
2571      .'<th>'. t('Filters') .'</th>'
2572      ."\n</tr>\n";
2573  
2574    // Get Input Formats.
2575    $result = db_query("SELECT * FROM {filter_formats}");
2576    while ($input = db_fetch_array($result)) {
2577      ++$count;
2578      $output .= "\n".'<tr class="'. ($count % 2 ? 'odd' : 'even') .'">';  
2579  
2580      $name = $input['name'];
2581      $format = $input['format'];
2582      $filter_names[$format] = $name;
2583  
2584      // Get the list of roles for this filter.
2585      // The roles list always starts and ends with a comma.
2586      $roles = substr($input['roles'], 1, strlen($input['roles']) - 2);
2587      $role_names = array();
2588      if (empty($roles)) {
2589        $roles = '<em>'. t('None') .'</em>';
2590      }
2591      else {
2592        // There is no need to look up the default, as all roles get it.
2593        if ($format == $default_format) {
2594          $roles = '<em>'. t('Default') .'</em>';
2595        }
2596        else {
2597          $r_result = db_query("SELECT name FROM {role} WHERE rid IN (%s)", $roles);
2598          while ($r = db_fetch_array($r_result)) {
2599            if (!in_array($r['name'], $role_names)) {
2600              $role_names[] = $r['name'];
2601            }
2602          }
2603          $roles = theme('item_list', $role_names);
2604        }
2605      }
2606      // Get the filter info.
2607      $f_rows = array();
2608      $filter_stuff = "\n<table><tr><th>". t('Name (Module, Delta)') .'</th><th>'.
2609        t('No Cache') .'</th><th>'.
2610        t('Weight') .'</th></tr>';
2611      $f_result = db_query("SELECT * FROM {filters} WHERE format=%d ORDER BY format, weight", $format);
2612      while ($filter = db_fetch_array($f_result)) {
2613        $module = $filter['module'];
2614        $delta = $filter['delta'];
2615        $filter_stuff .= "\n".'<tr>'
2616          .'<td>'. ucwords($filter_list[$module][$delta]['name']) .' ('. ucwords($module) .', '. $delta .')</td>'
2617          .'<td align="center">'. (module_invoke($module, 'filter', 'no cache', $delta, $format) ? 'True' : null) .'</td>'
2618          .'<td align="center">'. $filter['weight'] .'</td>'
2619          .'<tr>';
2620        $filter_list[$module][$delta]['used'][] = $filter_names[$format];
2621      }
2622      $filter_stuff .= "</table>\n";
2623  
2624      $cache = $input['cache'] ? _sitedoc_img_ok() : _sitedoc_img_warning();
2625  
2626      $output .= '<td valign="top">'. $format .'</td>'
2627        .'<td valign="top">'. l($name, 'admin/settings/filters/'. $format) .'</td>'
2628        .'<td valign="top">'. $roles .'</td>'
2629        .'<td align="center" valign="top">'. $cache .'</td>'
2630        .'<td valign="top">'. $filter_stuff .'</td>'
2631        ."\n</tr>";  
2632    }
2633    $output .= "\n</table>\n";
2634  
2635    $output .= "\n<h4>". t('Available Filters') ."</h4>\n";
2636  
2637  // Show usage info.
2638    $list_rows = array();
2639    foreach ($filter_list as $module => $deltas) {
2640      $descs = "\n<table>";
2641      foreach ($deltas as $delta => $values) {
2642        $name = ucwords($values['name']);
2643        $descs .= "\n".'<tr><td align="center">'. $delta .'</td><td width="100">'. $name .'</td><td>'.  $values['desc'] .'</td></tr>';
2644      }
2645      $descs .= '</table>';
2646      $used = $filter_list[$module][$delta]['used'];
2647      $list_rows[] = array(ucwords($module), $descs,
2648        count(used) ? implode(', ', $used) : t('None'),
2649        );
2650    }
2651    $output .= theme('table', array(t('Module'), t('Provides'), t('Used In')), $list_rows);
2652  
2653    return $output;
2654  }


Generated: Thu Mar 24 11:18:33 2011 Cross-referenced by PHPXref 0.7