You are here

pagenotfound_reports.module in Page Not Found Reports 7

Same filename and directory in other branches
  1. 6.2 pagenotfound_reports.module
  2. 6 pagenotfound_reports.module

Page Not Found Reports

Adds admin reports about common 404 errors.

File

pagenotfound_reports.module
View source
<?php

/**
 * @file
 * Page Not Found Reports
 *
 * Adds admin reports about common 404 errors.
 */

/**
 * Implements hook_menu().
 */
function pagenotfound_reports_menu() {
  $items = array();
  $items['admin/reports/404'] = array(
    'title' => '404 Reports',
    'description' => 'Reports on 404 (page not found) errors',
    'page callback' => 'pagenotfound_reports_top_404s',
    'access arguments' => array(
      'access site reports',
    ),
  );
  $items['admin/reports/404/overview'] = array(
    'title' => 'Most Common',
    'description' => 'Reports on 404 (page not found) errors',
    'page callback' => 'pagenotfound_reports_top_404s',
    'access arguments' => array(
      'access site reports',
    ),
    'type' => MENU_DEFAULT_LOCAL_TASK,
    'weight' => 1,
  );
  $items['admin/reports/404/referers'] = array(
    'title' => 'Referers',
    'description' => 'Pages that link to or include (as images or Javascript) paths that are not defined.',
    'page callback' => 'pagenotfound_reports_referers',
    'access arguments' => array(
      'access site reports',
    ),
    'type' => MENU_LOCAL_TASK,
    'weight' => 2,
  );
  $items['admin/reports/404/all'] = array(
    'title' => 'All 404s',
    'description' => 'All 404 errors in the log.',
    'page callback' => 'pagenotfound_reports_all',
    'access arguments' => array(
      'access site reports',
    ),
    'type' => MENU_LOCAL_TASK,
    'weight' => 3,
  );
  $items['admin/reports/404/tips'] = array(
    'title' => 'Tips',
    'description' => 'Tips to minimize 404 errors.',
    'page callback' => 'pagenotfound_reports_tips',
    'access arguments' => array(
      'access site reports',
    ),
    'type' => MENU_LOCAL_TASK,
    'weight' => 4,
  );
  return $items;
}

/**
 * Implements hook_form_FORM_ID_alter().
 *
 * Modify the redirect add/edit form to prepopualate the source field with
 * the path of a 404 record referenced in the querystring.
 */
function pagenotfound_reports_form_redirect_edit_form_alter(&$form, &$form_state) {
  $wid = $_REQUEST['pagenotfound-wid'];
  if (!empty($wid) && is_numeric($wid) && empty($form['source']['#default_value'])) {
    $path = db_select('watchdog', 'w')
      ->fields('w', array(
      'message',
    ))
      ->condition('type', 'page not found')
      ->condition('wid', $wid)
      ->execute()
      ->fetchField();
    if (!empty($path)) {
      $form['source']['#default_value'] = $path;
    }
  }
}

/**
 * Add a set of operation links to a single dblog (404) entry.
 */
function _pagenotfound_reports_add_operations($dblog) {
  $operations = array();
  if (module_exists('redirect') && ($path = $dblog->message)) {
    if ($existing = redirect_load_by_source($path)) {
      $operations[] = l(t('Edit redirect'), 'admin/config/search/redirect/edit/' . $existing->rid, array(
        'query' => drupal_get_destination(),
      ));
      $operations[] = l(t('Delete redirect'), 'admin/config/search/redirect/delete/' . $existing->rid, array(
        'query' => drupal_get_destination(),
      ));
    }
    elseif (!empty($dblog->wid)) {
      $operations[] = l(t('Add redirect'), 'admin/config/search/redirect/add', array(
        'query' => drupal_get_destination() + array(
          'pagenotfound-wid' => $dblog->wid,
        ),
      ));
    }
  }
  return !empty($operations) ? implode(' | ', $operations) : NULL;
}

/**
 * List of all 404s sorted by most common
 */
function pagenotfound_reports_top_404s() {
  $oldest_item = db_select('watchdog', 'w')
    ->fields('w', array(
    'timestamp',
  ))
    ->condition('type', 'page not found')
    ->orderBy('timestamp', 'ASC')
    ->range(0, 1)
    ->execute()
    ->fetchAssoc();
  $item_count = db_select('watchdog', 'w')
    ->fields('w', array(
    'wid',
  ))
    ->condition('type', 'page not found')
    ->execute()
    ->rowCount();

  // Stats
  $output = _pagenotfound_reports_summary_stats($oldest_item['timestamp'], $item_count);
  $header = array(
    array(
      'data' => t('Count'),
      'field' => 'count',
      'sort' => 'desc',
    ),
    array(
      'data' => t('Path'),
      'field' => 'message',
    ),
    array(
      'data' => t('Operations'),
    ),
  );
  $result = db_select('watchdog', 'w')
    ->fields('w', array(
    'wid',
    'message',
  ))
    ->condition('type', 'page not found')
    ->extend('PagerDefault')
    ->limit(30)
    ->extend('TableSort')
    ->orderByHeader($header)
    ->groupBy('message');
  $result
    ->addExpression('COUNT(wid)', 'count');
  $result = $result
    ->execute();
  $rows = array();
  foreach ($result as $dblog) {
    $rows[] = array(
      $dblog->count,
      truncate_utf8($dblog->message, 56, TRUE, TRUE),
      _pagenotfound_reports_add_operations($dblog),
    );
  }
  if (empty($rows)) {
    $rows[] = array(
      array(
        'data' => t('No log messages available.'),
        'colspan' => 3,
      ),
    );
  }
  $output .= theme('table', array(
    'header' => $header,
    'rows' => $rows,
  ));
  $output .= theme('pager', array(
    'tags' => NULL,
    'element' => 0,
  ));
  return $output;
}

/**
 * List of 404s with referers.
 */
function pagenotfound_reports_referers() {
  $oldest_item = db_select('watchdog', 'w')
    ->fields('w', array(
    'timestamp',
  ))
    ->condition('type', 'page not found')
    ->condition('referer', '', '!=')
    ->isNotNull('referer')
    ->orderBy('timestamp', 'ASC')
    ->range(0, 1)
    ->execute()
    ->fetchAssoc();
  $item_count = db_select('watchdog', 'w')
    ->fields('w', array(
    'wid',
  ))
    ->condition('type', 'page not found')
    ->condition('referer', '', '!=')
    ->isNotNull('referer')
    ->execute()
    ->rowCount();

  // Stats
  // Do NOT call _pagenotfound_reports_summary_stats(), since only 404 erros
  // with referrers are shown in this report
  $list_items = array();
  $list_items[] = t('%count: Total 404 errors with a referer available in log', array(
    '%count' => $item_count,
  ));
  $output = theme('item_list', array(
    'items' => $list_items,
  ));
  $header = array(
    array(
      'data' => t('Count'),
      'field' => 'count',
      'sort' => 'desc',
    ),
    array(
      'data' => t('Path'),
      'field' => 'message',
    ),
    array(
      'data' => t('Referer'),
      'field' => 'referer',
    ),
    array(
      'data' => t('Operations'),
    ),
  );
  $result = db_select('watchdog', 'w')
    ->fields('w', array(
    'wid',
    'message',
    'referer',
  ))
    ->condition('type', 'page not found')
    ->condition('referer', '', '!=')
    ->isNotNull('referer')
    ->extend('PagerDefault')
    ->limit(30)
    ->extend('TableSort')
    ->orderByHeader($header)
    ->groupBy('message')
    ->groupBy('referer');
  $result
    ->addExpression('COUNT(wid)', 'count');
  $result = $result
    ->execute();
  $rows = array();
  foreach ($result as $dblog) {
    $rows[] = array(
      $dblog->count,
      truncate_utf8($dblog->message, 56, TRUE, TRUE),
      l(truncate_utf8($dblog->referer, 56, TRUE, TRUE), $dblog->referer),
      _pagenotfound_reports_add_operations($dblog),
    );
  }
  if (empty($rows)) {
    $rows[] = array(
      array(
        'data' => t('No log messages available.'),
        'colspan' => 4,
      ),
    );
  }
  $output .= theme('table', array(
    'header' => $header,
    'rows' => $rows,
  ));
  $output .= theme('pager', array(
    'tags' => NULL,
    'element' => 0,
  ));
  return $output;
}

/**
 * List of all 404s in the log
 */
function pagenotfound_reports_all() {
  $oldest_item = db_select('watchdog', 'w')
    ->fields('w', array(
    'timestamp',
  ))
    ->condition('type', 'page not found')
    ->range(0, 1)
    ->execute()
    ->fetchAssoc();
  $item_count = db_select('watchdog', 'w')
    ->fields('w', array(
    'wid',
  ))
    ->condition('type', 'page not found')
    ->execute()
    ->rowCount();

  // Stats
  $output = _pagenotfound_reports_summary_stats($oldest_item['timestamp'], $item_count);
  $header = array(
    array(
      'data' => t('Date'),
      'field' => 'timestamp',
      'sort' => 'desc',
    ),
    array(
      'data' => t('Path'),
      'field' => 'message',
    ),
    array(
      'data' => t('Referer'),
      'field' => 'referer',
    ),
    array(
      'data' => t('Operations'),
    ),
  );
  $result = db_select('watchdog', 'w')
    ->fields('w', array(
    'wid',
    'timestamp',
    'message',
    'referer',
  ))
    ->condition('type', 'page not found')
    ->extend('PagerDefault')
    ->limit(30)
    ->extend('TableSort')
    ->orderByHeader($header)
    ->execute();
  $rows = array();
  foreach ($result as $dblog) {
    $rows[] = array(
      l(format_date($dblog->timestamp), 'admin/reports/event/' . $dblog->wid, array(
        'html' => TRUE,
      )),
      truncate_utf8($dblog->message, 56, TRUE, TRUE),
      l(truncate_utf8($dblog->referer, 56, TRUE, TRUE), $dblog->referer),
      _pagenotfound_reports_add_operations($dblog),
    );
  }
  if (empty($rows)) {
    $rows[] = array(
      array(
        'data' => t('No log messages available.'),
        'colspan' => 4,
      ),
    );
  }
  $output .= theme('table', array(
    'header' => $header,
    'rows' => $rows,
  ));
  $output .= theme('pager', array(
    'tags' => NULL,
    'element' => 0,
  ));
  return $output;
}

/**
 * Get summary stats to show above the report. Takes the $oldest_item and
 * $item_count variables because different reports include different types or
 * sets of log records, so they need to be figured for each report.
 *
 * @param $oldest_item_timestamp
 *   Timestamp of the oldest 404 error shown in current report
 * @param $item_count
 *   Number of 404 errors included in this report
 *
 * @return
 *   Formatted HTML of summary stats to show at the top of the report
 */
function _pagenotfound_reports_summary_stats($oldest_item_timestamp, $item_count) {

  // If there are no 404 errors, there are no stats to show
  if (empty($oldest_item_timestamp) || $item_count == 0) {
    return '';
  }

  // Stats
  $rate_of_404s = round($item_count / ((REQUEST_TIME - $oldest_item_timestamp) / 60), 2);
  $list_items = array();
  $list_items[] = t('%oldest: Oldest "page not found" error available in log', array(
    '%oldest' => format_date($oldest_item_timestamp),
  ));
  $list_items[] = t('%count: Total 404 errors available in log', array(
    '%count' => $item_count,
  ));
  $list_items[] = t('%count_per_minute: Errors per minute', array(
    '%count_per_minute' => $rate_of_404s,
  ));
  return theme('item_list', array(
    'items' => $list_items,
  ));
}

/**
 * Page of tips to minimize 404 errors
 */
function pagenotfound_reports_tips() {
  $header = array(
    t('Status'),
    t('Description'),
  );
  $rows = array();
  if (module_exists('redirect')) {
    $row = array(
      'data' => array(
        t('Done'),
        t('Redirect module is installed, so that redirects can be created from the 404 reports.'),
      ),
      'class' => array(
        'ok',
      ),
    );
  }
  else {
    $row = array(
      'data' => array(
        t('Todo'),
        t('Install the !redirect module so that redirects can be quickly and easily created for any item in the 404 reports.', array(
          '!redirect' => l('Redirect', 'http://drupal.org/project/redirect'),
        )),
      ),
      'class' => array(
        'warning',
      ),
    );
  }
  $rows[] = $row;
  return theme('table', array(
    'header' => $header,
    'rows' => $rows,
  ));
}

Functions

Namesort descending Description
pagenotfound_reports_all List of all 404s in the log
pagenotfound_reports_form_redirect_edit_form_alter Implements hook_form_FORM_ID_alter().
pagenotfound_reports_menu Implements hook_menu().
pagenotfound_reports_referers List of 404s with referers.
pagenotfound_reports_tips Page of tips to minimize 404 errors
pagenotfound_reports_top_404s List of all 404s sorted by most common
_pagenotfound_reports_add_operations Add a set of operation links to a single dblog (404) entry.
_pagenotfound_reports_summary_stats Get summary stats to show above the report. Takes the $oldest_item and $item_count variables because different reports include different types or sets of log records, so they need to be figured for each report.