1 <?php # vim:ts=2:sw=2:et:
2 /* For licensing and copyright terms, see the file named LICENSE */
4 die("web.php not used any more");
5 /* Simplistic pathinfo parsing - could optionally have additional features such
7 function mtrack_parse_pathinfo($vars) {
8 $pi = isset($_SERVER['PATH_INFO']) ? $_SERVER['PATH_INFO'] : '';
9 $data = explode('/', $pi);
11 $return_vars = array();
13 foreach($vars as $name => $value) {
14 if (isset($data[$i])) {
15 $return_vars[$name] = $data[$i];
18 $return_vars[$name] = $value;
24 /* Pathinfo retrieval minus starting slash */
25 function mtrack_get_pathinfo($no_strip = false) {
26 $pi = isset($_SERVER['PATH_INFO']) ? $_SERVER['PATH_INFO'] : NULL;
27 if ($pi !== NULL && strlen($pi) && $no_strip == false) {
33 function mtrack_calc_root()
35 /* ABSWEB: the absolute URL to the base of the web app */
38 /* if they have one, use the weburl config value for this */
39 $ABSWEB = MTrackConfig::get('core', 'weburl');
40 if (strlen($ABSWEB)) {
44 /* otherwise, determine the root of the app.
45 * This is complicated because the DOCUMENT_ROOT may refer to an area that
46 * is completely unrelated to the actual root of the web application, for
47 * instance, in the case that the user has a public_html dir where they
48 * are running mtrack */
50 /* determine the root of the app */
51 $sdir = dirname($_SERVER['SCRIPT_FILENAME']);
52 $idir = dirname(dirname(__FILE__)) . '/web';
53 $diff = substr($sdir, strlen($idir)+1);
54 $rel = preg_replace('@[^/]+@', '..', $diff);
58 /* $rel is now the relative path to the root of the web app, from the current
61 if (isset($_SERVER['HTTP_HOST'])) {
62 $ABSWEB = ((isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') ?
63 'https' : 'http') . '://' . $_SERVER['HTTP_HOST'];
65 $ABSWEB = 'http://localhost';
68 $bits = explode('/', $rel);
69 $base = $_SERVER['SCRIPT_NAME'];
70 foreach ($bits as $b) {
71 $base = dirname($base);
76 $ABSWEB .= $base . '/';
81 function mtrack_head($title, $navbar = true)
84 static $mtrack_did_head;
86 $whoami = mtrack_username(MTrackAuth::whoami(),
92 if ($mtrack_did_head) {
95 $mtrack_did_head = true;
97 $projectname = htmlentities(MTrackConfig::get('core', 'projectname'),
99 $logo = MTrackConfig::get('core', 'projectlogo');
101 $projectname = "<img alt='$projectname' src='$logo'>";
103 $fav = MTrackConfig::get('core', 'favicon');
106 <link rel="icon" href="$fav" type="image/x-icon" />
107 <link rel="shortcut icon" href="$fav" type="image/x-icon" />
113 $title = htmlentities($title, ENT_QUOTES, 'utf-8');
115 $userinfo = "Logged in as $whoami";
116 MTrackNavigation::augmentUserInfo($userinfo);
118 echo "<!DOCTYPE html>
121 <meta http-equiv=\"Content-Type\" value=\"text/html; charset=utf-8\">
122 <meta http-equiv=\"X-UA-Compatible\" content=\"IE=8\">
123 <title>$title</title>
127 /* For licensing and copyright terms, see the file named LICENSE */
130 'css/smoothness/jquery-ui-1.7.2.custom.css',
133 'css/markitup/markitup-simple.css',
134 'css/markitup/wiki.css',
135 'css/hyperlight/plain.css',
136 'css/hyperlight/vibrant-ink.css',
137 'css/hyperlight/zenburn.css',
138 'css/hyperlight/wezterm.css',
141 foreach ($scripts as $name) {
143 echo "<link rel=\"stylesheet\" href=\"${ABSWEB}$name\" type=\"text/css\" />";
150 'jquery-1.4.2.min.js',
151 'jquery-ui-1.8.2.custom.min.js',
152 'jquery.asmselect.js',
153 'jquery.flot.pack.js',
154 'jquery.MultiFile.pack.js',
156 'jquery.treeview.js',
157 'jquery.tablesorter.js',
158 'jquery.metadata.js',
159 'jquery.markitup.js',
168 <script type="text/javascript">
169 var ABSWEB = "'. $ABSWEB . '";
173 foreach ($scripts as $name) {
174 echo "<script type=\"text/javascript\" src=\"$ABSWEB/js/$name\"></script>\n";
179 foreach (MTrackDB::q('select priorityname, value from priorities')
180 ->fetchAll() as $row) {
181 $PRI_SWITCH .= "case '$row[0]': return $row[1];\n";
184 foreach (MTrackDB::q('select sevname, ordinal from severities')
185 ->fetchAll() as $row) {
186 $SEV_SWITCH .= "case '$row[0]': return $row[1];\n";
191 <script type="text/javsacript">
193 $(document).ready(function() {
195 $.tablesorter.addParser({
201 format: function(s) {
209 $.tablesorter.addParser({
215 format: function(s) {
236 <div id="banner-back">
237 <form id="mainsearch" action="${ABSWEB}search.php">
239 <input type="text" class="search" title="Type and press enter to Search"
240 name="q" accesskey="f">
252 if (MTrackAuth::whoami() !== 'anonymous') {
253 // $nav['/'] = 'Today';
255 $navcandidates = array(
256 "/Browse.php" => array("Browse", 'read', 'Browser'),
257 // "/wiki.php" => array("Wiki", 'read', 'Wiki'),
258 "/timeline.php" => array("Timeline", 'read', 'Timeline'),
259 // "/roadmap.php" => array("Roadmap", 'read', 'Roadmap'),
260 "/report.php/1" => array("Active Tickets", 'read', 'Reports'),
261 "/report.php/3" => array("Pending Review", 'read', 'Reports'),
262 "/Ticket.php/new" => array("New Ticket", 'create', 'Tickets'),
263 // "/snippet.php" => array("Snippets", 'read', 'Snippets'),
264 "/admin/" => array("Administration", 'modify', 'Enumerations', 'Components', 'Projects', 'Browser'),
266 foreach ($navcandidates as $url => $data) {
267 $label = array_shift($data);
268 $right = array_shift($data);
270 foreach ($data as $object) {
271 if (MTrackACL::hasAllRights($object, $right)) {
281 echo mtrack_nav('mainnav', $nav);
286 if (MTrackConfig::get('core', 'admin_party') == 1 &&
287 MTrackAuth::whoami() == 'adminparty' &&
288 ($_SERVER['REMOTE_ADDR'] == '127.0.0.1' ||
289 $_SERVER['REMOTE_ADDR'] == '::1')) {
291 <div class='ui-state-error ui-corner-all'>
292 <span class='ui-icon ui-icon-alert'></span>
293 <b>Welcome to the admin party!</b> Authentication is not yet configured;
294 while it is in this state, any user connecting from the localhost
295 address is treated as having admin rights (that includes you, and this
296 is why you are seeing this message). All other users are treated
297 as anonymous users.<br>
298 <b><a href="{$ABSWEB}admin/auth.php">Click here to Configure Authentication</a></b>
301 } elseif (!MTrackAuth::isAuthConfigured() &&
302 MTrackConfig::get('core', 'admin_party') == 1)
304 $localaddr = preg_replace('@^(https?://)([^/]+)/(.*)$@',
305 "\\1localhost/\\3", $ABSWEB);
308 <div class='ui-state-highlight ui-corner-all'>
309 <span class='ui-icon ui-icon-info'></span>
310 <b>Authentication is not yet configured</b>. If you are the admin,
311 you should use the <b><a href="$localaddr">localhost address</a></b>
312 to reach the system and configure it.
315 } elseif (!MTrackAuth::isAuthConfigured()) {
317 <div class='ui-state-highlight ui-corner-all'>
318 <span class='ui-icon ui-icon-info'></span>
319 <b>Authentication is not yet configured</b>. If you are the admin,
320 you will need to edit the config.ini file to configure authentication.
325 if (ini_get('magic_quotes_gpc') === true ||
326 !strcasecmp(ini_get('magic_quotes_gpc'), 'on')) {
328 <div class='ui-state-error ui-corner-all'>
329 <span class='ui-icon ui-icon-alert'></span>
330 <b>magic_quotes_gpc</b> is enabled. This causes mtrack not to work.
331 Please disable this setting in your server configuration.
343 function mtrack_foot($visible_markup = true)
348 if ($visible_markup) {
351 <div class="navfoot">
352 Powered by <a href="http://bitbucket.org/wez/mtrack/">mtrack</a>
357 \$(document).ready(function () {
358 window.mtrack_footer_position();
363 if (MTrackConfig::get('core', 'debug.footer')) {
366 echo "<!-- " . MTrackDB::$queries . " queries\n";
367 var_export(MTrackDB::$query_strings);
368 echo "\n\nforks\n\n";
375 interface IMTrackExtensionPage {
376 /** called to dispatch a page render */
377 function dispatchRequest();
380 class MTrackExtensionPage {
381 static $locations = array();
382 static function registerLocation($location, IMTrackExtensionPage $page) {
383 self::$locations[$location] = $page;
385 static function locationToURL($location) {
387 return $ABSWEB . 'ext.php/' . $location;
389 static function bindToPage($location) {
390 while (strlen($location)) {
391 if (isset(self::$locations[$location])) {
392 return self::$locations[$location];
394 if (strpos($location, '/') === false) {
397 $location = dirname($location);
402 function mtrack_date($tstring, $show_full = false)
404 /* database time is always relative to UTC */
405 $d = date_create($tstring, new DateTimeZone('UTC'));
406 if (!is_object($d)) {
407 throw new Exception("could not represent $tstring as a datetime object");
409 $iso8601 = $d->format(DateTime::W3C);
410 /* but we want to render relative to user prefs */
411 date_timezone_set($d, new DateTimeZone(date_default_timezone_get()));
412 $full = $d->format('D, M d Y H:i');
415 return "<abbr title=\"$iso8601\" class='timeinterval'>$full</abbr>";
418 return "<abbr title='$iso8601' class='timeinterval'>$full</abbr> <span class='fulldate'>$full</span>";
421 function mtrack_rmdir($dir)
423 foreach (scandir($dir) as $ent) {
424 if ($ent == '.' || $ent == '..') {
427 $full = $dir . DIRECTORY_SEPARATOR . $ent;
437 function mtrack_make_temp_dir($do_make = true)
439 $tempdir = sys_get_temp_dir();
440 $base = $tempdir . DIRECTORY_SEPARATOR . "mtrack." . uniqid();
441 for ($i = 0; $i < 1024; $i++) {
442 $candidate = $base . sprintf("%04x", $i);
444 if (mkdir($candidate)) {
449 if (!file_exists($candidate) && !is_dir($candidate)) {
454 throw new Exception("unable to make temp dir based on path $candidate");
456 /// moved to mtrack_dataobjects_event (currently)
457 //function mtrack_diff_strings($before, $now)
460 function mtrack_last_chance_saloon($e)
462 // adding headers on exceptions here causes nightmares for dynamic content..
464 if ($e instanceof MTrackAuthorizationException) {
465 if (MTrackAuth::whoami() == 'anonymous') {
466 MTrackAuth::forceAuthenticate();
468 // mtrack_head('Insufficient Privilege');
469 echo '<h1>Insufficient Privilege</h1>';
470 $rights = is_array($e->rights) ? join(', ', $e->rights) : $e->rights;
471 echo "You do not have the required set of rights ($rights) to access this page<br>";
476 $msg = $e->getMessage();
479 // mtrack_head('Whoops: ' . $msg);
480 } catch (Exception $doublefault) {
483 echo "<h1>An error occurred!</h1>";
485 echo htmlentities($msg, ENT_QUOTES, 'utf-8');
489 echo nl2br(htmlentities($e->getTraceAsString(), ENT_QUOTES, 'utf-8'));
493 } catch (Exception $doublefault) {
499 function mtrack_gravatar($email, $size = 24)
504 return "<img class='gravatar' width='$size' height='$size' src='http://www.gravatar.com/avatar/" . md5(strtolower($email)) . "?s=$size&d=wavatar'>";
506 // mtrack_defrepo ==> MTrack_Repo::defaultRepo($cfg);
507 // mtrack_changeset_url ==> MTrack_Repo::loadByChangeSet = see linkhandler::changeset
510 function mtrack_tag($tag, $repo = null)
512 return "<span class='tagname'>$tag</span>";
515 function mtrack_keyword($keyword)
518 $kw = urlencode($keyword);
519 return "<a class='keyword' href='{$ABSWEB}search.php?q=keyword%3A$kw'>$keyword</span>";
522 function mtrack_multi_select_box($name, $title, $items, $values = null)
524 $title = htmlentities($title, ENT_QUOTES, 'utf-8');
525 $html = "<select id='$name' name='{$name}[]' multiple='multiple' title='$title'>";
526 foreach ($items as $k => $v) {
527 $html .= "<option value='" .
528 htmlspecialchars($k, ENT_QUOTES, 'utf-8') .
530 if (isset($values[$k])) {
531 $html .= ' selected';
533 $html .= ">" . htmlentities($v, ENT_QUOTES, 'utf-8') . "</option>\n";
535 return $html . "</select>";
538 function mtrack_select_box($name, $items, $value = null, $keyed = true)
540 $html = "<select id='$name' name='$name'>";
541 foreach ($items as $k => $v) {
542 $html .= "<option value='" .
543 htmlspecialchars($k, ENT_QUOTES, 'utf-8') .
545 if (($keyed && $value == $k) || (!$keyed && $value == $v)) {
546 $html .= ' selected';
548 $html .= ">" . htmlentities($v, ENT_QUOTES, 'utf-8') . "</option>\n";
550 return $html . "</select>";
553 function mtrack_radio($name, $value, $curval)
555 $checked = $curval == $value ? " checked='checked'": '';
556 return "<input type='radio' id='$value' name='$name' value='$value'$checked>";
559 function mtrack_diff($diffstr)
563 if (is_resource($diffstr)) {
565 while (($line = fgets($diffstr)) !== false) {
566 $lines[] = rtrim($line, "\r\n");
571 if (is_string($diffstr)) {
572 $abase = md5($diffstr);
573 $diffstr = preg_split("/\r?\n/", $diffstr);
575 $abase = md5(join("\n", $diffstr));
578 /* we could use toggle() below, but it is much faster to determine
579 * if we are hiding or showing based on a single variable than evaluating
580 * that for each possible cell */
582 <button class='togglediffcopy' type='button'>Toggle Diff Line Numbers</button>
584 $html .= "<table class='code diff'>";
585 //$html = "<pre class='code diff'>";
588 if (!count($diffstr)) {
591 $line = array_shift($diffstr);
593 if (!strncmp($line, '@@ ', 3)) {
594 /* done with preamble */
597 $line = htmlspecialchars($line, ENT_QUOTES, 'utf-8');
598 $line = "<tr class='meta'><td class='lineno'></td><td class='lineno'></td><td class='lineno'></td><td width='100%'>$line</tr>";
599 $html .= $line . "\n";
602 $lines = array(0, 0);
607 if (preg_match("/^@@\s+-(\pN+)(?:,\pN+)?\s+\+(\pN+)(?:,\pN+)?\s*@@/",
609 $lines[0] = (int)$M[1] - 1;
610 $lines[1] = (int)$M[2] - 1;
613 } elseif (strlen($line)) {
614 if ($line[0] == '-') {
617 } elseif ($line[0] == '+') {
628 $row = "<tr class='$class";
632 if ($class != 'meta' && $first) {
640 $row .= "<td class='lineno'></td><td class='lineno'></td>";
643 $row .= "<td class='lineno'></td><td class='lineno'>" . $lines[1] . "</td>";
646 $row .= "<td class='lineno'>" . $lines[0] . "</td><td class='lineno'></td>";
649 $row .= "<td class='lineno'>" . $lines[0] . "</td><td class='lineno'>" . $lines[1] . "</td>";
651 $anchor = $abase . '.' . $nlines;
652 $row .= "<td class='linelink'><a name='$anchor'></a><a href='#$anchor' title='link to this line'>#</a></td>";
654 $line = htmlspecialchars($line, ENT_QUOTES, 'utf-8');
655 $row .= "<td class='line' width='100%'>$line</td></tr>\n";
658 if (!count($diffstr)) {
661 $line = array_shift($diffstr);
673 function mtrack_mime_detect($filename, $namehint = null)
675 /* does config tell us how to decide mimetype */
676 $detector = MTrackConfig::get('core', 'mimetype_detect');
678 /* if detector is blank, we'll try to figure out which one to use */
679 if (empty($detector)) {
680 if (function_exists('finfo_open')) {
681 $detector = 'fileinfo';
682 } elseif (function_exists('mime_content_type')) {
683 $detector = 'mime_magic';
689 /* use detector or all mimetypes will be blank */
690 if ($detector === 'fileinfo') {
691 if (defined('FILEINFO_MIME_TYPE')) {
692 $fileinfo = finfo_open(FILEINFO_MIME_TYPE);
694 $magic = MTrackConfig::get('core', 'mime.magic');
695 if (strlen($magic)) {
696 $fileinfo = finfo_open(FILEINFO_MIME, $magic);
698 $fileinfo = finfo_open(FILEINFO_MIME);
701 $mimetype = finfo_file($fileinfo, $filename);
702 finfo_close($fileinfo);
703 } elseif ($detector === 'mime_magic') {
704 $mimetype = mime_content_type($filename);
705 } elseif (PHP_OS != 'SunOS') {
706 $mimetype = shell_exec("file -b --mime " . escapeshellarg($filename));
708 $mimetype = 'application/octet-stream';
710 $mimetype = trim(preg_replace("/\s*;.*$/", '', $mimetype));
711 if (empty($mimetype)) {
712 $mimetype = 'application/octet-stream';
714 if ($mimetype == 'application/octet-stream') {
715 if ($namehint === null) {
716 $namehint = $filename;
718 $pi = pathinfo($namehint);
719 switch (strtolower($pi['extension'])) {
720 case 'bin': return 'application/octet-stream';
721 case 'exe': return 'application/octet-stream';
722 case 'dll': return 'application/octet-stream';
723 case 'iso': return 'application/octet-stream';
724 case 'so': return 'application/octet-stream';
725 case 'a': return 'application/octet-stream';
726 case 'lib': return 'application/octet-stream';
727 case 'pdf': return 'application/pdf';
728 case 'ps': return 'application/postscript';
729 case 'ai': return 'application/postscript';
730 case 'eps': return 'application/postscript';
731 case 'ppt': return 'application/vnd.ms-powerpoint';
732 case 'xls': return 'application/vnd.ms-excel';
733 case 'tiff': return 'image/tiff';
734 case 'tif': return 'image/tiff';
735 case 'wbmp': return 'image/vnd.wap.wbmp';
736 case 'png': return 'image/png';
737 case 'gif': return 'image/gif';
738 case 'jpg': return 'image/jpeg';
739 case 'jpeg': return 'image/jpeg';
740 case 'ico': return 'image/x-icon';
741 case 'bmp': return 'image/bmp';
742 case 'css': return 'text/css';
743 case 'htm': return 'text/html';
744 case 'html': return 'text/html';
745 case 'txt': return 'text/plain';
746 case 'xml': return 'text/xml';
747 case 'eml': return 'message/rfc822';
748 case 'asc': return 'text/plain';
749 case 'rtf': return 'application/rtf';
750 case 'wml': return 'text/vnd.wap.wml';
751 case 'wmls': return 'text/vnd.wap.wmlscript';
752 case 'gtar': return 'application/x-gtar';
753 case 'gz': return 'application/x-gzip';
754 case 'tgz': return 'application/x-gzip';
755 case 'tar': return 'application/x-tar';
756 case 'zip': return 'application/zip';
757 case 'sql': return 'text/plain';
759 // if the file is ascii, then treat it as text/plain
760 $fp = fopen($filename, 'rb');
761 $mimetype = 'text/plain';
763 $x = fread($fp, 8192);
764 if (!strlen($x)) break;
765 if (preg_match('/([\x80-\xff])/', $x, $M)) {
766 $mimetype = 'application/octet-stream';
776 if (php_sapi_name() != 'cli') {
777 set_exception_handler('mtrack_last_chance_saloon');
778 error_reporting(E_NOTICE|E_ERROR|E_WARNING);
779 // ini_set('display_errors', false);