Dave Jarvis' Repositories

git clone https://repo.autonoma.ca/repo/treetrek.git

Reformats code

AuthorDave Jarvis <email>
Date2026-02-08 18:29:12 GMT-0800
Commit8b1325a1bab49e862081d81428149e09f59aecec
Parent7008695
new/Router.php
class Router {
- private $repositories;
-
- public function __construct(array $repositories) {
- $this->repositories = $repositories;
- }
+ private $repositories;
- public function route(): Page {
- $reqRepo = $_GET['repo'] ?? '';
- $action = $_GET['action'] ?? 'home';
- $hash = $this->sanitizePath($_GET['hash'] ?? '');
+ public function __construct(array $repositories) {
+ $this->repositories = $repositories;
+ }
- // Find the specific repository object
- $currentRepo = null;
- $decoded = urldecode($reqRepo);
- foreach ($this->repositories as $repo) {
- if ($repo['safe_name'] === $reqRepo || $repo['name'] === $decoded) {
- $currentRepo = $repo;
- break;
- }
- }
+ public function route(): Page {
+ $reqRepo = $_GET['repo'] ?? '';
+ $action = $_GET['action'] ?? 'home';
+ $hash = $this->sanitizePath($_GET['hash'] ?? '');
- // Inject the full list ($this->repositories) into every page
- // so they can render the navigation dropdown.
- if (!$currentRepo) {
- return new HomePage($this->repositories);
- }
+ // Find the specific repository object
+ $currentRepo = null;
+ $decoded = urldecode($reqRepo);
+ foreach ($this->repositories as $repo) {
+ if ($repo['safe_name'] === $reqRepo || $repo['name'] === $decoded) {
+ $currentRepo = $repo;
+ break;
+ }
+ }
- $git = new Git($currentRepo['path']);
+ // Inject the full list ($this->repositories) into every page
+ // so they can render the navigation dropdown.
+ if (!$currentRepo) {
+ return new HomePage($this->repositories);
+ }
- if ($action === 'commits') {
- return new CommitsPage($this->repositories, $currentRepo, $git, $hash);
- }
+ $git = new Git($currentRepo['path']);
- return new FilePage($this->repositories, $currentRepo, $git, $hash);
+ if ($action === 'commits') {
+ return new CommitsPage($this->repositories, $currentRepo, $git, $hash);
}
- private function sanitizePath($path) {
- $path = str_replace(['..', '\\', "\0"], ['', '/', ''], $path);
- return preg_replace('/[^a-zA-Z0-9_\-\.\/]/', '', $path);
- }
+ return new FilePage($this->repositories, $currentRepo, $git, $hash);
+ }
+
+ private function sanitizePath($path) {
+ $path = str_replace(['..', '\\', "\0"], ['', '/', ''], $path);
+ return preg_replace('/[^a-zA-Z0-9_\-\.\/]/', '', $path);
+ }
}
new/Views.php
<?php
abstract class BasePage implements Page {
- protected $repositories;
- protected $title;
+ protected $repositories;
+ protected $title;
- public function __construct(array $repositories) {
- $this->repositories = $repositories;
- }
+ public function __construct(array $repositories) {
+ $this->repositories = $repositories;
+ }
- // Shared Layout Logic
- protected function renderLayout($contentCallback, $currentRepo = null) {
- ?>
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
- <title><?php echo Config::SITE_TITLE . ($this->title ? ' - ' . htmlspecialchars($this->title) : ''); ?></title>
- <link rel="stylesheet" href="repo.css">
- <style>
- .commit-list { margin-top: 20px; }
- .commit-row { display: flex; padding: 10px 0; border-bottom: 1px solid #eee; gap: 15px; align-items: baseline; }
- .commit-row:last-child { border-bottom: none; }
- .commit-row .sha { font-family: monospace; color: #0366d6; text-decoration: none; }
- .commit-row .message { flex: 1; font-weight: 500; }
- .commit-row .meta { font-size: 0.85em; color: #666; white-space: nowrap; }
- </style>
- </head>
- <body>
- <div class="container">
- <header>
- <h1><?php echo Config::SITE_TITLE; ?></h1>
- <nav class="nav">
- <a href="?">Home</a>
- <?php if ($currentRepo):
- $safeName = urlencode($currentRepo['safe_name']); ?>
- <a href="?repo=<?php echo $safeName; ?>">Files</a>
- <a href="?action=commits&repo=<?php echo $safeName; ?>">Commits</a>
- <a href="?action=refs&repo=<?php echo $safeName; ?>">Branches</a>
- <?php endif; ?>
+ // Shared Layout Logic
+ protected function renderLayout($contentCallback, $currentRepo = null) {
+ ?>
+ <!DOCTYPE html>
+ <html lang="en">
+ <head>
+ <meta charset="UTF-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <title><?php echo Config::SITE_TITLE . ($this->title ? ' - ' . htmlspecialchars($this->title) : ''); ?></title>
+ <link rel="stylesheet" href="repo.css">
+ <style>
+ .commit-list { margin-top: 20px; }
+ .commit-row { display: flex; padding: 10px 0; border-bottom: 1px solid #eee; gap: 15px; align-items: baseline; }
+ .commit-row:last-child { border-bottom: none; }
+ .commit-row .sha { font-family: monospace; color: #0366d6; text-decoration: none; }
+ .commit-row .message { flex: 1; font-weight: 500; }
+ .commit-row .meta { font-size: 0.85em; color: #666; white-space: nowrap; }
+ </style>
+ </head>
+ <body>
+ <div class="container">
+ <header>
+ <h1><?php echo Config::SITE_TITLE; ?></h1>
+ <nav class="nav">
+ <a href="?">Home</a>
+ <?php if ($currentRepo):
+ $safeName = urlencode($currentRepo['safe_name']); ?>
+ <a href="?repo=<?php echo $safeName; ?>">Files</a>
+ <a href="?action=commits&repo=<?php echo $safeName; ?>">Commits</a>
+ <a href="?action=refs&repo=<?php echo $safeName; ?>">Branches</a>
+ <?php endif; ?>
- <?php if ($currentRepo): ?>
- <div class="repo-selector">
- <label>Repository:</label>
- <select onchange="window.location.href='?repo=' + encodeURIComponent(this.value)">
- <option value="">Select repository...</option>
- <?php foreach ($this->repositories as $r): ?>
- <option value="<?php echo htmlspecialchars($r['safe_name']); ?>"
- <?php echo $r['safe_name'] === $currentRepo['safe_name'] ? 'selected' : ''; ?>>
- <?php echo htmlspecialchars($r['name']); ?>
- </option>
- <?php endforeach; ?>
- </select>
- </div>
- <?php endif; ?>
- </nav>
+ <?php if ($currentRepo): ?>
+ <div class="repo-selector">
+ <label>Repository:</label>
+ <select onchange="window.location.href='?repo=' + encodeURIComponent(this.value)">
+ <option value="">Select repository...</option>
+ <?php foreach ($this->repositories as $r): ?>
+ <option value="<?php echo htmlspecialchars($r['safe_name']); ?>"
+ <?php echo $r['safe_name'] === $currentRepo['safe_name'] ? 'selected' : ''; ?>>
+ <?php echo htmlspecialchars($r['name']); ?>
+ </option>
+ <?php endforeach; ?>
+ </select>
+ </div>
+ <?php endif; ?>
+ </nav>
- <?php if ($currentRepo): ?>
- <div style="margin-top: 15px;">
- <span class="current-repo">Current: <strong><?php echo htmlspecialchars($currentRepo['name']); ?></strong></span>
- </div>
- <?php endif; ?>
- </header>
+ <?php if ($currentRepo): ?>
+ <div style="margin-top: 15px;">
+ <span class="current-repo">Current: <strong><?php echo htmlspecialchars($currentRepo['name']); ?></strong></span>
+ </div>
+ <?php endif; ?>
+ </header>
- <?php call_user_func($contentCallback); ?>
+ <?php call_user_func($contentCallback); ?>
- </div>
- </body>
- </html>
- <?php
- }
+ </div>
+ </body>
+ </html>
+ <?php
+ }
- protected function time_elapsed_string($timestamp) {
- if (!$timestamp) return 'never';
- $diff = time() - $timestamp;
- if ($diff < 5) return 'just now';
- $tokens = [31536000 => 'year', 2592000 => 'month', 604800 => 'week', 86400 => 'day', 3600 => 'hour', 60 => 'minute', 1 => 'second'];
- foreach ($tokens as $unit => $text) {
- if ($diff < $unit) continue;
- $num = floor($diff / $unit);
- return $num . ' ' . $text . (($num > 1) ? 's' : '') . ' ago';
- }
- return 'just now';
+ protected function time_elapsed_string($timestamp) {
+ if (!$timestamp) return 'never';
+ $diff = time() - $timestamp;
+ if ($diff < 5) return 'just now';
+ $tokens = [31536000 => 'year', 2592000 => 'month', 604800 => 'week', 86400 => 'day', 3600 => 'hour', 60 => 'minute', 1 => 'second'];
+ foreach ($tokens as $unit => $text) {
+ if ($diff < $unit) continue;
+ $num = floor($diff / $unit);
+ return $num . ' ' . $text . (($num > 1) ? 's' : '') . ' ago';
}
+ return 'just now';
+ }
}
class HomePage extends BasePage {
- public function render() {
- $this->renderLayout(function() {
- echo '<h2>Repositories</h2>';
- if (empty($this->repositories)) {
- echo '<div class="empty-state">No repositories found in ' . htmlspecialchars(Config::getReposPath()) . '</div>';
- return;
- }
- echo '<div class="repo-grid">';
- foreach ($this->repositories as $repo) {
- $this->renderRepoCard($repo);
- }
- echo '</div>';
- });
- }
+ public function render() {
+ $this->renderLayout(function() {
+ echo '<h2>Repositories</h2>';
+ if (empty($this->repositories)) {
+ echo '<div class="empty-state">No repositories found in ' . htmlspecialchars(Config::getReposPath()) . '</div>';
+ return;
+ }
+ echo '<div class="repo-grid">';
+ foreach ($this->repositories as $repo) {
+ $this->renderRepoCard($repo);
+ }
+ echo '</div>';
+ });
+ }
- private function renderRepoCard($repo) {
- $git = new Git($repo['path']);
- $main = $git->getMainBranch();
+ private function renderRepoCard($repo) {
+ $git = new Git($repo['path']);
+ $main = $git->getMainBranch();
- $stats = ['branches' => 0, 'tags' => 0];
- $git->eachBranch(function() use (&$stats) { $stats['branches']++; });
- $git->eachTag(function() use (&$stats) { $stats['tags']++; });
+ $stats = ['branches' => 0, 'tags' => 0];
+ $git->eachBranch(function() use (&$stats) { $stats['branches']++; });
+ $git->eachTag(function() use (&$stats) { $stats['tags']++; });
- echo '<a href="?repo=' . urlencode($repo['safe_name']) . '" class="repo-card">';
- echo '<h3>' . htmlspecialchars($repo['name']) . '</h3>';
- if ($main) echo '<p>Branch: ' . htmlspecialchars($main['name']) . '</p>';
- echo '<p>' . $stats['branches'] . ' branches, ' . $stats['tags'] . ' tags</p>';
+ echo '<a href="?repo=' . urlencode($repo['safe_name']) . '" class="repo-card">';
+ echo '<h3>' . htmlspecialchars($repo['name']) . '</h3>';
+ if ($main) echo '<p>Branch: ' . htmlspecialchars($main['name']) . '</p>';
+ echo '<p>' . $stats['branches'] . ' branches, ' . $stats['tags'] . ' tags</p>';
- // Fixed: Only attempt to fetch history if a main branch exists
- if ($main) {
- $git->history('HEAD', 1, function($c) {
- echo '<p style="margin-top: 8px; color: #58a6ff;">' . $this->time_elapsed_string($c->date) . '</p>';
- });
- }
- echo '</a>';
+ // Fixed: Only attempt to fetch history if a main branch exists
+ if ($main) {
+ $git->history('HEAD', 1, function($c) {
+ echo '<p style="margin-top: 8px; color: #58a6ff;">' . $this->time_elapsed_string($c->date) . '</p>';
+ });
}
+ echo '</a>';
+ }
}
class CommitsPage extends BasePage {
- private $currentRepo;
- private $git;
- private $hash;
+ private $currentRepo;
+ private $git;
+ private $hash;
- public function __construct($allRepos, $currentRepo, $git, $hash) {
- parent::__construct($allRepos);
- $this->currentRepo = $currentRepo;
- $this->git = $git;
- $this->hash = $hash;
- $this->title = $currentRepo['name'];
- }
+ public function __construct($allRepos, $currentRepo, $git, $hash) {
+ parent::__construct($allRepos);
+ $this->currentRepo = $currentRepo;
+ $this->git = $git;
+ $this->hash = $hash;
+ $this->title = $currentRepo['name'];
+ }
- public function render() {
- $this->renderLayout(function() {
- $main = $this->git->getMainBranch();
- if (!$main) {
- echo '<div class="empty-state"><h3>No branches</h3><p>Empty repository.</p></div>';
- return;
- }
+ public function render() {
+ $this->renderLayout(function() {
+ $main = $this->git->getMainBranch();
+ if (!$main) {
+ echo '<div class="empty-state"><h3>No branches</h3><p>Empty repository.</p></div>';
+ return;
+ }
- $this->renderBreadcrumbs();
- echo '<h2>Commit History <span class="branch-badge">' . htmlspecialchars($main['name']) . '</span></h2>';
- echo '<div class="commit-list">';
+ $this->renderBreadcrumbs();
+ echo '<h2>Commit History <span class="branch-badge">' . htmlspecialchars($main['name']) . '</span></h2>';
+ echo '<div class="commit-list">';
- $start = $this->hash ?: $main['hash'];
- $repoParam = '&repo=' . urlencode($this->currentRepo['safe_name']);
+ $start = $this->hash ?: $main['hash'];
+ $repoParam = '&repo=' . urlencode($this->currentRepo['safe_name']);
- $this->git->history($start, 100, function($commit) use ($repoParam) {
- $msg = htmlspecialchars(explode("\n", $commit->message)[0]);
- echo '<div class="commit-row">';
- echo '<a href="?action=commit&hash=' . $commit->sha . $repoParam . '" class="sha">' . substr($commit->sha, 0, 7) . '</a>';
- echo '<span class="message">' . $msg . '</span>';
- echo '<span class="meta">' . htmlspecialchars($commit->author) . ' &bull; ' . date('Y-m-d', $commit->date) . '</span>';
- echo '</div>';
- });
- echo '</div>';
- }, $this->currentRepo);
- }
+ $this->git->history($start, 100, function($commit) use ($repoParam) {
+ $msg = htmlspecialchars(explode("\n", $commit->message)[0]);
+ echo '<div class="commit-row">';
+ echo '<a href="?action=commit&hash=' . $commit->sha . $repoParam . '" class="sha">' . substr($commit->sha, 0, 7) . '</a>';
+ echo '<span class="message">' . $msg . '</span>';
+ echo '<span class="meta">' . htmlspecialchars($commit->author) . ' &bull; ' . date('Y-m-d', $commit->date) . '</span>';
+ echo '</div>';
+ });
+ echo '</div>';
+ }, $this->currentRepo);
+ }
- private function renderBreadcrumbs() {
- echo '<div class="breadcrumb">';
- echo '<a href="?">Repositories</a><span>/</span>';
- echo '<a href="?repo=' . urlencode($this->currentRepo['safe_name']) . '">' . htmlspecialchars($this->currentRepo['name']) . '</a><span>/</span>';
- echo '<span>Commits</span></div>';
- }
+ private function renderBreadcrumbs() {
+ echo '<div class="breadcrumb">';
+ echo '<a href="?">Repositories</a><span>/</span>';
+ echo '<a href="?repo=' . urlencode($this->currentRepo['safe_name']) . '">' . htmlspecialchars($this->currentRepo['name']) . '</a><span>/</span>';
+ echo '<span>Commits</span></div>';
+ }
}
class FilePage extends BasePage {
- private $currentRepo;
- private $git;
- private $hash;
+ private $currentRepo;
+ private $git;
+ private $hash;
- public function __construct($allRepos, $currentRepo, $git, $hash) {
- parent::__construct($allRepos);
- $this->currentRepo = $currentRepo;
- $this->git = $git;
- $this->hash = $hash;
- $this->title = $currentRepo['name'];
- }
+ public function __construct($allRepos, $currentRepo, $git, $hash) {
+ parent::__construct($allRepos);
+ $this->currentRepo = $currentRepo;
+ $this->git = $git;
+ $this->hash = $hash;
+ $this->title = $currentRepo['name'];
+ }
- public function render() {
- $this->renderLayout(function() {
- $main = $this->git->getMainBranch();
- if (!$main) {
- echo '<div class="empty-state"><h3>No branches</h3></div>';
- return;
- }
+ public function render() {
+ $this->renderLayout(function() {
+ $main = $this->git->getMainBranch();
+ if (!$main) {
+ echo '<div class="empty-state"><h3>No branches</h3></div>';
+ return;
+ }
- $target = $this->hash ?: $main['hash'];
- $entries = [];
- $this->git->walk($target, function($e) use (&$entries) {
- $entries[] = (array)$e;
- });
+ $target = $this->hash ?: $main['hash'];
+ $entries = [];
+ $this->git->walk($target, function($e) use (&$entries) {
+ $entries[] = (array)$e;
+ });
- if (!empty($entries)) {
- $this->renderTree($main, $target, $entries);
- } else {
- $this->renderBlob($target);
- }
- }, $this->currentRepo);
- }
+ if (!empty($entries)) {
+ $this->renderTree($main, $target, $entries);
+ } else {
+ $this->renderBlob($target);
+ }
+ }, $this->currentRepo);
+ }
- private function renderTree($main, $targetHash, $entries) {
- $this->renderBreadcrumbs($targetHash, 'Tree');
- echo '<h2>' . htmlspecialchars($this->currentRepo['name']) . ' <span class="branch-badge">' . htmlspecialchars($main['name']) . '</span></h2>';
+ private function renderTree($main, $targetHash, $entries) {
+ $this->renderBreadcrumbs($targetHash, 'Tree');
+ echo '<h2>' . htmlspecialchars($this->currentRepo['name']) . ' <span class="branch-badge">' . htmlspecialchars($main['name']) . '</span></h2>';
- usort($entries, function($a, $b) {
- return ($b['isDir'] <=> $a['isDir']) ?: strcasecmp($a['name'], $b['name']);
- });
+ usort($entries, function($a, $b) {
+ return ($b['isDir'] <=> $a['isDir']) ?: strcasecmp($a['name'], $b['name']);
+ });
- echo '<div class="file-list">';
- foreach ($entries as $e) {
- $icon = $e['isDir'] ? '[dir]' : '[file]';
- $url = '?repo=' . urlencode($this->currentRepo['safe_name']) . '&hash=' . $e['sha'];
- echo '<a href="' . $url . '" class="file-item">';
- echo '<span class="file-mode">' . $e['mode'] . '</span>';
- echo '<span class="file-name"><span class="' . ($e['isDir'] ? 'dir' : 'file') . '-icon">' . $icon . '</span> ' . htmlspecialchars($e['name']) . '</span>';
- echo '</a>';
- }
- echo '</div>';
+ echo '<div class="file-list">';
+ foreach ($entries as $e) {
+ $icon = $e['isDir'] ? '[dir]' : '[file]';
+ $url = '?repo=' . urlencode($this->currentRepo['safe_name']) . '&hash=' . $e['sha'];
+ echo '<a href="' . $url . '" class="file-item">';
+ echo '<span class="file-mode">' . $e['mode'] . '</span>';
+ echo '<span class="file-name"><span class="' . ($e['isDir'] ? 'dir' : 'file') . '-icon">' . $icon . '</span> ' . htmlspecialchars($e['name']) . '</span>';
+ echo '</a>';
}
+ echo '</div>';
+ }
- private function renderBlob($targetHash) {
- $content = '';
- $this->git->stream($targetHash, function($d) use (&$content) { $content = $d; });
+ private function renderBlob($targetHash) {
+ $content = '';
+ $this->git->stream($targetHash, function($d) use (&$content) { $content = $d; });
- $this->renderBreadcrumbs($targetHash, 'File');
- echo '<h2>' . substr($targetHash, 0, 7) . '</h2>';
- echo '<div class="blob-content"><div class="blob-code">' . htmlspecialchars($content) . '</div></div>';
- }
+ $this->renderBreadcrumbs($targetHash, 'File');
+ echo '<h2>' . substr($targetHash, 0, 7) . '</h2>';
+ echo '<div class="blob-content"><div class="blob-code">' . htmlspecialchars($content) . '</div></div>';
+ }
- private function renderBreadcrumbs($hash, $type) {
- echo '<div class="breadcrumb">';
- echo '<a href="?">Repositories</a><span>/</span>';
- echo '<a href="?repo=' . urlencode($this->currentRepo['safe_name']) . '">' . htmlspecialchars($this->currentRepo['name']) . '</a>';
- if ($this->hash) echo '<span>/</span><span>' . $type . ' ' . substr($hash, 0, 7) . '</span>';
- echo '</div>';
- }
+ private function renderBreadcrumbs($hash, $type) {
+ echo '<div class="breadcrumb">';
+ echo '<a href="?">Repositories</a><span>/</span>';
+ echo '<a href="?repo=' . urlencode($this->currentRepo['safe_name']) . '">' . htmlspecialchars($this->currentRepo['name']) . '</a>';
+ if ($this->hash) echo '<span>/</span><span>' . $type . ' ' . substr($hash, 0, 7) . '</span>';
+ echo '</div>';
+ }
}
Delta235 lines added, 235 lines removed