Dave Jarvis' Repositories

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

Uses media types

AuthorDave Jarvis <email>
Date2026-02-08 20:34:24 GMT-0800
Commit7bc6c6a0d700ecf248dc97229691cb2db2a0eebf
Parent25ac3e2
new/File.php
private string $mode;
private int $timestamp;
+ private int $size;
private bool $isDir;
- public function __construct(string $name, string $sha, string $mode, int $timestamp = 0) {
+ public function __construct(string $name, string $sha, string $mode, int $timestamp = 0, int $size = 0) {
$this->name = $name;
$this->sha = $sha;
$this->mode = $mode;
$this->timestamp = $timestamp;
+ $this->size = $size;
$this->isDir = ($mode === '40000' || $mode === '040000');
}
public function render(FileRenderer $renderer): void {
- $renderer->render(
+ $renderer->renderFileItem(
$this->name,
$this->sha,
$this->mode,
+ $this->getIconClass(),
$this->getTimeElapsed(),
- $this->isDir,
- fn(string $type) => $this->isType($type),
- fn(string $cat) => $this->isCategory($cat)
+ $this->isDir ? '' : $this->getFormattedSize()
);
}
- /**
- * Checks if the file matches a specific Media Type string.
- */
+ private function getIconClass(): string {
+ if ($this->isDir) return 'fa-folder';
+
+ return match (true) {
+ $this->isType('application/pdf') => 'fa-file-pdf',
+ $this->isCategory(MediaTypeSniffer::CAT_ARCHIVE) => 'fa-file-archive',
+ $this->isCategory(MediaTypeSniffer::CAT_IMAGE) => 'fa-file-image',
+ $this->isCategory(MediaTypeSniffer::CAT_AUDIO) => 'fa-file-audio',
+ $this->isCategory(MediaTypeSniffer::CAT_VIDEO) => 'fa-file-video',
+ $this->isCategory(MediaTypeSniffer::CAT_TEXT) => 'fa-file-code',
+ default => 'fa-file',
+ };
+ }
+
+ private function getFormattedSize(): string {
+ if ($this->size <= 0) return '0 B';
+ $units = ['B', 'KB', 'MB', 'GB'];
+ $i = (int)floor(log($this->size, 1024));
+ return round($this->size / pow(1024, $i), 1) . ' ' . $units[$i];
+ }
+
public function isType(string $type): bool {
- $data = $this->getSniffBuffer();
- return str_contains(MediaTypeSniffer::isMediaType($data, $this->name), $type);
+ return str_contains(MediaTypeSniffer::isMediaType($this->getSniffBuffer(), $this->name), $type);
}
- /**
- * Checks if the file belongs to a specific category (image, video, etc).
- */
public function isCategory(string $category): bool {
- $data = $this->getSniffBuffer();
- return MediaTypeSniffer::isCategory($data, $this->name) === $category;
+ return MediaTypeSniffer::isCategory($this->getSniffBuffer(), $this->name) === $category;
}
- /**
- * Reads the first 12 bytes required for signature sniffing.
- */
private function getSniffBuffer(): string {
- if ($this->isDir || !file_exists($this->name)) {
- return '';
- }
-
+ if ($this->isDir || !file_exists($this->name)) return '';
$handle = @fopen($this->name, 'rb');
- if (!$handle) {
- return '';
- }
-
+ if (!$handle) return '';
$read = fread($handle, 12);
fclose($handle);
-
return ($read !== false) ? $read : '';
}
private function getTimeElapsed(): string {
if (!$this->timestamp) return '';
-
$diff = time() - $this->timestamp;
if ($diff < 5) return 'just now';
-
$tokens = [
- 31536000 => 'year',
- 2592000 => 'month',
- 604800 => 'week',
- 86400 => 'day',
- 3600 => 'hour',
- 60 => 'minute',
- 1 => 'second'
+ 31536000 => 'year', 2592000 => 'month', 604800 => 'week',
+ 86400 => 'day', 3600 => 'hour', 60 => 'minute', 1 => 'second'
];
-
foreach ($tokens as $unit => $text) {
if ($diff < $unit) continue;
new/FileRenderer.php
<?php
interface FileRenderer {
- public function render(
+ public function renderFileItem(
string $name,
string $sha,
string $mode,
+ string $iconClass,
string $time,
- bool $isDir,
- callable $isMediaType
+ string $size = ''
): void;
}
}
- public function render(
+ public function renderFileItem(
string $name,
string $sha,
string $mode,
+ string $iconClass,
string $time,
- bool $isDir,
- callable $isMediaType
+ string $size = ''
): void {
- $iconClass = $this->getIconClass($isDir, $isMediaType);
-
$url = '?repo=' . urlencode($this->repoSafeName) . '&hash=' . $sha;
echo '<a href="' . $url . '" class="file-item">';
echo '<span class="file-mode">' . $mode . '</span>';
echo '<span class="file-name">';
-
- // Render the FontAwesome Icon
echo '<i class="fas ' . $iconClass . '" style="width: 20px; text-align: center; margin-right: 5px; color: #7a828e;"></i>';
-
echo htmlspecialchars($name);
echo '</span>';
+
+ if ($size) {
+ echo '<span class="file-size" style="color: #8b949e; font-size: 0.8em; margin-left: 10px;">' . $size . '</span>';
+ }
if ($time) {
echo '<span class="file-date" style="color: #8b949e; font-size: 0.8em; margin-left: auto;">' . $time . '</span>';
}
echo '</a>';
- }
-
- /**
- * Maps media types to FontAwesome 6 icon classes.
- */
- private function getIconClass(bool $isDir, callable $isMediaType): string {
- if ($isDir) {
- return 'fa-folder';
- }
-
- // Explicit Mime Matches
- if ($isMediaType('application/pdf')) return 'fa-file-pdf';
-
- // Archives
- $archives = [
- 'application/zip', 'application/x-tar', 'application/gzip',
- 'application/x-bzip2', 'application/vnd.rar', 'application/x-7z-compressed'
- ];
- foreach ($archives as $archive) {
- if ($isMediaType($archive)) return 'fa-file-archive';
- }
-
- // Broad Categories (based on prefix)
- if ($isMediaType('image/')) return 'fa-file-image';
- if ($isMediaType('audio/')) return 'fa-file-audio';
- if ($isMediaType('video/')) return 'fa-file-video';
-
- // Code / Text
- if ($isMediaType('text/')) return 'fa-file-code';
-
- // Common Application Types behaving like code/text
- if (
- $isMediaType('javascript') ||
- $isMediaType('json') ||
- $isMediaType('xml') ||
- $isMediaType('php') ||
- $isMediaType('sh')
- ) {
- return 'fa-file-code';
- }
-
- // Default fallback
- return 'fa-file';
}
}
new/MediaTypeSniffer.php
// Categories
- private const CAT_IMAGE = 'image';
- private const CAT_VIDEO = 'video';
- private const CAT_AUDIO = 'audio';
- private const CAT_TEXT = 'text';
- private const CAT_ARCHIVE = 'archive';
- private const CAT_APP = 'application';
- private const CAT_BINARY = 'binary';
+ public const CAT_IMAGE = 'image';
+ public const CAT_VIDEO = 'video';
+ public const CAT_AUDIO = 'audio';
+ public const CAT_TEXT = 'text';
+ public const CAT_ARCHIVE = 'archive';
+ public const CAT_APP = 'application';
+ public const CAT_BINARY = 'binary';
private const FORMATS = [
/**
- * Returns [Category, Mime] or null if not found.
+ * Internal helper to resolve category and mime type.
+ * Guaranteed to return a non-empty array.
*/
- private static function sniff( $data ): ?array {
- if( !empty( $data ) ) {
- $dataLength = strlen( $data );
- $maxScan = min( $dataLength, self::BUFFER );
- $sourceBytes = [];
+ private static function getTypeInfo( string $data, string $filePath ): array {
+ $info = self::sniff( $data );
- for( $i = 0; $i < $maxScan; $i++ ) {
- $sourceBytes[$i] = ord( $data[$i] ) & 0xFF;
- }
+ if ( empty( $info ) && !empty( $filePath ) ) {
+ $info = self::getInfoByExtension( $filePath );
+ }
- foreach( self::FORMATS as [$category, $pattern, $type] ) {
- $patternLength = count( $pattern );
- if( $patternLength > $dataLength ) continue;
+ return !empty( $info ) ? $info : [self::CAT_BINARY, 'application/octet-stream'];
+ }
- $matches = true;
- for( $i = 0; $i < $patternLength; $i++ ) {
- if( $pattern[$i] !== self::ANY && $pattern[$i] !== $sourceBytes[$i] ) {
- $matches = false;
- break;
- }
- }
+ private static function sniff( string $data ): array {
+ if( empty( $data ) ) return [];
- if( $matches ) {
- return [$category, $type];
+ $dataLength = strlen( $data );
+ $maxScan = min( $dataLength, self::BUFFER );
+ $sourceBytes = [];
+
+ for( $i = 0; $i < $maxScan; $i++ ) {
+ $sourceBytes[$i] = ord( $data[$i] ) & 0xFF;
+ }
+
+ foreach( self::FORMATS as [$category, $pattern, $type] ) {
+ $patternLength = count( $pattern );
+
+ if( $patternLength > $dataLength ) continue;
+
+ $matches = true;
+
+ for( $i = 0; $i < $patternLength; $i++ ) {
+ if( $pattern[$i] !== self::ANY && $pattern[$i] !== $sourceBytes[$i] ) {
+ $matches = false;
+ break;
}
}
+
+ if( $matches ) return [$category, $type];
}
- return null;
+
+ return [];
}
- private static function getInfoByExtension( $filePath ): array {
+ private static function getInfoByExtension( string $filePath ): array {
$extension = strtolower( pathinfo( $filePath, PATHINFO_EXTENSION ) );
return self::EXTENSION_MAP[$extension] ?? [self::CAT_BINARY, 'application/octet-stream'];
}
-
- public static function isMediaType( $data, $filePath = '' ): string {
- $info = self::sniff( $data );
-
- if ( $info === null && !empty( $filePath ) ) {
- $info = self::getInfoByExtension( $filePath );
- }
- return $info ? $info[1] : 'application/octet-stream';
+ public static function isMediaType( string $data, string $filePath = '' ): string {
+ return self::getTypeInfo( $data, $filePath )[1];
}
-
- public static function isCategory( $data, $filePath = '' ): string {
- $info = self::sniff( $data );
- if ( $info === null && !empty( $filePath ) ) {
- $info = self::getInfoByExtension( $filePath );
- }
+ public static function isCategory( string $data, string $filePath = '' ): string {
+ return self::getTypeInfo( $data, $filePath )[0];
+ }
- return $info ? $info[0] : self::CAT_BINARY;
+ public static function isBinary( string $data, string $filePath = '' ): bool {
+ return self::isCategory( $data, $filePath ) !== self::CAT_TEXT;
}
}
Delta92 lines added, 136 lines removed, 44-line decrease