<?php
namespace Tlf;
class Provi2 {
/**
* the state of the current request
*/
public $state = [];
/**
* the directory containing all your git repos
*/
public string $dir;
/**
* all urls for documentation will begin with this url. This url will show a list of projects
*/
public string $base_url;
/**
* the directory where all the views are found
*/
public string $view_dir;
public string $gitlab_hook_path;
/** the token sent with the webhook request */
public ?string $secret_token = null;
public string $git_icon_path;
/** dir to store temporary info in */
public string $cache_dir;
/**
* `json_decode`d project settings file
*/
public array $settings;
/**
* the name of the vendor these projects belong to
*/
public string $name;
public array $public_file_params = [];
/**
* Provi2 doesn't seem to have a connection to liaison, and I don't want to risk disrupting any of the mess that is this package, so here's a prop to avoid that. This is used by some new features (setting seo title!).
*/
public ?\Lia $actual_liaison = null;
/**
* @param $project_dir the directory containing all your git repos
* @param $base_url all urls for documentation will begin with this url. This url will show a list of projects
* @param $vendor_name the name of the vendor these projects belong to
*/
public function __construct(string $project_dir, string $base_url, string $vendor_name, ?\Lia $lia = null){
$this->dir = $project_dir;
$this->base_url = $base_url;
$this->view_dir = dirname(__DIR__).'/view/';
$this->name = $vendor_name;
$this->gitlab_hook_path = dirname(__DIR__).'/public/gitlab-hook.php';
$this->git_icon_path = dirname(__DIR__).'/public/git-icon-1788c-sm.png';
$this->cache_dir = dirname(__DIR__).'/cache/';
$this->settings = json_decode(file_get_contents($this->settings_file()),true);
$this->actual_liaison = $lia;
}
public function url($rel_path=''){
return str_replace(['///','//'],'/',$this->base_url.'/'.$rel_path);
}
/**
* Get a response to a request
* @return false on failure or on success an array containing keys `css`, `js`, and `content`, where `content` is a string, and the other two are arrays of `key=>value` where `key` is url & `value` is file path
*/
public function route(){
$url = $_SERVER['REQUEST_URI'];
$pos = strpos($url, '?');
if ($pos!==false)$url = substr($url,0, $pos);
// if (substr($url,0,$len=strlen($this->base_url))!==$this->base_url)return false;
// $url = substr($url,$len);
if (@$url[0]!='/')$url = '/'.$url;
if ($url==$this->base_url.'gitlab-hook/'
||$url==$this->base_url.'git-icon-1788c-sm.png'
){
return;
}
$resources = $this->view('Resources')->resources();
$response_view = $this->get_response($url);
if ($response_view===false)return false;
$response_resources = $response_view->resources();
$all_resources = array_merge($resources, $response_resources);
$all_resources = $this->cleanup_resources($all_resources);
$response = $all_resources;
$response['content'] = $response_view.'';
$response['view'] = $response_view;
return $response;
}
/**
* Get a url-specific response ... @see(route()) wraps this to make it easier to send all resource files
*
* @param $url the relative url (base url is removed)
* @return a view on success or `false` on failure
*/
public function get_response($url){
$parsed = $this->parse_url2($url);
if ($parsed==false)return false;
if ($parsed->type=='not-a-project'){
return $this->view('Error');
}
if ($parsed->type=='project_listing'){
$view = $this->view('ProjectSelector');
return $view;
}
$view = $this->view('ProjectLayout',['project'=>$parsed]);
return $view;
}
/**
* does nothing ... just here so `\Lia\View` doesn't crash
*/
public function addResourceFile(){}
/**
* get array of all projects from settings file
* @return array of ... idk
*/
public function all_projects(): array{
// echo "Thsi page will return very soon!";
$settings = $this->settings;
// print_r($settings);exit;
$projects = [];
foreach ($settings as $file => $info){
if (!is_dir($path = $this->dir.'/'.$file))continue;
$projects[$file] = $this->get_project($file);
if (isset($settings[$file]['description']))$projects[$file]->description = $settings[$file]['description'];
else {
$projects[$file]->description = "No description found for '$file'";
}
}
//foreach (scandir($this->dir) as $file){
//if ($file=='.'||$file=='..')continue;
//if (!is_dir($path = $this->dir.'/'.$file))continue;
//$projects[$file] = $this->get_project($file);
//if (isset($settings[$file]['description']))$projects[$file]->description = $settings[$file]['description'];
//else {
//$projects[$file]->description = "No description found for '$file'";
//}
//}
return $projects;
}
/**
* get project info for the list of all projects
*
* @param $project_dir the relative path name inside `$this->dir`
*/
public function get_project($project_dir){
$path = $this->dir.'/'.$project_dir;
$url = $this->base_url.'/'.$project_dir;
$url = str_replace('//','/',$url);
$project = (object)[
'name'=>basename($project_dir),
'dir'=>$project_dir,
// @todo make docs dir configurable
'url'=>$url,
// @todo add project descriptions
'description'=>'project descriptions not yet implemented.',
'src_url'=>$url.'-src',
'docs_url'=>$url,
// 'file'=>
];
return $project;
}
/**
* take the file paths as given by `Lia\Obj\View` and convert them into an array like `['css'=>['/url.css'=>'/path/to/css/file.css'], 'js'=>[...]]`
*/
public function cleanup_resources(array $resources){
$clean = [];
foreach ($resources as $file_path){
$url = substr($file_path,strlen($this->view_dir));
$ext = pathinfo($file_path,PATHINFO_EXTENSION);
$url = $this->base_url.'/files/'.$url;
$url = str_replace('//', '/', $url);
$clean[$ext][$url] = $file_path;
}
return $clean;
}
public function view($rel_name, $args=[]){
if ($this->actual_liaison != null){
$args['actual_liaison'] = $this->actual_liaison;
}
$args['lia'] = $this;
$args['provi'] = $this;
$args['state'] = $this->state;
$view = new \Lia\Obj\View('Docu/'.$rel_name, $this->view_dir, $args);
return $view;
}
/**
* @return false if not a request for provi. or an stdClass object of parsed info
*/
public function parse_url2(string $url){
$parsed = [];
$url = str_replace('%20',' ', $url);
$base_url = $this->base_url;
// check if it's a request to provi
if (substr($url,0,strlen($base_url))==$base_url){
$parsed['prefix'] = $base_url;
} else {
return false;
}
// check if request to project listing
$rel_url = substr($url,strlen($base_url));
if ($rel_url==''){
$project = new \Tlf\Provi\Project();
$project->type = 'project_listing';
$project->prefix = $base_url;
return $project;
}
// parse out the project name & branch
$parts = explode('/', $rel_url);
// array_shift($parts); // might need this if the prefix does not end with a `/`
$project_name_and_branch = array_shift($parts);
$parsed['project_url'] = $base_url.'/'.$project_name_and_branch.'/';
$pnab_parts = explode(':', $project_name_and_branch);
// parse out the type (src or docs)
$project_name_and_type = $pnab_parts[0];
if (substr($project_name_and_type,-4)=='-src'){
$parsed['project_name'] = substr($project_name_and_type,0,-4);
$parsed['type']='src';
} else {
$parsed['project_name'] = $project_name_and_type;
$parsed['type'] = 'docs';
}
if (!isset($this->settings[$parsed['project_name']])){
return (object)['type'=>'not-a-project'];
}
$parsed['default_branch'] = $this->get_default_branch((object)['name'=>$parsed['project_name']]);
if (count($pnab_parts)>1){
$parsed['branch'] = $pnab_parts[1];
$parsed['is_default_branch'] = false;
} else {
// what info do i need to get the default branch?
// project name
$parsed['branch'] = $this->get_default_branch((object)['name'=>$parsed['project_name']]);
$parsed['is_default_branch'] = true;
}
// determine the branch dir
$project_branch_dir = $this->dir.'/'.$parsed['project_name'].'/'.$parsed['branch'].'/';
$parsed['branch_dir'] = $project_branch_dir;
if ($parsed['type']=='docs'){
$docs_dir = $this->get_docs_dir($parsed['branch_dir']);
$file_base_dir = $project_branch_dir.'/'.$docs_dir.'/';//$this->get_docs_dir();
} else {
$file_base_dir = $project_branch_dir;
}
// build the absolute paths
$parsed['docs_dir'] = $project_branch_dir.'/'.$this->get_docs_dir($parsed['branch_dir']).'/';
$parsed['rel_file_path'] = implode('/', $parts);
$parsed['abs_file_path'] = str_replace('//','/',
$file_base_dir.'/'.$parsed['rel_file_path']
);
if (is_dir($parsed['abs_file_path'])){
$parsed['rel_dir_path'] = $parsed['rel_file_path'].'/';
$parsed['abs_dir_path'] = $parsed['abs_file_path'].'/';
// check for a README
if (file_exists($readme_path=$parsed['abs_file_path'].'README.md')){
$parsed['abs_file_path'] = $readme_path;
$parsed['rel_file_path'] .= 'README.md';
}
} else {
$parsed['rel_dir_path'] = dirname($parsed['rel_file_path']).'/';
if ($parsed['rel_dir_path']=='./')$parsed['rel_dir_path'] = '/';
$parsed['abs_dir_path'] = dirname($parsed['abs_file_path']).'/';
}
// remove duplicate forward slashes
$parsed = array_map(
function($v){
return str_replace('//', '/',$v);
},
$parsed
);
// convert array to object
$project = new \Tlf\Provi\Project();
foreach ($parsed as $k=>$v){
$project->$k = $v;
}
$project->git_url = $this->get_git_url($project->project_name);
return $project;
}
/** get the url to the git repo for this project
* @param $project_name should be the same as the project's directory name
*/
public function get_git_url(string $project_name){
$cache_settings_file = $this->cache_dir.'/projects/'.$project_name;
if (!file_exists($cache_settings_file)){
$full_settings = $this->settings;
$proj_settings = $full_settings[$project_name];
if (!file_exists(dirname($cache_settings_file))){
mkdir(dirname($cache_settings_file), 0755, true);
}
file_put_contents($cache_settings_file, serialize($proj_settings));
}
$settings = unserialize(file_get_contents($cache_settings_file));
return $settings['git'];
}
/**
* @return false on failure or the relative path inside the branch dir in which docs are contained
*/
public function get_docs_dir(string $branch_dir){
$try = [
'doc','docs'
];
foreach ($try as $d){
if (is_dir($branch_dir.'/'.$d)){
return $d;
}
}
return false;
}
/**
* @param $project the project as from get_project()
*/
public function get_default_branch(object $project){
$settings = $this->settings;
$branch = $settings[$project->name]['default'];
return $branch;
//@todo actually figure out default branch
// return 'v0.9';
}
public function settings_file(){
return $this->dir.'/settings.json';
}
/**
* get an array of files & directories in the given dir.
*
* @return array ['files'=>['file1','file2'], 'dirs'=>['dir1','dir2']]
*/
public function files_in_dir(string $dir): array{
$files = ['files'=>[],'dirs'=>[]];
if (!is_dir($dir))return $files;
foreach (scandir($dir) as $f){
if ($f=='..'||$f=='.')continue;
if (is_file($dir.'/'.$f))$files['files'][] = $f;
else $files['dirs'][] = $f;
}
sort($files['dirs']);
sort($files['files']);
return $files;
}
}