DaPortal
<?php //$Id$
							//Copyright (c) 2012-2016 Pierre Pronchery <khorben@defora.org>
							//This file is part of DeforaOS Web DaPortal
							//
							//This program is free software: you can redistribute it and/or modify
							//it under the terms of the GNU General Public License as published by
							//the Free Software Foundation, version 3 of the License.
							//
							//This program is distributed in the hope that it will be useful,
							//but WITHOUT ANY WARRANTY; without even the implied warranty of
							//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
							//GNU General Public License for more details.
							//
							//You should have received a copy of the GNU General Public License
							//along with this program.  If not, see <http://www.gnu.org/licenses/>.
							require_once('./modules/project/scm.php');
							//GitSCMProject
							class GitSCMProject extends SCMProject
							{
								//public
								//GitSCMProject::attach
								public function attach(Engine $engine)
								{
									global $config;
									$this->gitroot = $config->get('module::project',
											'scm::backend::git::gitroot');
									parent::attach($engine);
								}
								//actions
								//GitSCMProject::browse
								public function browse(ProjectContent $project, Request $request)
								{
									$error = _('No Git repository defined');
									//check the gitroot
									if($this->gitroot === FALSE
											|| strlen($project->get('cvsroot')) == 0)
										return new PageElement('dialog', array(
												'type' => 'error', 'text' => $error));
									$vbox = new PageElement('vbox');
									//browse
									$url = parse_url($project->get('cvsroot'));
									$path = $this->gitroot.'/'.basename($url['path']);
									if(($file = $request->get('file')) !== FALSE)
									{
										$file = $this->helperSanitizePath($file);
										$path .= "/$file";
									}
									else
										$file = '/';
									$error = _('No such file or directory');
									if(($st = @lstat($path)) === FALSE)
										return new PageElement('dialog', array(
												'type' => 'error', 'text' => $error));
									if(($st['mode'] & static::$S_IFDIR) === static::$S_IFDIR)
										return $this->_browseDir($request, $vbox, $path, $file);
									return $this->_browseFile($request, $vbox, $path, $file);
								}
								private function _browseDir(Request $request, PageElement $vbox, $path,
										$file)
								{
									$error = _('Could not open directory');
									$vbox->append('title', array('text' => _('Browse source')));
									if(($dir = opendir($path)) === FALSE)
										return new PageElement('dialog', array(
												'type' => 'error', 'text' => $error));
									//view
									$columns = array('icon' => '', 'title' => _('Filename'),
											'date' => _('Date'));
									$view = $vbox->append('treeview', array('columns' => $columns));
									$folders = array();
									$files = array();
									while(($de = readdir($dir)) !== FALSE)
									{
										if(strncmp('.', $de, 1) == 0)
											continue;
										if(($st = lstat($path.'/'.$de)) === FALSE)
											continue;
										if(($st['mode'] & static::$S_IFDIR) == static::$S_IFDIR)
											$folders[$de] = $st;
										else
											$files[$de] = $st;
									}
									ksort($folders);
									ksort($files);
									foreach($folders as $de => $st)
									{
										$row = $view->append('row');
										$icon = Mime::getIconByType($this->engine,
												'inode/directory', 16);
										$icon = new PageElement('image', array(
												'source' => $icon));
										$row->set('icon', $icon);
										//title
										$f = ltrim($file.'/'.$de, '/');
										$r = new Request($request->getModule(),
											$request->getAction(),
											$request->getID(), $request->getTitle(),
											array('file' => $f));
										$link = new PageElement('link', array('request' => $r,
												'text' => $de));
										$row->set('title', $link);
										//date
										$date = Common::getDateTime($st['mtime']);
										$row->set('date', $date);
									}
									foreach($files as $de => $st)
									{
										$row = $view->append('row');
										$icon = Mime::getIcon($this->engine, $de, 16);
										$icon = new PageElement('image', array(
												'source' => $icon));
										$row->set('icon', $icon);
										//title
										$f = ltrim($file.'/'.$de, '/');
										$r = new Request($request->getModule(),
											$request->getAction(),
											$request->getID(), $request->getTitle(),
											array('file' => $f));
										$link = new PageElement('link', array('request' => $r,
												'text' => $de));
										$row->set('title', $link);
										//date
										$date = Common::getDateTime($st['mtime']);
										$row->set('date', $date);
									}
									closedir($dir);
									return $vbox;
								}
								private function _browseFile(Request $request, PageElement $vbox, $path,
										$file)
								{
									$error = _('Could not open file');
									if(($fp = fopen($path, 'r')) === FALSE)
										return new PageElement('dialog', array(
												'type' => 'error', 'text' => $error));
									$filename = basename($file);
									$type = Mime::getType($this->engine, $filename);
									if($request->get('download') !== FALSE)
									{
										$ret = new StreamResponse($fp);
										$ret->setFilename($filename);
										$ret->setType($type);
										return $ret;
									}
									$label = $vbox->append('label');
									//link back
									$r = new Request('project', 'browse', $request->getID(),
										$request->getTitle(), array('file' => dirname($file)));
									$label->append('link', array('request' => $r,
											'stock' => 'updir', 'text' => 'Browse'));
									//link to the download
									$label->append('label', array('text' => ' '));
									$rdownload = new Request('project', 'browse', $request->getID(),
										$request->getTitle(), array('file' => $file,
										'download' => 1));
									$label->append('link', array('request' => $rdownload,
												'stock' => 'download',
												'text' => 'Download file'));
									//link to this page
									$label->append('label', array('text' => ' '));
									$r = new Request('project', 'browse', $request->getID(),
										$request->getTitle(), array('file' => $file));
									$label->append('link', array('request' => $r,
												'stock' => 'link',
												'text' => 'Permalink'));
									if(strncmp($type, 'image/', 6) == 0)
										//attempt to render the image inline
										$vbox->append('image', array('request' => $rdownload,
												'text' => $filename));
									else
										//render as text
										while(($line = fgets($fp)) !== FALSE)
										{
											$line = rtrim($line, "\r\n");
											$vbox->append('label', array(
													'class' => 'preformatted',
													'text' => $line));
										}
									fclose($fp);
									return $vbox;
								}
								//GitSCMProject::download
								public function download(ProjectContent $project, Request $request)
								{
									$title = _('Repository');
									//repository
									$vbox = new PageElement('vbox');
									$vbox->append('title', array('text' => $title));
									$text = _('The source code can be obtained as follows: ');
									$vbox->append('label', array('text' => $text));
									$text = '$ git clone '.$project->get('cvsroot');
									$vbox->append('label', array('text' => $text,
											'class' => 'preformatted'));
									return $vbox;
								}
								//GitSCMProject::timeline
								public function timeline(ProjectContent $project, Request $request)
								{
									$error = _('No Git repository defined');
									//FIXME really implement
									//check the gitroot
									return new PageElement('dialog', array('type' => 'error',
											'text' => $error));
								}
								//protected
								//methods
								//helpers
								//GitSCMProject::helperSanitizePath
								protected function helperSanitizePath($path)
								{
									$path = '/'.ltrim($path, '/');
									$path = str_replace('/./', '/', $path);
									//FIXME really implement '..'
									if(strncmp($path, '/.', 2) == 0
											|| strpos($path, '/../') !== FALSE)
										return '/';
									return $path;
								}
								//private
								//properties
								static private $S_IFDIR = 040000;
								private $gitroot = FALSE;
							}
							?>
							