PageRenderTime 43ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/Phergie/Tools/LogViewer/index.php

https://github.com/zburnham/phergie
PHP | 416 lines | 229 code | 41 blank | 146 comment | 10 complexity | d86368cbf52d88a7a34869e58c374374 MD5 | raw file
  1. <?php
  2. /**
  3. * Phergie
  4. *
  5. * PHP version 5
  6. *
  7. * LICENSE
  8. *
  9. * This source file is subject to the new BSD license that is bundled
  10. * with this package in the file LICENSE.
  11. * It is also available through the world-wide-web at this URL:
  12. * http://phergie.org/license
  13. *
  14. * @category Phergie
  15. * @package Phergie
  16. * @author Phergie Development Team <team@phergie.org>
  17. * @copyright 2008-2012 Phergie Development Team (http://phergie.org)
  18. * @license http://phergie.org/license New BSD License
  19. * @link http://pear.phergie.org/package/Phergie
  20. */
  21. /**
  22. * Phergie Log Viewer ... Currently designed as a single PHP file in order to make
  23. * it easy to 'install' this. Just drop the index.php (or whatever name you wish
  24. * to rename it to) wherever you wish, and it will simply work. Sure, it would be
  25. * nice to structure some of this stuff into various include files/etc. But right
  26. * now this is simple enough of a quick log viewer, that it's just one file.
  27. *
  28. */
  29. /********** SETUP **********/
  30. // (Change any of these if/as needed for your setup)
  31. ini_set('default_charset', 'UTF-8');
  32. date_default_timezone_set('UTC');
  33. $log = "/PATH/AND/FILENAME/TO/YOUR/LOGFILE/PLEASE.db";
  34. /********** PREPARATION **********/
  35. $db = new PDO('sqlite:' . $log);
  36. if (!is_object($db)) {
  37. // Failure, can't access Phergie Log.
  38. // Bail with an error message, not pretty, but works:
  39. echo "ERROR: Cannot access Phergie Log File, "
  40. . "please check the configuration & access privileges";
  41. exit();
  42. }
  43. /********** DETECTION **********/
  44. // Determine the mode of the application and call the appropriate handler function
  45. $mode = empty($_GET['m']) ? '' : $_GET['m'];
  46. switch ($mode) {
  47. case 'channel':
  48. show_days($db);
  49. break;
  50. case 'day':
  51. show_log($db);
  52. break;
  53. default:
  54. show_channels($db);
  55. }
  56. // Exit not really needed here,
  57. // but reminds us that everything below is support functions:
  58. exit();
  59. /********** MODES **********/
  60. /**
  61. * show_channels
  62. *
  63. * Provide a list of all channel's that we are logging information for:
  64. *
  65. * @param PDO $db A PDO object referring to the database
  66. *
  67. * @return void
  68. * @author Eli White <eli@eliw.com>
  69. **/
  70. function show_channels(PDO $db)
  71. {
  72. // Begin the HTML page:
  73. template_header('Channels');
  74. echo "\nChannels:\n<ul>\n";
  75. // Loop through the database reading in each channel,
  76. // and echoing out a <li> for it.
  77. // only grab actual channels that start with # ... also pre-lowercase everything.
  78. // this allows us to 'deal' with variable caps in how the channels were logged.
  79. $channels = $db->query(
  80. "select distinct lower(chan) as c
  81. from logs
  82. where chan like '#%'"
  83. );
  84. foreach ($channels as $row) {
  85. $html = utf8specialchars($row['c']);
  86. $url = urlencode($row['c']);
  87. echo "<li><a href=\"?m=channel&w={$url}\">{$html}</a></li>\n";
  88. }
  89. // Finish off the page:
  90. echo "\n</ul>\n";
  91. template_footer();
  92. }
  93. /**
  94. * show_days
  95. *
  96. * Create a calendar view of all days available for this particular channel
  97. *
  98. * NOTE: May get unwieldy if large log files. Perhaps consider in the future
  99. * making a paginated version of this? by year? Or a separate 'which year' page
  100. * before this? Not to worry about now.
  101. *
  102. * @param PDO $db A PDO object referring to the database
  103. *
  104. * @return void
  105. * @author Eli White <eli@eliw.com>
  106. **/
  107. function show_days(PDO $db)
  108. {
  109. $channel = $_GET['w'];
  110. $url = urlencode($channel);
  111. // Begin the HTML page:
  112. template_header('Daily Logs for Channel: ' . utf8specialchars($channel));
  113. echo "\n<ul>\n";
  114. // Query the database to discover all days that are available for this channel:
  115. $data = array();
  116. $prepared = $db->prepare(
  117. "select distinct date(tstamp) as day
  118. from logs
  119. where lower(chan) = :chan"
  120. );
  121. $prepared->execute(array(':chan' => $channel));
  122. foreach ($prepared as $row) {
  123. list($y, $m, $d) = explode('-', $row['day']);
  124. $data[$y][$m][$d] = "{$y}-{$m}-{$d}";
  125. }
  126. // For now, just loop over them all and provide a list:
  127. ksort($data);
  128. foreach ($data as $year => $months) {
  129. ksort($months);
  130. foreach ($months as $month => $days) {
  131. // Figure out a few facts about this month:
  132. $stamp = mktime(0, 0, 0, $month, 1, $year);
  133. $first_weekday = idate('w', $stamp);
  134. $days_in_month = idate('t', $stamp);
  135. $name = date('F', $stamp);
  136. // We have a month ... start a new table:
  137. echo <<<EOTABLE
  138. <div class="month">
  139. <table>
  140. <caption>{$name} {$year}</caption>
  141. <tr><th>Sun</th><th>Mon</th><th>Tue</th><th>Wed</th>
  142. <th>Thu</th><th>Fri</th><th>Sat</th>
  143. </tr>
  144. EOTABLE;
  145. // Now we need to start looping through the days in this month:
  146. echo '<tr>';
  147. $rowmod = 0;
  148. // Loop through all day entries, no matter how many blanks we need:
  149. for ($d = (-$first_weekday + 1); $d < $days_in_month + 1; $d++) {
  150. if (!($rowmod++ % 7)) {
  151. // Stop/start a new row:
  152. echo '</tr><tr>';
  153. }
  154. echo '<td>';
  155. // If this day is pre or post actual month days, make it blank:
  156. if (($d < 1) || ($d > $days_in_month)) {
  157. echo '&nbsp;';
  158. } elseif (isset($days[$d])) {
  159. // Make a link to the day's log:
  160. echo "<a href=\"?m=day&w={$url}&d={$days[$d]}\">{$d}</a>";
  161. } else {
  162. // Just a dead number:
  163. echo $d;
  164. }
  165. echo '</td>';
  166. }
  167. // Finish off any blanks needed for a complete table row:
  168. while ($rowmod++ % 7) {
  169. echo '<td>&nbsp;</td>';
  170. }
  171. echo "</tr></table></div>\n";
  172. }
  173. }
  174. // Finish off the page:
  175. echo "\n</ul>\n";
  176. template_footer();
  177. }
  178. /**
  179. * show_log
  180. *
  181. * Actually show the log for this specific day
  182. *
  183. * @param PDO $db A PDO object referring to the database
  184. *
  185. * @return void
  186. * @author Eli White <eli@eliw.com>
  187. **/
  188. function show_log(PDO $db)
  189. {
  190. $channel = $_GET['w'];
  191. $day = $_GET['d'];
  192. $parts = explode('-', $day);
  193. $formatted_date = "{$parts[0]}-{$parts[1]}-{$parts[2]}";
  194. // Begin the HTML page:
  195. template_header(
  196. 'Date: ' . utf8specialchars($formatted_date) .
  197. ' - Channel: ' . utf8specialchars($channel)
  198. );
  199. // Query the database to get all log lines for this date:
  200. $prepared = $db->prepare(
  201. "select time(tstamp) as t, type, nick, message
  202. from logs
  203. where lower(chan) = :chan and date(tstamp) = :day
  204. order by tstamp asc"
  205. );
  206. $prepared->execute(
  207. array(
  208. ':chan' => $channel,
  209. ':day' => $day,
  210. )
  211. );
  212. // Loop through each line,
  213. foreach ($prepared as $row) {
  214. // Prepare some basic details for output:
  215. $color = nick_color($row['nick']);
  216. $time = utf8specialchars($row['t']);
  217. $msg = utf8specialchars($row['message']);
  218. $nick = utf8specialchars($row['nick']);
  219. $type = false;
  220. // Now change the format of the line based upon the type:
  221. switch ($row['type']) {
  222. case 4: // PRIVMSG (A Regular Message)
  223. echo "[$time] <span style=\"color:#{$color};\">"
  224. . "&lt;{$nick}&gt;</span> {$msg}<br />\n";
  225. break;
  226. case 5: // ACTION (emote)
  227. echo "[$time] <span style=\"color:#{$color};\">"
  228. . "*{$nick} {$msg}</span><br />\n";
  229. break;
  230. case 1: // JOIN
  231. echo "[$time] -> {$nick} joined the room.<br />\n";
  232. break;
  233. case 2: // PART (leaves channel)
  234. echo "[$time] -> {$nick} left the room: {$msg}<br />\n";
  235. break;
  236. case 3: // QUIT (quits the server)
  237. echo "[$time] -> {$nick} left the server: {$msg}<br />\n";
  238. break;
  239. case 6: // NICK (changes their nickname)
  240. echo "[$time] -> {$nick} is now known as: {$msg}<br />\n";
  241. break;
  242. case 7: // KICK (booted)
  243. echo "[$time] -> {$nick} boots {$msg} from the room.<br />\n";
  244. break;
  245. case 8: // MODE (changed their mode)
  246. $type = 'MODE';
  247. case 9: // TOPIC (changed the topic)
  248. $type = $type ? $type : 'TOPIC';
  249. echo "[$time] -> {$nick}: :{$type}: {$msg}<br />\n";
  250. }
  251. }
  252. // Finish up the page:
  253. template_footer();
  254. }
  255. /**
  256. * nick_color
  257. *
  258. * Uses a silly little algorithm to pick a consistent but unique(ish) color for
  259. * any given username. NOTE: Augment this in the future to make it not generate
  260. * 'close to white' ones, also maybe to ensure uniqueness? (Not allow two to have
  261. * colors that are close to each other?)
  262. *
  263. * @param String $user TODO username to operate on
  264. *
  265. * @return string A CSS valid hex color string
  266. * @author Eli White <eli@eliw.com>
  267. **/
  268. function nick_color($user)
  269. {
  270. static $colors = array();
  271. if (!isset($colors[$user])) {
  272. $colors[$user] = substr(md5($user), 0, 6);
  273. }
  274. return $colors[$user];
  275. }
  276. /**
  277. * utf8specialchars
  278. *
  279. * Just a quick wrapper around htmlspecialchars
  280. *
  281. * @param string $string The UTF-8 string to escape
  282. *
  283. * @return string An escaped and ready for HTML use string
  284. * @author Eli White <eli@eliw.com>
  285. **/
  286. function utf8specialchars($string)
  287. {
  288. return htmlspecialchars($string, ENT_COMPAT, 'UTF-8');
  289. }
  290. /********** TEMPLATES **********/
  291. /**
  292. * template_header
  293. *
  294. * Echo out the header for each HTML page
  295. *
  296. * @param String $title The title to be used for this page.
  297. *
  298. * @return void
  299. * @author Eli White <eli@eliw.com>
  300. **/
  301. function template_header($title)
  302. {
  303. $css = template_css();
  304. echo <<<EOHTML
  305. <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
  306. "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
  307. <html lang="en" xmlns="http://www.w3.org/1999/xhtml">
  308. <head>
  309. <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
  310. <title>Phergie LogViewer - {$title}</title>
  311. <style type="text/css" media="all">{$css}</style>
  312. </head>
  313. <body>
  314. <h2>Phergie LogViewer - {$title}</h2>
  315. EOHTML;
  316. }
  317. /**
  318. * template_footer
  319. *
  320. * Echo out the bottom of each HTML page
  321. *
  322. * @return void
  323. * @author Eli White <eli@eliw.com>
  324. **/
  325. function template_footer()
  326. {
  327. echo <<<EOHTML
  328. </body>
  329. </html>
  330. EOHTML;
  331. }
  332. /**
  333. * template_css
  334. *
  335. * Generate the CSS used by these HTML pages & return it.
  336. *
  337. * @return string The CSS in question:
  338. * @author Eli White <eli@eliw.com>
  339. **/
  340. function template_css()
  341. {
  342. return <<<EOCSS
  343. div.month {
  344. float: left;
  345. height: 15em;
  346. }
  347. div.month table {
  348. border-collapse: collapse;
  349. border: 2px solid black;
  350. margin-right: 2em;
  351. }
  352. div.month td, div.month th {
  353. text-align: center;
  354. vertical-align: bottom;
  355. border: 1px solid gray;
  356. width: 2em;
  357. height: 1.7em;
  358. padding: 1px;
  359. margin: 0px;
  360. }
  361. div.month th {
  362. text-decoration: bold;
  363. border: 2px solid black;
  364. }
  365. div.month a {
  366. text-decoration: none;
  367. }
  368. a:visited, a:link {
  369. color: blue;
  370. }
  371. a:active, a:hover {
  372. color: red;
  373. }
  374. EOCSS;
  375. }