ProjectRouter.php

<?php

namespace Taeluf\ProjectViewer;

class ProjectRouter extends \Lia\Compo {

    const PROJECT_NOT_ALLOWED = 'PROJECT_NOT_ALLOWED';
    const FILE_NOT_FOUND = 'FILE_NOT_FOUND';

    protected $active;
    protected $vendors;
    protected $webhookUrl = '/project-viewer/update-project/';

    public $baseUrl;
    protected $srcDir;

    /**
     * Pass an options array to the package like:
     * ```php
     * [
     *      'projects'=>
     *      [
     *          'VendorName'=>['url'=>'/base-url/', 'dir'=>'/absolute/path/']
     *          Currently, we only support one vendor.
     *      ],
     *      //exclude 'permit' to allow all projects or pass 'permit'=>['VendorName/*'] to permit all projects under a given vendor or 'permit'=>['*'] (allow all projects)
     *      'this-is-not-active-yet-permit'=>[
     *          'VendorName/ProjectDirectory'
     *          'VendorName/Project2Dir'
     *      ]
     * ]
     * ```
     * 
     * @export(Usage.SetupProjects)
     */
    public function __construct($package){
        parent::__construct($package);

        $this->projectDirs = $package->get('projects');
        $this->baseUrl = $package->get('baseUrl');

        // /base/vendor/project(-src)?(:branch_name)?/DocFile/?
        $this->lia->addRoute($this->baseUrl, function(){echo "zeep";exit;});
        var_dump($this->baseUrl);

        $this->lia->append('lia:route.routers', [$this, 'newRoute']);
        $this->srcDir = array_values((array)$this->vendors)[0]['dir'];
        $vendorName = array_keys((array)$this->vendors)[0];
        $serverDir = array_values((array)$this->vendors)[0]['dir'];
        $this->vendorDir = $serverDir.'/repos/'.$vendorName.'/';
    }
    public function newRoute($request){
        // see route() below for actual code to use maybe
        //
        // I want to parse the url into its relevant pieces
        // Ultimately, I just have a pattern that the url matches
        //
        // Starts with base url
        // then... vendor? then project name?
        // then :branch_name (unless we're on default branch)
            // If it ends with a `/` then its a directory, not a file
            // Maybe just deliver with .md extension
            // 
        //
        //
    }


    public function isDocsRequest(){
        if ($this->active->mode=='docs')return true;
        return false;
    }
    public function isRequested($url){
        if ($this->urlRelToBase($url)===false)return false;
        return true;
    }
    public function handleProjectError($errorType, $msg){
        switch ($errorType){
            case self::PROJECT_NOT_ALLOWED:
                echo "Project Not allowed\n<br>\n{$msg}";
                break;
            case self::FILE_NOT_FOUND:
                echo "You asked for file that does not seem to exist!";
                break;
        }
    }
    public function isProjectAllowed($projectName) {
        if (is_dir($path=$this->vendorDir.$projectName))return true;
        return false;
    }
    /**
     * Returns false if base url is not present in $url  
     * otherwise returns the url with the base url removed. There is no leading slash
     */
    public function urlRelToBase($url){
        if (substr($url,0,$len=strlen($this->baseUrl))!==$this->baseUrl)return false;
        $rel = substr($url,$len);
        if (strlen($rel)>0&&$rel[0]=='/')$rel = substr($rel,1);

        return $rel;
    }
    
    public function getActiveProject(){
        return $this->active;
    }
    

    public function urlFor($project, $relUrl){
        $url = $this->baseUrl.'/'.$project->name.'/'.$relUrl;
        $url = str_replace(['///','//'],'/',$url);
        return $url;
    }
    // public function urlForFilePath($project, $filePath){
    //     $filePath = substr($filePath,strlen($this->safeDir));
    //     $parts = explode('/',$filePath);
    //     $relUrl = '/'.implode('/',array_slice($parts,4));
    //     $url = $this->baseUrl.'/'.$project->name.'/'.$relUrl;
    //     $url = str_replace(['///','//'],'/',$url);
    //     $url = str_replace(' ', '%20', $url);
    //     return $url;
    // }

    public function urlForFilePath($path){
        $srcDir = $this->srcDir;
        
        $path = substr($path,strlen($srcDir));
        $base = $this->baseUrl;
        $base .= $this->active->project->urlForVendorFile($this->active->branch, $this->active->mode, $path);
        $base = str_replace(' ', '%20', $base);
        $base = str_replace('//','/',$base);
        return $base;
    }
    public function isActive($relUrl){
        $url = str_replace(['///','//'],'/',$relUrl.'/');
        $requested = $this->getFileUrl($this->requestUrl);
        if (substr($requested,0,strlen($url))==$url){
            return true;
        }
        return false;
    }

    public function getFileUrl($url){
        $relPath = substr($url,strlen($this->baseUrl));
        if (strlen($relPath)==0){
            return null;
        }
        $parts = explode('/',$relPath);
        $projectName = array_shift($parts);
        return '/'.implode('/',$parts);
    }

    public function getProject($url){
        $relPath = substr($url,strlen($this->baseUrl));

        if (strlen($relPath)==0){
            return null;
        }
        $parts = explode('/',$relPath);
        $projectName = array_shift($parts);


        foreach ($this->projects as $project){
            if (strtolower($projectName)==strtolower($project->name)){
                return $project;
            }
        }
        return null;
    }

    public function projectUrl($project){
        $base = $this->baseUrl.'/';
        $base .= $project->urlForRequest($this->active->branch,$this->active->mode,'');
        return str_replace('//','/',$base);
    }
    public function defaultBranchUrl($project){
        $path = $this->baseUrl.'/'.$project->name.'/';
        return str_replace('//','/',$path);
    }

    protected function handleWebhook(){
        try {
            $rootDir = $this->dir;
            $wh = new \tlf\GithubWebhook(
                $_POST['payload'], 
                $this->webhookKey,
                $rootDir.'/work/',
                $rootDir.'/repos/',
                $this->repos,
            );
            $wh->outputCurrentBranch();
            ob_start();

        } catch (\Exception $e){
            echo 'we caught an exception';
        } catch (\Throwable $e){
            echo 'we caught a throwable';
        } catch (\Error $e){
            echo 'We caught an error';
        } finally {
            //so I'm not accidentally passing anything sensitive back to github, such as my username on my server!
            ob_get_clean();
        }

        exit;
    }

    static public function packageDir(){
        return dirname(__DIR__);
    }


    public function getProjects(){
        if ($this->projects!=null)return $this->projects;
        $list = [];
        foreach (scandir($this->srcDir) as $projDir){
            if ($projDir=='.'||$projDir=='..'||!is_dir($this->srcDir.'/'.$projDir))continue;
            $list[]  = $project = new Project($this->srcDir.'/'.$projDir);
        }
        $this->projects = $list;
        
        return $list;
    }

    public function curBranch(){
        return $this->active->branch;
    }
    public function filesFor($url){
        return $this->active->project->filesForRequest($this->active->branch, $this->active->mode, $url);
    }
    public function fileFor($url){
        $url = str_replace('%20', ' ',$url);
        // var_dump("<br>\n--".$url."\n<br>");
        // exit;
        return $this->active->project->fileForRequest($this->active->branch, $this->active->mode, $url);
    }
    public function getBranchUrl($branchName){
        $route = $this->active->project->urlForRequest($branchName, $this->active->mode, '');
        $base = $this->baseUrl;
        return $base.$route;
    }
    public function getFullUrl($rel){
        $route = $this->active->project->urlForRequest($this->active->branch, $this->active->mode, $rel);
        $base = $this->baseUrl;
        return $base.$route;
    }
    public function docsUrlFor($rel){
        $route = $this->active->project->urlForRequest($this->active->branch, 'docs', $rel);
        $base = $this->baseUrl;
        return $base.$route;
    }
    public function srcUrlFor($rel){
        $route = $this->active->project->urlForRequest($this->active->branch, 'src', $rel);
        $base = $this->baseUrl;
        return $base.$route;
    }







    public function isWebhookRequest($url){
        return false;
    }

    public function requestedVendors($url){
        //@TODO cache vendors so I don't create the same vendor over and over
        $list = [];
        foreach ($this->vendors as $name=>$vData){
            $len = strlen($vData['url']);
            $urlCheck = substr($url,0,$len);
            if ($vData['url']!=$urlCheck)continue;
            $list[] = new Vendor($name,$vData);
        }
        if ($list===[])return false;
        return $list;
    }

    public function requestedProjectAndVendor($url){
        $projects = [];
        $matchedVendor = false;
        foreach ($this->requestedVendors($url) as $vendor){
            $p = $vendor->projectForUrl($url);
            if ($p==false)continue;
            $matchedVendor = $vendor;
            $projects[] = $p;
        }
        if (count($projects)>1){
            throw new \Exception("Multiple projects were found for this page...");
        }
        if ($projects==[])return false;
        return [
            'project'=>$projects[0],
            'vendor'=>$vendor
        ];
    }

    public function deliver(\Lia\Obj\Route $route, \Lia\Obj\Response $response){
        $this->lia->view('Docu/Resources').'';
        $response->content = "OKAY MAYBE THIS IS GOOD NOW!?";
        $url = $route->url();


        $this->setActiveProject($url);
        $vendors=$this->requestedVendors($url);


        if ($this->isWebhookRequest($url)){
            echo "webhook request";
            exit;
            return;
        } else if ($pair=$this->requestedProjectAndVendor($url)) {
            $url = $this->urlRelToBase($url);
            if ($url[0]=='/')$url = substr($url,1);
            $url = substr($url, strpos($url, '/'));
            if ($url[0]=='/')$url = substr($url,1);
            // var_dump($url);
            // exit;
            // var_dump(substr($this->urlRelToBase($url), strpos('/', $ur)));
            // exit;
            $response->content = $this->lia->view('Docu/ProjectLayout', ['docu'=>$this, 'project'=>$pair['project'],'vendor'=>$pair['vendor'], 
                'url'=>$url, 'router'=>$this]);
        } else if ($vendors=$this->requestedVendors($url)){
            foreach ($vendors as $vendor){
                $response->content .= $this->lia->view('Docu/ProjectSelector', ['vendor'=>$vendor]);
            }
        } else {
            echo "Something went wrong loading the projects...";
        }

        return;
    }

    public function route(\Lia\Obj\Request $request){
        $url = $request->url();

        ob_start();
        $this->lia->view('Docu/Resources');


        if (!$this->isRequested($url)){
            return false;
        }

        $this->requestUrl = $url;

        $route = new \Lia\Obj\Route(
            [
                'url' => $request->url(),
                'method'=>$request->method(),
                'target'=>[$this, 'deliver']
            ]
         );

        return [$route];
    }

    protected function setActiveProject($url){
        // \Lia\JSExt\Autowire::setup($this->lia);
        $relBaseUrl = $this->urlRelToBase($url);
        $parts = explode('/',$relBaseUrl);
        $details = array_shift($parts);
        $relPath = implode('/',$parts);
        $splitBranch = explode(':',$details,2);
        $project = $splitBranch[0];
        $mode = 'docs';
        if (substr($project,-4)=='-src'){
            $project = substr($project,0,-4);
            $mode = 'src';
        }

        $projectName = $project;
        if (!$this->isProjectAllowed($projectName)){
            $this->handleProjectError(self::PROJECT_NOT_ALLOWED, "Project '{$projectName}' error...");
            return;
        }
        $project = new Project($this->vendorDir.'/'.$projectName, 'idk');
        $branch = $splitBranch[1] ?? $project->defaultBranch();
        $this->active = (object)[
            'project'=>$project,
            'branch'=>$branch,
            'request'=>$relPath,
            'mode'=>$mode,
            'file'=>$project->fileForRequest($branch,$mode,$relPath)
        ];
    }
}