Dave Jarvis' Repositories

git clone https://repo.autonoma.ca/repo/treetrek.git
<?php
require_once __DIR__ . '/BasePage.php';
require_once __DIR__ . '/../render/HtmlFileRenderer.php';

class FilePage extends BasePage {
  private $currentRepo;
  private $git;
  private $hash;

  public function __construct(
    array $repositories,
    array $currentRepo,
    Git $git,
    string $hash = ''
  ) {
    parent::__construct( $repositories );
    $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>';
      } else {
        $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->renderListEntry( $renderer );
    }

    echo '</div>';
  }

  private function renderBlob( $targetHash ) {
    $repoParam = '&repo=' . urlencode( $this->currentRepo['safe_name'] );
    $filename = $_GET['name'] ?? '';
    $file = $this->git->readFile( $targetHash, $filename );
    $size = $this->git->getObjectSize( $targetHash );
    $renderer = new HtmlFileRenderer( $this->currentRepo['safe_name'] );

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

    if( $size === 0 ) {
      $this->renderDownloadState( $targetHash, "This file is empty." );
    } else {
      $rawUrl = '?action=raw&hash=' . $targetHash . $repoParam .
                '&name=' . urlencode( $filename );

      if( !$file->renderMedia( $renderer, $rawUrl ) ) {
        if( $file->isText() ) {
          if( $size > 524288 ) {
            ob_start();
            $file->renderSize( $renderer );
            $sizeStr = ob_get_clean();

            $this->renderDownloadState(
              $targetHash,
              "File is too large to display ($sizeStr)."
            );
          } else {
            $content = '';

            $this->git->stream( $targetHash, function( $d ) use ( &$content ) {
              $content .= $d;
            } );

            echo '<div class="blob-content"><pre class="blob-code">' .
                 $file->highlight( $renderer, $content ) . '</pre></div>';
          }
        } else {
          $this->renderDownloadState( $targetHash, "This is a binary file." );
        }
      }
    }
  }

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

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

  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;

        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>';
  }
}