Dave Jarvis' Repositories

git clone https://repo.autonoma.ca/repo/treetrek.git
<?php
class FilePage extends BasePage {
  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 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( $file ) use ( &$entries ) {
        $entries[] = $file;
      } );

      if( !empty( $entries ) ) {
        $this->renderTree( $main, $target, $entries );
      } else {
        $this->renderBlob( $target );
      }
    }, $this->currentRepo );
  }

  private function renderTree( $main, $targetHash, $entries ) {
    $path = $_GET['name'] ?? '';

    $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 $a->compare( $b );
    } );

    echo '<div class="file-list">';
    $renderer = new HtmlFileRenderer( $this->currentRepo['safe_name'], $path );

    foreach($entries as $file) {
      $file->render( $renderer );
    }

    echo '</div>';
  }

  private function renderBlob( $targetHash ) {
    $repoParam = '&repo=' . urlencode( $this->currentRepo['safe_name'] );
    $size      = $this->git->getObjectSize( $targetHash );
    $buffer    = '';

    $this->git->stream( $targetHash, function( $d ) use ( &$buffer ) {
      if( strlen( $buffer ) < 12 ) $buffer .= $d;
    } );

    $filename  = $_GET['name'] ?? '';
    $category  = MediaTypeSniffer::isCategory( $buffer, $filename );
    $mediaType = MediaTypeSniffer::isMediaType( $buffer, $filename );

    $this->renderBreadcrumbs( $targetHash, 'File' );

    if( $size === 0 ) {
      $this->renderDownloadState( $targetHash, "This file is empty." );
      return;
    }

    $rawUrl = '?action=raw&hash=' . $targetHash . $repoParam . '&name=' . urlencode( $filename );

    if( $category === MediaTypeSniffer::CAT_IMAGE ) {
      echo '<div class="blob-content blob-content-image"><img src="' . $rawUrl . '"></div>';
    } elseif( $category === MediaTypeSniffer::CAT_VIDEO ) {
      echo '<div class="blob-content blob-content-video"><video controls><source src="' . $rawUrl . '" type="' . $mediaType . '"></video></div>';
    } elseif( $category === MediaTypeSniffer::CAT_AUDIO ) {
      echo '<div class="blob-content blob-content-audio"><audio controls><source src="' . $rawUrl . '" type="' . $mediaType . '"></audio></div>';
    } elseif( $category === MediaTypeSniffer::CAT_TEXT ) {
      if( $size > 524288 ) {
        $this->renderDownloadState( $targetHash, "File is too large to display (" . $this->formatSize( $size ) . ")." );
      } else {
        $content = '';
        $this->git->stream( $targetHash, function( $d ) use ( &$content ) { $content .= $d; } );
        echo '<div class="blob-content"><pre class="blob-code">' . htmlspecialchars( $content ) . '</pre></div>';
      }
    } else {
      $this->renderDownloadState( $targetHash, "This is a binary file." );
    }
  }

  private function renderDownloadState( $hash, $reason ) {
    $url = '?action=raw&hash=' . $hash . '&repo=' . urlencode( $this->currentRepo['safe_name'] );

    echo '<div class="empty-state download-state">';
    echo   '<p>' . htmlspecialchars( $reason ) . '</p>';
    echo   '<a href="' . $url . '" class="btn-download">Download Raw File</a>';
    echo '</div>';
  }

  private function formatSize( $size ) {
    if( $size <= 0 ) return '0 B';

    $units = ['B', 'KB', 'MB', 'GB'];
    $i     = (int)floor( log( $size, 1024 ) );

    return round( $size / pow( 1024, $i ), 1 ) . ' ' . $units[$i];
  }

  private function renderBreadcrumbs( $hash, $type ) {
    $repoUrl = '?repo=' . urlencode( $this->currentRepo['safe_name'] );
    $path    = $_GET['name'] ?? '';

    $crumbs = [
      '<a href="?">Repositories</a>',
      '<a href="' . $repoUrl . '">' . htmlspecialchars( $this->currentRepo['name'] ) . '</a>'
    ];

    if ( $path ) {
      $parts = explode( '/', trim( $path, '/' ) );
      $acc   = '';
      foreach ( $parts as $idx => $part ) {
        $acc .= ( $idx === 0 ? '' : '/' ) . $part;

        // The last segment isn't a link
        if ( $idx === count( $parts ) - 1 ) {
          $crumbs[] = htmlspecialchars( $part );
        } else {
          $crumbs[] = '<a href="' . $repoUrl . '&name=' . urlencode( $acc ) . '">' .
                      htmlspecialchars( $part ) . '</a>';
        }
      }
    } elseif ( $this->hash ) {
      $crumbs[] = $type . ' ' . substr( $hash, 0, 7 );
    }

    echo '<div class="breadcrumb">' . implode( ' / ', $crumbs ) . '</div>';
  }
}