and work all night, producing logs for WordPress administrators and developers alike. I'll also press wildflowers if necessary. Version: 1.0a20070125 Author: Joel Pan Author URI: http://ketsugi.com/ Copyright 2006 Joel Pan This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ require_once('hatchet.php'); $lumberjack_db = new Hatchet(); $lumberjack_logs_table = $lumberjack_db->prefix('lumberjack_logs'); $lumberjack_loggers = array(); /** * The base class for all of Lumberjack's functions, for name-spacing purposes * * @package Lumberjack * @author Joel Pan * @version 1.0 **/ class Lumberjack { /** * Creates the necessary MySQL tables and WordPress options * * @return void * @global Hatchet $lumberjack_db **/ function install () { // Create MySQL table global $lumberjack_db, $lumberjack_logs_table; $query = <<query($query); // Set default options add_option('lumberjack_logviewer_dateformat', 'Y-m-d H:i:s'); } /** * Removes the MySQL tables and WordPress options * * @return void * @global Hatchet $lumberjack_db **/ function uninstall () { global $wpdb, $lumberjack_db, $lumberjack_logs_table; // Delete all Lumberjack-related options $query = 'DELETE FROM `' . $wpdb->options . '` WHERE `option_name` LIKE "lumberjack_%"'; $lumberjack_db->query($query); // Remove MySQL table $query = "DROP TABLE `$lumberjack_logs_table`"; $lumberjack_db->query($query); } /** * Adds an entry to the log table * * @param string $event The type of event that's being logged * @param string $log The log entry * @return void **/ function add_log_entry ($event, $log, $extended='') { global $lumberjack_db, $lumberjack_logs_table; $timestamp = gmdate('Y-m-d H:i:s'); $query = sprintf("INSERT INTO `$lumberjack_logs_table` (`time`, `event`, `log`, `extended`) VALUES (%s, %s, %s, %s)", Lumberjack::_quote_smart($timestamp), Lumberjack::_quote_smart($event), Lumberjack::_quote_smart($log), Lumberjack::_quote_smart($extended)); $lumberjack_db->query($query); } /** * Smart escaping of SQL data, taken from PHP.net * * @param string $value * @return string **/ function _quote_smart($value) { // Stripslashes if ( get_magic_quotes_gpc() ) { $value = stripslashes($value); } // Quote if not a number or a numeric string if ( !is_numeric($value) ) { $value = "'" . mysql_real_escape_string($value) . "'"; } return $value; } /** * Adds the log viewer and options page to WordPress * * @return void **/ function add_pages () { // Add the Options page add_options_page(__('Lumberjack Options', 'Lumberjack'), __('Lumberjack', 'Lumberjack'), "manage_options", 'wp-content/plugins/lumberjack/options.php'); // Add the Log Viewer page add_submenu_page('index.php', __('Lumberjack Logs', 'Lumberjack'), __('Lumberjack Logs', 'Lumberjack'), "manage_options", 'wp-content/plugins/lumberjack/view_logs.php'); } /** * Generate RSS link for Log viewer page * * @return string **/ function get_logviewer_rsslink () { // Store search query in $logs_search $logs_search = $_REQUEST['s']; $logs_search_query = $logs_search == '' ? "1" : '`log` REGEXP ' . Lumberjack::_quote_smart($logs_search); if ( '' != $logs_search ) $logs_search_link = '&s=' . urlencode($logs_search); // Store events to show in $logs_show_types, default to all $logs_show_types = $_REQUEST['type']; if ( !is_array($logs_show_types) && 0 == count($logs_show_types) ) { $logs_show_types_query = ''; } else { foreach ( $logs_show_types as $log_type ) { $logs_show_types_link .= '&type[]=' . urlencode($log_type); } } return sprintf("%s/wp-admin/index.php?page=lumberjack/view_logs.php&export=rss%s%s", get_bloginfo('wpurl'), $logs_search_link, $logs_show_types_link); } /** * The CSS information and Javascript files for the Log Viewer page * * @return void **/ function view_log_header () { // CSS echo ''; // Javascript echo ''; echo ''; echo ''; // RSS printf('', Lumberjack::get_logviewer_rsslink()); } /** * Create and return the export file to the browser * * @return void **/ function export_file() { // Export file // Get the export format $export_format = strtolower($_GET['export']); if ($export_format != '') { require_once('hatchet.php'); $lumberjack_db = new Hatchet(); $lumberjack_logs_table = $lumberjack_db->prefix('lumberjack_logs'); // Store search query in $logs_search $logs_search = $_REQUEST['s']; $logs_search_query = $logs_search == '' ? "1" : '`log` REGEXP "' . mysql_real_escape_string($logs_search) . '"'; if ( '' != $logs_search ) $logs_search_link = '&s=' . urlencode($logs_search); // Store events to show in $logs_show_types, default to all $logs_show_types = $_REQUEST['type']; if ( !is_array($logs_show_types) && 0 == count($logs_show_types) ) { $logs_show_types_query = ''; $logs_show_types = array(); } else { $logs_show_types_query = 'AND '; $i = 0; foreach ( $logs_show_types as $log_type ) { if ( $i++ > 0 ) $logs_show_types_query .= ' OR'; $logs_show_types_query .= " `event`='$log_type'"; $logs_show_types_link .= '&type%255B%255D=' . urlencode($log_type); } } // Create select query $query = sprintf("SELECT `id`, UNIX_TIMESTAMP(`time`) as `timestamp`, `event`, `log`, `extended` FROM `%s` WHERE %s %s ORDER BY `id` DESC", $lumberjack_logs_table, $logs_search_query, $logs_show_types_query); $result = $lumberjack_db->query($query); switch ($export_format) { case 'rss': // Set the appropriate mime header header("Content-type: text/xml"); $permalink = sprintf("%s/wp-admin/index.php?page=lumberjack/view_logs.php%s%s", get_bloginfo('wpurl'), $logs_search_link, $logs_show_types_link); $rss_permalink = $permalink . '&export=rss'; $rss_description = sprintf("Lumberjack Logs for %s (Search: %s, Types: %s)", get_bloginfo_rss('name'), htmlentities($logs_search), ( 0 == count($logs_show_types) ) ? 'All' : htmlentities(implode(' & ', $logs_show_types)) ); // Define RSS Date format for PHP < 5.1.1 if (!defined('DATE_RSS')) define ( 'DATE_RSS', 'D, d M Y H:i:s +0000' ); $date = gmdate(DATE_RSS); // XML headers echo << Lumberjack Logs $rss_permalink $rss_description $date http://ketsugi.com/software/wordpress/lumberjack-plugin en EOT; // Generate RSS items $max = 20; // Max number of items to output while ($max-- > 0 && $row = mysql_fetch_assoc($result)) { $guid = sprintf('Log #%d at %s', $row['id'], get_bloginfo('home')); // Create the RSS item title: event type + first line of log $title = ent2ncr(htmlentities($row['event'] . ': ' . $row['log'])); $title = __($row['event'], 'Lumberjack') . ': ' . ent2ncr(htmlentities($row['log'], ENT_COMPAT, 'UTF-8')); // Create the permalink $item_permalink = "$permalink#log-{$row['id']}"; // Create the RSS date $pubDate = date(DATE_RSS, $row['timestamp']); // Create the RSS description $description = ent2ncr(htmlentities($row['extended']), ENT_COMPAT, 'UTF-8'); $replace = array ( "\n" => '
' ); $description = str_replace(array_keys($replace), array_values($replace), $description); echo << $title $item_permalink $guid $pubDate EOT; } ?>
32767 ) $row['extended'] = substr($row['extended'], 0, 32764) . '...'; // Excel can handle a max of 1024 chars // Escape the quote marks foreach ( $row as $key => $value ) { $value = '"' . str_replace('"', '""', $value) . '"'; } // Add the comma delimiters $csv_row = implode(',', $row); echo $csv_row. "\n"; } break; } flush(); die(); } } } // END CLASS Lumberjack /** * Base class for all logger extension classes * * @package Lumberjack * @author Joel Pan * @version 1.0 **/ class Logger { /** * Returns the name of the event type being logged by this Logger * * @abstract * @return string **/ function get_event () { return 'Logger Base Class'; } /** * Returns a nested array of logger options and their corresponding hooks and functions * * @abstract * @return array **/ function get_hooks () { return array(); } /** * Hook into the necessary WordPress actions * * @return void **/ function do_hooks () { $hooks = $this->get_hooks(); foreach ( $hooks as $option => $hook ) { if ( get_option($option) == 'on' ) { if ( $hook['action'] != '' ) add_action($hook['action'], array(get_class($this), $hook['function']), isset($hook['priority']) ? $hook['priority'] : 10, isset($hook['params']) ? $hook['params'] : 1); elseif ( $hook['filter'] != '' ) add_filter($hook['filter'], array(get_class($this), $hook['function']), isset($hook['priority']) ? $hook['priority'] : 10, isset($hook['params']) ? $hook['params'] : 1); } } } /** * Converts an underscored string into a regular string * eg. "comment_name" --> "Comment Name" * * @param string $text * @return string **/ function convert_underscores ($text) { $output = str_replace('_', ' ', $text); $output = ucwords($output); return $output; } /** * Returns a print-friendly display of an array's elements for extended log purposes * * @param array $info * @return string **/ function array_to_string ($info) { if ( is_array($info) ) { foreach ( $info as $key => $value ) { $output .= Logger::convert_underscores($key); if ( Logger::is_associative_array($info) ) $output .= ': ' . $value; $output .= "\n"; } } return $output; } /** * Returns true if the parameter is an associative array, false otherwise * * @param mixed $var * @return bool **/ function is_associative_array ($var) { return is_array( $var ) && !is_numeric( implode( array_keys( $var ) ) ); } } // END CLASS Logger /** * Initialisation and other code **/ $loggers_path = ABSPATH . 'wp-content/plugins/lumberjack/loggers'; // cwd is assumed to be /wp-admin global $lumberjack_loggers; // Stores all the loggers // Scan 'loggers' subdirectory and load logger files if ( function_exists('scandir') ) { // PHP5 method $logger_files = scandir($loggers_path); foreach ($logger_files as $logger_file) { if ( !is_dir($logger_file) && substr($logger_file, -4) == '.php' ) { // Load only PHP files require_once("$loggers_path/$logger_file"); } } } else { if ( $handle = opendir($loggers_path) ) { // PHP4 method while (false !== ($logger_file = readdir($handle))) { if ( !is_dir($logger_file) && substr($logger_file, -4) == '.php' ) { // Load only PHP files require_once("$loggers_path/$logger_file"); } } closedir($handle); } // Sort the loggers alphabetically (this is automatic in the PHP5 version but not here, so let's just make sure behaviour is consistent across both versions asort ($lumberjack_loggers); } // Hook into the actions/filters foreach ($lumberjack_loggers as $logger) { $logger->do_hooks(); } add_action('admin_menu', array('Lumberjack', 'add_pages')); if ( 'lumberjack' == substr($_REQUEST['page'], 0, 10) ) { // Only for Lumberjack pages, no need to clutter up other pages with this stuff add_action('admin_head', array('Lumberjack', 'view_log_header')); add_action('init', array('Lumberjack', 'export_file')); } add_action('activate_lumberjack/lumberjack.php', array('Lumberjack', 'install')); add_action('deactivate_lumberjack/lumberjack.php', array('Lumberjack', 'uninstall')); ?>