forked from GNUsocial/gnu-social
		
	
		
			
				
	
	
		
			369 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			PHP
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			369 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			PHP
		
	
	
		
			Executable File
		
	
	
	
	
| <?php
 | |
| // Phergie Log Viewer ... Currently designed as a single PHP file in order to make it easy to
 | |
| //  'install' this.  Just drop the index.php (or whatever name you wish to rename it to) wherever
 | |
| //  you wish, and it will simply work.  Sure, it would be nice to structure some of this stuff into
 | |
| //  various include files/etc.  But right now this is simple enough of a quick log viewer, that it's
 | |
| //  just one file.
 | |
| 
 | |
| 
 | |
| /********** SETUP **********/
 | |
| 
 | |
| // (Change any of these if/as needed for your setup) 
 | |
| ini_set('default_charset', 'UTF-8');
 | |
| date_default_timezone_set('UTC');
 | |
| $log = "/PATH/AND/FILENAME/TO/YOUR/LOGFILE/PLEASE.db";
 | |
| 
 | |
| 
 | |
| /********** PREPARATION **********/
 | |
| 
 | |
| $db = new PDO('sqlite:' . $log);
 | |
| if (!is_object($db)) {
 | |
|     // Failure, can't access Phergie Log.  Bail with an error message, not pretty, but works:
 | |
|     echo "ERROR: Cannot access Phergie Log File, please check the configuration & access privileges";
 | |
|     exit();
 | |
| }
 | |
| 
 | |
| 
 | |
| /********** DETECTION **********/
 | |
| 
 | |
| // Determine the mode of the application and call the appropriate handler function
 | |
| $mode = empty($_GET['m']) ? '' : $_GET['m'];
 | |
| switch ($mode) {
 | |
|     case 'channel':
 | |
|         show_days($db);
 | |
|         break;
 | |
|     case 'day':
 | |
|         show_log($db);
 | |
|         break;
 | |
|     default:
 | |
|         show_channels($db);
 | |
| }
 | |
| 
 | |
| // Exit not really needed here, but reminds us that everything below is support functions:
 | |
| exit();
 | |
| 
 | |
| 
 | |
| /********** MODES **********/
 | |
| 
 | |
| /**
 | |
|  * show_channels
 | |
|  *
 | |
|  * Provide a list of all channel's that we are logging information for:
 | |
|  *
 | |
|  * @param PDO A PDO object referring to the database 
 | |
|  * @return void
 | |
|  * @author Eli White <eli@eliw.com>
 | |
|  **/
 | |
| function show_channels(PDO $db) {
 | |
|     // Begin the HTML page:
 | |
|     template_header('Channels');
 | |
|     echo "\nChannels:\n<ul>\n";
 | |
| 
 | |
|     // Loop through the database reading in each channel, and echoing out a <li> for it.
 | |
|     // only grab actual channels that start with # ... also pre-lowercase everything.
 | |
|     // this allows us to 'deal' with variable caps in how the channels were logged.
 | |
|     $channels = $db->query("
 | |
|         select distinct lower(chan) as c
 | |
|         from logs
 | |
|         where chan like '#%'
 | |
|         ");
 | |
|     foreach ($channels as $row) {
 | |
|         $html = utf8specialchars($row['c']);
 | |
|         $url = urlencode($row['c']);
 | |
|         echo "<li><a href=\"?m=channel&w={$url}\">{$html}</a></li>\n";        
 | |
|     }
 | |
| 
 | |
|     // Finish off the page:
 | |
|     echo "\n</ul>\n";
 | |
|     template_footer();
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * show_days
 | |
|  *
 | |
|  * Create a calendar view of all days available for this particular channel
 | |
|  * 
 | |
|  * NOTE: May get unwieldy if large log files.  Perhaps consider in the future
 | |
|  *  making a paginated version of this?  by year?  Or a separate 'which year' page
 | |
|  *  before this?  Not to worry about now.
 | |
|  *
 | |
|  * @param PDO A PDO object referring to the database 
 | |
|  * @return void
 | |
|  * @author Eli White <eli@eliw.com>
 | |
|  **/
 | |
| function show_days(PDO $db) {
 | |
|     $channel = $_GET['w'];
 | |
|     $url = urlencode($channel);
 | |
| 
 | |
|     // Begin the HTML page:
 | |
|     template_header('Daily Logs for Channel: ' . utf8specialchars($channel));
 | |
|     echo "\n<ul>\n";
 | |
| 
 | |
|     // Query the database to discover all days that are available for this channel:
 | |
|     $data = array();
 | |
|     $prepared = $db->prepare("
 | |
|         select distinct date(tstamp) as day
 | |
|         from logs
 | |
|         where lower(chan) = :chan
 | |
|         ");
 | |
|     $prepared->execute(array(':chan' => $channel));
 | |
|     foreach ($prepared as $row) {
 | |
|         list($y, $m, $d) = explode('-', $row['day']);
 | |
|         $data[$y][$m][$d] = "{$y}-{$m}-{$d}";
 | |
|     }
 | |
| 
 | |
|     // For now, just loop over them all and provide a list:
 | |
|     ksort($data);
 | |
|     foreach ($data as $year => $months) {
 | |
|         ksort($months);
 | |
|         foreach ($months as $month => $days) {
 | |
|             // Figure out a few facts about this month:
 | |
|             $stamp = mktime(0, 0, 0, $month, 1, $year);
 | |
|             $first_weekday = idate('w', $stamp);
 | |
|             $days_in_month = idate('t', $stamp);
 | |
|             $name = date('F', $stamp);
 | |
| 
 | |
|             // We have a month ... start a new table:
 | |
|             echo <<<EOTABLE
 | |
| <div class="month">
 | |
|   <table>
 | |
|     <caption>{$name} {$year}</caption>
 | |
|     <tr><th>Sun</th><th>Mon</th><th>Tue</th><th>Wed</th><th>Thu</th><th>Fri</th><th>Sat</th></tr>
 | |
| EOTABLE;
 | |
|             // Now we need to start looping through the days in this month:
 | |
|             echo '<tr>';
 | |
|             $rowmod = 0;
 | |
|             // Loop through all day entries, no matter how many blanks we need:
 | |
|             for ($d = (-$first_weekday + 1); $d < $days_in_month + 1; $d++) {
 | |
|                 if (!($rowmod++ % 7)) {
 | |
|                     // Stop/start a new row:
 | |
|                     echo '</tr><tr>';
 | |
|                 }
 | |
|                 echo '<td>';
 | |
|                 // If this day is pre or post actual month days, make it blank:
 | |
|                 if (($d < 1) || ($d > $days_in_month)) {
 | |
|                     echo ' ';
 | |
|                 } elseif (isset($days[$d])) {
 | |
|                     // Make a link to the day's log:
 | |
|                     echo "<a href=\"?m=day&w={$url}&d={$days[$d]}\">{$d}</a>";            
 | |
|                 } else {
 | |
|                     // Just a dead number:
 | |
|                     echo $d;
 | |
|                 }
 | |
|                 echo '</td>';
 | |
|             }
 | |
|             // Finish off any blanks needed for a complete table row:
 | |
|             while ($rowmod++ % 7) {
 | |
|                 echo '<td> </td>';
 | |
|             }
 | |
|             echo "</tr></table></div>\n";
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     // Finish off the page:
 | |
|     echo "\n</ul>\n";
 | |
|     template_footer();    
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * show_log
 | |
|  *
 | |
|  * Actually show the log for this specific day
 | |
|  *
 | |
|  * @param PDO A PDO object referring to the database 
 | |
|  * @return void
 | |
|  * @author Eli White <eli@eliw.com>
 | |
|  **/
 | |
| function show_log(PDO $db) {
 | |
|     $channel = $_GET['w'];
 | |
|     $day = $_GET['d'];
 | |
|     $parts = explode('-', $day);
 | |
|     $formatted_date = "{$parts[0]}-{$parts[1]}-{$parts[2]}";
 | |
| 
 | |
|     // Begin the HTML page:
 | |
|     template_header('Date: ' . utf8specialchars($formatted_date) .
 | |
|         ' - Channel: ' . utf8specialchars($channel));
 | |
| 
 | |
|     // Query the database to get all log lines for this date:
 | |
|     $prepared = $db->prepare("
 | |
|         select time(tstamp) as t, type, nick, message
 | |
|         from logs
 | |
|         where lower(chan) = :chan and date(tstamp) = :day
 | |
|         order by tstamp asc
 | |
|         ");
 | |
|     $prepared->execute(array(
 | |
|         ':chan' => $channel,
 | |
|         ':day' => $day,
 | |
|         ));
 | |
| 
 | |
|     // Loop through each line,
 | |
|     foreach ($prepared as $row) {
 | |
|         // Prepare some basic details for output:
 | |
|         $color = nick_color($row['nick']);
 | |
|         $time = utf8specialchars($row['t']);
 | |
|         $msg = utf8specialchars($row['message']);
 | |
|         $nick = utf8specialchars($row['nick']);
 | |
|         $type = false;
 | |
|         
 | |
|         // Now change the format of the line based upon the type:
 | |
|         switch ($row['type']) {
 | |
|             case 4: // PRIVMSG (A Regular Message)
 | |
|                 echo "[$time] <span style=\"color:#{$color};\"><{$nick}></span> {$msg}<br />\n";
 | |
|                 break;
 | |
|             case 5: // ACTION (emote)
 | |
|                 echo "[$time] <span style=\"color:#{$color};\">*{$nick} {$msg}</span><br />\n";
 | |
|                 break;
 | |
|             case 1: // JOIN
 | |
|                 echo "[$time] -> {$nick} joined the room.<br />\n";
 | |
|                 break;
 | |
|             case 2: // PART (leaves channel)
 | |
|                 echo "[$time] -> {$nick} left the room: {$msg}<br />\n";
 | |
|                 break;
 | |
|             case 3: // QUIT (quits the server)
 | |
|                 echo "[$time] -> {$nick} left the server: {$msg}<br />\n";
 | |
|                 break;
 | |
|             case 6: // NICK (changes their nickname)
 | |
|                 echo "[$time] -> {$nick} is now known as: {$msg}<br />\n";
 | |
|                 break;
 | |
|             case 7: // KICK (booted)
 | |
|                 echo "[$time] -> {$nick} boots {$msg} from the room.<br />\n";
 | |
|                 break;
 | |
|             case 8: // MODE (changed their mode)
 | |
|                 $type = 'MODE';
 | |
|             case 9: // TOPIC (changed the topic)
 | |
|                 $type = $type ? $type : 'TOPIC';
 | |
|                 echo "[$time] -> {$nick}: :{$type}: {$msg}<br />\n";
 | |
|         }
 | |
|     }
 | |
|         
 | |
|     // Finish up the page:
 | |
|     template_footer();
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * nick_color
 | |
|  *
 | |
|  * Uses a silly little algorithm to pick a consistent but unique(ish) color for
 | |
|  *  any given username.  NOTE: Augment this in the future to make it not generate
 | |
|  *  'close to white' ones, also maybe to ensure uniqueness?  (Not allow two to have
 | |
|  *  colors that are close to each other?)
 | |
|  *
 | |
|  * @return string A CSS valid hex color string
 | |
|  * @author Eli White <eli@eliw.com>
 | |
|  **/
 | |
| function nick_color($user) {
 | |
|     static $colors = array();
 | |
|     
 | |
|     if (!isset($colors[$user])) {
 | |
|         $colors[$user] = substr(md5($user), 0, 6);
 | |
|     }
 | |
| 
 | |
|     return $colors[$user];
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * utf8specialchars
 | |
|  *
 | |
|  * Just a quick wrapper around htmlspecialchars
 | |
|  *
 | |
|  * @param string The UTF8 string to escape
 | |
|  * @return string An escaped and ready for HTML use string
 | |
|  * @author Eli White <eli@eliw.com>
 | |
|  **/
 | |
| function utf8specialchars($string) {
 | |
|     return htmlspecialchars($string, ENT_COMPAT, 'UTF-8');
 | |
| }
 | |
| 
 | |
| 
 | |
| /********** TEMPLATES **********/
 | |
| 
 | |
| /**
 | |
|  * template_header
 | |
|  *
 | |
|  * Echo out the header for each HTML page
 | |
|  *
 | |
|  * @param $title string The title to be used for this page.
 | |
|  * @return void
 | |
|  * @author Eli White <eli@eliw.com>
 | |
|  **/
 | |
| function template_header($title) {
 | |
|     $css = template_css();
 | |
|     echo <<<EOHTML
 | |
| <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" 
 | |
|   "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
 | |
| <html lang="en" xmlns="http://www.w3.org/1999/xhtml">
 | |
|   <head>
 | |
|     <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
 | |
|     <title>Phergie LogViewer - {$title}</title>
 | |
|     <style type="text/css" media="all">{$css}</style>
 | |
|   </head>
 | |
|   <body>
 | |
|     <h2>Phergie LogViewer - {$title}</h2>
 | |
| EOHTML;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * template_footer
 | |
|  *
 | |
|  * Echo out the bottom of each HTML page
 | |
|  *
 | |
|  * @return void
 | |
|  * @author Eli White <eli@eliw.com>
 | |
|  **/
 | |
| function template_footer() {
 | |
|     echo <<<EOHTML
 | |
|   </body>
 | |
| </html>
 | |
| EOHTML;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * template_css
 | |
|  *
 | |
|  * Generate the CSS used by these HTML pages & return it.
 | |
|  *
 | |
|  * @return string The CSS in question:
 | |
|  * @author Eli White <eli@eliw.com>
 | |
|  **/
 | |
| function template_css() {
 | |
|     return <<<EOCSS
 | |
|     div.month {
 | |
|         float: left;
 | |
|         height: 15em;
 | |
|     }
 | |
| 
 | |
|     div.month table {
 | |
|         border-collapse: collapse;
 | |
|         border: 2px solid black;
 | |
|         margin-right: 2em;
 | |
|     }
 | |
| 
 | |
|     div.month td, div.month th {
 | |
|         text-align: center;
 | |
|         vertical-align: bottom;
 | |
|         border: 1px solid gray;
 | |
|         width: 2em;
 | |
|         height: 1.7em;
 | |
|         padding: 1px;
 | |
|         margin: 0px;
 | |
|     }
 | |
| 
 | |
|     div.month th {
 | |
|         text-decoration: bold;
 | |
|         border: 2px solid black;
 | |
|     }
 | |
| 
 | |
|     div.month a {
 | |
|         text-decoration: none;
 | |
|     }
 | |
| 
 | |
|     a:visited, a:link {
 | |
|         color: blue;
 | |
|     }
 | |
| 
 | |
|     a:active, a:hover {
 | |
|         color: red;
 | |
|     }
 | |
| EOCSS;
 | |
| }
 |