Smarter way of getting project age for caching and display
Smarter way of getting project age for caching and display

.italic {font-style:italic;} .italic {font-style:italic;}
.bold {font-weight:bold;} .bold {font-weight:bold;}
.underline {text-decoration:underline;} .underline {text-decoration:underline;}
  .empty {color: gray;}
body { body {
font-family: sans-serif; font-size: 12px; border:solid #d9d8d1; border-width:1px; font-family: sans-serif; font-size: 12px; border:solid #d9d8d1; border-width:1px;
margin:10px; background-color:#ffffff; color:#000000; margin:10px; background-color:#ffffff; color:#000000;
} }
a { color:#0000cc; } a { color:#0000cc; }
a:hover, a:visited, a:active { color:#880000; } a:hover, a:visited, a:active { color:#880000; }
div.page_header { height:25px; padding:8px; font-size:18px; font-weight:bold; background-color:#d9d8d1; } div.page_header { height:25px; padding:8px; font-size:18px; font-weight:bold; background-color:#d9d8d1; }
div.page_header a:visited, a.header { color:#0000cc; } div.page_header a:visited, a.header { color:#0000cc; }
div.page_header a:hover { color:#880000; } div.page_header a:hover { color:#880000; }
div.page_nav { padding:8px; } div.page_nav { padding:8px; }
div.page_nav a:visited { color:#0000cc; } div.page_nav a:visited { color:#0000cc; }
div.page_path { padding:8px; border:solid #d9d8d1; border-width:0px 0px 1px} div.page_path { padding:8px; border:solid #d9d8d1; border-width:0px 0px 1px}
div.page_footer { height:17px; padding:4px 8px; background-color: #d9d8d1; } div.page_footer { height:17px; padding:4px 8px; background-color: #d9d8d1; }
div.page_footer_text { float:left; color:#555555; font-style:italic; } div.page_footer_text { float:left; color:#555555; font-style:italic; }
div.page_body { padding:8px; } div.page_body { padding:8px; }
div.title { div.title {
padding:6px 8px; padding:6px 8px;
background-color:#edece6; background-color:#edece6;
} }
div.title a.title { div.title a.title {
display: block; font-weight:bold; text-decoration:none; color:#000000; display: block; font-weight:bold; text-decoration:none; color:#000000;
} }
div.title:hover { background-color: #d9d8d1; } div.title:hover { background-color: #d9d8d1; }
div.title_text { padding:6px 0px; border: solid #d9d8d1; border-width:0px 0px 1px; } div.title_text { padding:6px 0px; border: solid #d9d8d1; border-width:0px 0px 1px; }
div.log_body { padding:8px 8px 8px 150px; } div.log_body { padding:8px 8px 8px 150px; }
span.age { position:relative; float:left; width:142px; font-style:italic; } span.age { position:relative; float:left; width:142px; font-style:italic; }
div.log_link { div.log_link {
margin: -5px 0px; margin: -5px 0px;
padding:0px 8px; padding:0px 8px;
font-size:10px; font-family:sans-serif; font-style:normal; font-size:10px; font-family:sans-serif; font-style:normal;
position:relative; float:left; width:136px; position:relative; float:left; width:136px;
} }
div.list_head { padding:6px 8px 4px; border:solid #d9d8d1; border-width:1px 0px 0px; font-style:italic; } div.list_head { padding:6px 8px 4px; border:solid #d9d8d1; border-width:1px 0px 0px; font-style:italic; }
a.list { text-decoration:none; color:#000000; } a.list { text-decoration:none; color:#000000; }
a.list:hover { text-decoration:underline; color:#880000; } a.list:hover { text-decoration:underline; color:#880000; }
a.text { text-decoration:none; color:#0000cc; } a.text { text-decoration:none; color:#0000cc; }
a.text:visited { text-decoration:none; color:#880000; } a.text:visited { text-decoration:none; color:#880000; }
a.text:hover { text-decoration:underline; color:#880000; } a.text:hover { text-decoration:underline; color:#880000; }
table { padding:8px 4px; } table { padding:8px 4px; }
th { padding:2px 5px; font-size:12px; text-align:left; } th { padding:2px 5px; font-size:12px; text-align:left; }
tr.light:hover { background-color:#edece6; } tr.light:hover { background-color:#edece6; }
tr.dark { background-color:#f6f6f0; } tr.dark { background-color:#f6f6f0; }
tr.dark:hover { background-color:#edece6; } tr.dark:hover { background-color:#edece6; }
td { padding:2px 5px; font-size:12px; vertical-align:top; } td { padding:2px 5px; font-size:12px; vertical-align:top; }
td.link { padding:2px 5px; font-family:sans-serif; font-size:10px; } td.link { padding:2px 5px; font-family:sans-serif; font-size:10px; }
div.pre { font-family:monospace; font-size:12px; white-space:pre; } div.pre { font-family:monospace; font-size:12px; white-space:pre; }
div.diff_info { font-family:monospace; color:#000099; background-color:#edece6; font-style:italic; } div.diff_info { font-family:monospace; color:#000099; background-color:#edece6; font-style:italic; }
div.index_header { border:solid #d9d8d1; border-width:0px 0px 1px; padding:12px 8px; } div.index_header { border:solid #d9d8d1; border-width:0px 0px 1px; padding:12px 8px; }
div.search { font-size: 12px; font-weight: normal; margin:4px 8px; position: absolute; top:56px; right:12px } div.search { font-size: 12px; font-weight: normal; margin:4px 8px; position: absolute; top:56px; right:12px }
a.linenr { color:#999999; text-decoration:none } a.linenr { color:#999999; text-decoration:none }
a.rss_logo { a.rss_logo {
float:right; padding:3px 0px; width:35px; line-height:10px; float:right; padding:3px 0px; width:35px; line-height:10px;
border:1px solid; border-color:#fcc7a5 #7d3302 #3e1a01 #ff954e; border:1px solid; border-color:#fcc7a5 #7d3302 #3e1a01 #ff954e;
color:#ffffff; background-color:#ff6600; color:#ffffff; background-color:#ff6600;
font-weight:bold; font-family:sans-serif; font-size:10px; font-weight:bold; font-family:sans-serif; font-size:10px;
text-align:center; text-decoration:none; text-align:center; text-decoration:none;
} }
a.rss_logo:hover { background-color:#ee5500; } a.rss_logo:hover { background-color:#ee5500; }
span.refs a { span.refs a {
color: #000000; text-decoration: none; color: #000000; text-decoration: none;
} }
span.refs a:hover { span.refs a:hover {
color: #880000; text-decoration: underline; color: #880000; text-decoration: underline;
} }
span.tag { span.tag {
padding:0px 4px; font-size:10px; font-weight:normal; padding:0px 4px; font-size:10px; font-weight:normal;
background-color:#ffffaa; border:1px solid; border-color:#ffffcc #ffee00 #ffee00 #ffffcc; background-color:#ffffaa; border:1px solid; border-color:#ffffcc #ffee00 #ffee00 #ffffcc;
} }
span.head { span.head {
padding:0px 4px; font-size:10px; font-weight:normal; padding:0px 4px; font-size:10px; font-weight:normal;
background-color:#aaffaa; border:1px solid; border-color:#ccffcc #00cc33 #00cc33 #ccffcc; background-color:#aaffaa; border:1px solid; border-color:#ccffcc #00cc33 #00cc33 #ccffcc;
} }
.diffplus { .diffplus {
color: #008800; color: #008800;
} }
.diffminus { .diffminus {
color: #cc0000; color: #cc0000;
} }
.diffat { .diffat {
color: #990099; color: #990099;
} }
span.agehighlight { span.agehighlight {
color: #009900; color: #009900;
} }
img.logo { img.logo {
float: right; float: right;
border-width: 0px; border-width: 0px;
} }
div.lang_select { div.lang_select {
font-size: 12px; font-size: 12px;
font-weight: normal; font-weight: normal;
width: 300px; width: 300px;
float: right; float: right;
text-align: right; text-align: right;
padding-right: 10px; padding-right: 10px;
} }
.respectwhitespace { .respectwhitespace {
white-space: pre; white-space: pre;
} }
.monospace { .monospace {
font-family: monospace; font-family: monospace;
} }
span.newfile { span.newfile {
color: #008000; color: #008000;
} }
span.deletedfile { span.deletedfile {
color: #c00000; color: #c00000;
} }
span.changedfile { span.changedfile {
color: #777777; color: #777777;
} }
span.movedfile { span.movedfile {
color: #777777; color: #777777;
} }
span.latenight { span.latenight {
color: #cc0000; color: #cc0000;
} }
span.searchmatch { span.searchmatch {
color: #e00000; color: #e00000;
} }
div.message { div.message {
padding: 12px; padding: 12px;
} }
div.error { div.error {
color: #ff0000; color: #ff0000;
} }
.indent { .indent {
margin-left: 8px; margin-left: 8px;
} }
table.code td { table.code td {
padding: 0px 0px; padding: 0px 0px;
} }
table.code td.num { table.code td.num {
text-align: right; text-align: right;
font-family:monospace; font-size:12px; white-space:pre; font-family:monospace; font-size:12px; white-space:pre;
} }
table.code td.codeline { table.code td.codeline {
padding-left: 5px; padding-left: 5px;
font-family:monospace; font-size:12px; white-space:pre; font-family:monospace; font-size:12px; white-space:pre;
} }
table.code td.author, table.code td.date { table.code td.author, table.code td.date {
white-space: nowrap; white-space: nowrap;
} }
table.code tr.light:hover { background-color:#ffffff; } table.code tr.light:hover { background-color:#ffffff; }
table.code tr.dark:hover { background-color:#f6f6f0; } table.code tr.dark:hover { background-color:#f6f6f0; }
   
td#blameData { td#blameData {
white-space: nowrap; white-space: nowrap;
text-align: left; text-align: left;
} }
td#blameData div.light:hover { background-color:#edece6; } td#blameData div.light:hover { background-color:#edece6; }
td#blameData div.dark { background-color:#f6f6f0; } td#blameData div.dark { background-color:#f6f6f0; }
td#blameData div.dark:hover { background-color:#edece6; } td#blameData div.dark:hover { background-color:#edece6; }
#blobData .de1, #blobData .de2 {font: normal normal 1em/1.2em monospace; margin:0; padding:0; background:none; vertical-align:top;} #blobData .de1, #blobData .de2 {font: normal normal 1em/1.2em monospace; margin:0; padding:0; background:none; vertical-align:top;}
#blobData {font-family:monospace;} #blobData {font-family:monospace;}
#blobData li, #blobData .li1 {font-weight: normal; vertical-align:top;} #blobData li, #blobData .li1 {font-weight: normal; vertical-align:top;}
table.treeTable td.filesize { text-align: right; } table.treeTable td.filesize { text-align: right; }
table.treeTable td.expander { padding-right: 0px; } table.treeTable td.expander { padding-right: 0px; }
div.projectSearch { padding:8px; border:solid #d9d8d1; border-width:0px 0px 1px; } div.projectSearch { padding:8px; border:solid #d9d8d1; border-width:0px 0px 1px; }
   
<?php <?php
/** /**
* GitPHP ControllerBase * GitPHP ControllerBase
* *
* Base class that all controllers extend * Base class that all controllers extend
* *
* @author Christopher Han <xiphux@gmail.com * @author Christopher Han <xiphux@gmail.com
* @copyright Copyright (c) 2010 Christopher Han * @copyright Copyright (c) 2010 Christopher Han
* @package GitPHP * @package GitPHP
* @subpackage Controller * @subpackage Controller
*/ */
   
/** /**
* ControllerBase class * ControllerBase class
* *
* @package GitPHP * @package GitPHP
* @subpackage Controller * @subpackage Controller
* @abstract * @abstract
*/ */
abstract class GitPHP_ControllerBase abstract class GitPHP_ControllerBase
{ {
   
/** /**
* tpl * tpl
* *
* Smarty instance * Smarty instance
* *
* @access protected * @access protected
*/ */
protected $tpl; protected $tpl;
   
/** /**
* project * project
* *
* Current project * Current project
* *
* @access protected * @access protected
*/ */
protected $project; protected $project;
   
/** /**
* params * params
* *
* Parameters * Parameters
* *
* @access protected * @access protected
*/ */
protected $params = array(); protected $params = array();
   
/** /**
* headers * headers
* *
* Headers * Headers
* *
* @access protected * @access protected
*/ */
protected $headers = array(); protected $headers = array();
   
/** /**
* __construct * __construct
* *
* Constructor * Constructor
* *
* @access public * @access public
* @return mixed controller object * @return mixed controller object
* @throws Exception on invalid project * @throws Exception on invalid project
*/ */
public function __construct() public function __construct()
{ {
require_once(GitPHP_Util::AddSlash(GitPHP_Config::GetInstance()->GetValue('smarty_prefix', 'lib/smarty/libs/')) . 'Smarty.class.php'); require_once(GitPHP_Util::AddSlash(GitPHP_Config::GetInstance()->GetValue('smarty_prefix', 'lib/smarty/libs/')) . 'Smarty.class.php');
$this->tpl = new Smarty; $this->tpl = new Smarty;
$this->tpl->plugins_dir[] = GITPHP_INCLUDEDIR . 'smartyplugins'; $this->tpl->plugins_dir[] = GITPHP_INCLUDEDIR . 'smartyplugins';
   
if (GitPHP_Config::GetInstance()->GetValue('cache', false)) { if (GitPHP_Config::GetInstance()->GetValue('cache', false)) {
$this->tpl->caching = 2; $this->tpl->caching = 2;
if (GitPHP_Config::GetInstance()->HasKey('cachelifetime')) { if (GitPHP_Config::GetInstance()->HasKey('cachelifetime')) {
$this->tpl->cache_lifetime = GitPHP_Config::GetInstance()->GetValue('cachelifetime'); $this->tpl->cache_lifetime = GitPHP_Config::GetInstance()->GetValue('cachelifetime');
} }
   
$servers = GitPHP_Config::GetInstance()->GetValue('memcache', null); $servers = GitPHP_Config::GetInstance()->GetValue('memcache', null);
if (isset($servers) && is_array($servers) && (count($servers) > 0)) { if (isset($servers) && is_array($servers) && (count($servers) > 0)) {
require_once(GITPHP_CACHEDIR . 'Memcache.class.php'); require_once(GITPHP_CACHEDIR . 'Memcache.class.php');
GitPHP_Memcache::GetInstance()->AddServers($servers); GitPHP_Memcache::GetInstance()->AddServers($servers);
require_once(GITPHP_CACHEDIR . 'memcache_cache_handler.php'); require_once(GITPHP_CACHEDIR . 'memcache_cache_handler.php');
$this->tpl->cache_handler_func = 'memcache_cache_handler'; $this->tpl->cache_handler_func = 'memcache_cache_handler';
} }
   
} }
   
if (isset($_GET['p'])) { if (isset($_GET['p'])) {
$this->project = GitPHP_ProjectList::GetInstance()->GetProject(str_replace(chr(0), '', $_GET['p'])); $this->project = GitPHP_ProjectList::GetInstance()->GetProject(str_replace(chr(0), '', $_GET['p']));
if (!$this->project) { if (!$this->project) {
throw new GitPHP_MessageException(sprintf(__('Invalid project %1$s'), $_GET['p']), true); throw new GitPHP_MessageException(sprintf(__('Invalid project %1$s'), $_GET['p']), true);
} }
} }
   
if (isset($_GET['s'])) if (isset($_GET['s']))
$this->params['search'] = $_GET['s']; $this->params['search'] = $_GET['s'];
if (isset($_GET['st'])) if (isset($_GET['st']))
$this->params['searchtype'] = $_GET['st']; $this->params['searchtype'] = $_GET['st'];
   
$this->ReadQuery(); $this->ReadQuery();
} }
   
/** /**
* GetTemplate * GetTemplate
* *
* Gets the template for this controller * Gets the template for this controller
* *
* @access protected * @access protected
* @abstract * @abstract
* @return string template filename * @return string template filename
*/ */
protected abstract function GetTemplate(); protected abstract function GetTemplate();
   
/** /**
* GetCacheKey * GetCacheKey
* *
* Gets the cache key for this controller * Gets the cache key for this controller
* *
* @access protected * @access protected
* @abstract * @abstract
* @return string cache key * @return string cache key
*/ */
protected abstract function GetCacheKey(); protected abstract function GetCacheKey();
   
/** /**
* GetCacheKeyPrefix * GetCacheKeyPrefix
* *
* Get the prefix for all cache keys * Get the prefix for all cache keys
* *
* @access private * @access private
* @param string $projectKeys include project-specific key pieces * @param string $projectKeys include project-specific key pieces
* @return string cache key prefix * @return string cache key prefix
*/ */
private function GetCacheKeyPrefix($projectKeys = true) private function GetCacheKeyPrefix($projectKeys = true)
{ {
$cacheKeyPrefix = GitPHP_Resource::GetLocale(); $cacheKeyPrefix = GitPHP_Resource::GetLocale();
   
$projList = GitPHP_ProjectList::GetInstance(); $projList = GitPHP_ProjectList::GetInstance();
if ($projList) { if ($projList) {
$cacheKeyPrefix .= '|' . sha1(serialize($projList->GetConfig())) . '|' . sha1(serialize($projList->GetSettings())); $cacheKeyPrefix .= '|' . sha1(serialize($projList->GetConfig())) . '|' . sha1(serialize($projList->GetSettings()));
unset($projList); unset($projList);
} }
if ($this->project && $projectKeys) { if ($this->project && $projectKeys) {
$cacheKeyPrefix .= '|' . sha1($this->project->GetProject()); $cacheKeyPrefix .= '|' . sha1($this->project->GetProject());
} }
return $cacheKeyPrefix; return $cacheKeyPrefix;
} }
   
/** /**
* GetFullCacheKey * GetFullCacheKey
* *
* Get the full cache key * Get the full cache key
* *
* @access protected * @access protected
* @return string full cache key * @return string full cache key
*/ */
protected function GetFullCacheKey() protected function GetFullCacheKey()
{ {
$cacheKey = $this->GetCacheKeyPrefix(); $cacheKey = $this->GetCacheKeyPrefix();
   
$subCacheKey = $this->GetCacheKey(); $subCacheKey = $this->GetCacheKey();
   
if (!empty($subCacheKey)) if (!empty($subCacheKey))
$cacheKey .= '|' . $subCacheKey; $cacheKey .= '|' . $subCacheKey;
   
return $cacheKey; return $cacheKey;
} }
   
/** /**
* GetName * GetName
* *
* Gets the name of this controller's action * Gets the name of this controller's action
* *
* @abstract * @abstract
* @access public * @access public
* @param boolean $local true if caller wants the localized action name * @param boolean $local true if caller wants the localized action name
* @return string action name * @return string action name
*/ */
public abstract function GetName($local = false); public abstract function GetName($local = false);
   
/** /**
* ReadQuery * ReadQuery
* *
* Read query into parameters * Read query into parameters
* *
* @abstract * @abstract
* @access prtected * @access prtected
*/ */
protected abstract function ReadQuery(); protected abstract function ReadQuery();
   
/** /**
* SetParam * SetParam
* *
* Set a parameter * Set a parameter
* *
* @access protected * @access protected
* @param string $key key to set * @param string $key key to set
* @param mixed $value value to set * @param mixed $value value to set
*/ */
public function SetParam($key, $value) public function SetParam($key, $value)
{ {
if (empty($key)) if (empty($key))
return; return;
   
if (empty($value)) if (empty($value))
unset($this->params[$key]); unset($this->params[$key]);
   
$this->params[$key] = $value; $this->params[$key] = $value;
} }
   
/** /**
* LoadHeaders * LoadHeaders
* *
* Loads headers for this template * Loads headers for this template
* *
* @access protected * @access protected
*/ */
protected function LoadHeaders() protected function LoadHeaders()
{ {
} }
   
/** /**
* LoadData * LoadData
* *
* Loads data for this template * Loads data for this template
* *
* @access protected * @access protected
* @abstract * @abstract
*/ */
protected abstract function LoadData(); protected abstract function LoadData();
   
/** /**
* LoadCommonData * LoadCommonData
* *
* Loads common data used by all templates * Loads common data used by all templates
* *
* @access private * @access private
*/ */
private function LoadCommonData() private function LoadCommonData()
{ {
global $gitphp_version, $gitphp_appstring; global $gitphp_version, $gitphp_appstring;
   
$this->tpl->assign('version', $gitphp_version); $this->tpl->assign('version', $gitphp_version);
$this->tpl->assign('stylesheet', GitPHP_Config::GetInstance()->GetValue('stylesheet', 'gitphp.css')); $this->tpl->assign('stylesheet', GitPHP_Config::GetInstance()->GetValue('stylesheet', 'gitphp.css'));
$this->tpl->assign('javascript', GitPHP_Config::GetInstance()->GetValue('javascript', true)); $this->tpl->assign('javascript', GitPHP_Config::GetInstance()->GetValue('javascript', true));
$this->tpl->assign('pagetitle', GitPHP_Config::GetInstance()->GetValue('title', $gitphp_appstring)); $this->tpl->assign('pagetitle', GitPHP_Config::GetInstance()->GetValue('title', $gitphp_appstring));
$this->tpl->assign('homelink', GitPHP_Config::GetInstance()->GetValue('homelink', __('projects'))); $this->tpl->assign('homelink', GitPHP_Config::GetInstance()->GetValue('homelink', __('projects')));
$this->tpl->assign('action', $this->GetName()); $this->tpl->assign('action', $this->GetName());
$this->tpl->assign('actionlocal', $this->GetName(true)); $this->tpl->assign('actionlocal', $this->GetName(true));
if ($this->project) if ($this->project)
$this->tpl->assign('project', $this->project); $this->tpl->assign('project', $this->project);
if (GitPHP_Config::GetInstance()->GetValue('search', true)) if (GitPHP_Config::GetInstance()->GetValue('search', true))
$this->tpl->assign('enablesearch', true); $this->tpl->assign('enablesearch', true);
if (GitPHP_Config::GetInstance()->GetValue('filesearch', true)) if (GitPHP_Config::GetInstance()->GetValue('filesearch', true))
$this->tpl->assign('filesearch', true); $this->tpl->assign('filesearch', true);
if (isset($this->params['search'])) if (isset($this->params['search']))
$this->tpl->assign('search', $this->params['search']); $this->tpl->assign('search', $this->params['search']);
if (isset($this->params['searchtype'])) if (isset($this->params['searchtype']))
$this->tpl->assign('searchtype', $this->params['searchtype']); $this->tpl->assign('searchtype', $this->params['searchtype']);
$this->tpl->assign('currentlocale', GitPHP_Resource::GetLocale()); $this->tpl->assign('currentlocale', GitPHP_Resource::GetLocale());
$this->tpl->assign('supportedlocales', GitPHP_Resource::SupportedLocales()); $this->tpl->assign('supportedlocales', GitPHP_Resource::SupportedLocales());
   
$getvars = explode('&', $_SERVER['QUERY_STRING']); $getvars = explode('&', $_SERVER['QUERY_STRING']);
$getvarsmapped = array(); $getvarsmapped = array();
foreach ($getvars as $varstr) { foreach ($getvars as $varstr) {
$eqpos = strpos($varstr, '='); $eqpos = strpos($varstr, '=');
if ($eqpos > 0) { if ($eqpos > 0) {
$var = substr($varstr, 0, $eqpos); $var = substr($varstr, 0, $eqpos);
$val = substr($varstr, $eqpos + 1); $val = substr($varstr, $eqpos + 1);
if (!(empty($var) || empty($val))) { if (!(empty($var) || empty($val))) {
$getvarsmapped[$var] = urldecode($val); $getvarsmapped[$var] = urldecode($val);
} }
} }
} }
$this->tpl->assign('requestvars', $getvarsmapped); $this->tpl->assign('requestvars', $getvarsmapped);
   
$this->tpl->assign('snapshotformats', GitPHP_Archive::SupportedFormats()); $this->tpl->assign('snapshotformats', GitPHP_Archive::SupportedFormats());
} }
   
/** /**
* RenderHeaders * RenderHeaders
* *
* Renders any special headers * Renders any special headers
* *
* @access public * @access public
*/ */
public function RenderHeaders() public function RenderHeaders()
{ {
$this->LoadHeaders(); $this->LoadHeaders();
   
if (count($this->headers) > 0) { if (count($this->headers) > 0) {
foreach ($this->headers as $hdr) { foreach ($this->headers as $hdr) {
header($hdr); header($hdr);
} }
} }
} }
   
/** /**
* Render * Render
* *
* Renders the output * Renders the output
* *
* @access public * @access public
*/ */
public function Render() public function Render()
{ {
if ((GitPHP_Config::GetInstance()->GetValue('cache', false) == true) && (GitPHP_Config::GetInstance()->GetValue('cacheexpire', true) === true)) if ((GitPHP_Config::GetInstance()->GetValue('cache', false) == true) && (GitPHP_Config::GetInstance()->GetValue('cacheexpire', true) === true))
$this->CacheExpire(); $this->CacheExpire();
   
if (!$this->tpl->is_cached($this->GetTemplate(), $this->GetFullCacheKey())) { if (!$this->tpl->is_cached($this->GetTemplate(), $this->GetFullCacheKey())) {
$this->tpl->clear_all_assign(); $this->tpl->clear_all_assign();
$this->LoadCommonData(); $this->LoadCommonData();
$this->LoadData(); $this->LoadData();
} }
   
$this->tpl->display($this->GetTemplate(), $this->GetFullCacheKey()); $this->tpl->display($this->GetTemplate(), $this->GetFullCacheKey());
} }
   
/** /**
* CacheExpire * CacheExpire
* *
* Expires the cache * Expires the cache
* *
* @access public * @access public
* @param boolean $expireAll expire the whole cache * @param boolean $expireAll expire the whole cache
*/ */
public function CacheExpire($expireAll = false) public function CacheExpire($expireAll = false)
{ {
if ($expireAll) { if ($expireAll) {
$this->tpl->clear_all_cache(); $this->tpl->clear_all_cache();
return; return;
} }
   
if (!$this->project) if (!$this->project)
return; return;
   
$headList = $this->project->GetHeads(); $epoch = $this->project->GetEpoch();
  if (empty($epoch))
if (count($headList) > 0) { return;
$age = $headList[0]->GetCommit()->GetAge();  
  $age = $this->project->GetAge();
$this->tpl->clear_cache(null, $this->GetCacheKeyPrefix(), null, $age);  
$this->tpl->clear_cache('projectlist.tpl', $this->GetCacheKeyPrefix(false), null, $age); $this->tpl->clear_cache(null, $this->GetCacheKeyPrefix(), null, $age);
} $this->tpl->clear_cache('projectlist.tpl', $this->GetCacheKeyPrefix(false), null, $age);
} }
   
} }
   
<?php <?php
/** /**
* GitPHP GitExe * GitPHP GitExe
* *
* Class to wrap git executable * Class to wrap git executable
* *
* @author Christopher Han <xiphux@gmail.com> * @author Christopher Han <xiphux@gmail.com>
* @copyright Copyright (c) 2010 Christopher Han * @copyright Copyright (c) 2010 Christopher Han
* @package GitPHP * @package GitPHP
* @subpackage Git * @subpackage Git
*/ */
   
define('GIT_CAT_FILE','cat-file'); define('GIT_CAT_FILE','cat-file');
define('GIT_DIFF_TREE','diff-tree'); define('GIT_DIFF_TREE','diff-tree');
define('GIT_LS_TREE','ls-tree'); define('GIT_LS_TREE','ls-tree');
define('GIT_REV_LIST','rev-list'); define('GIT_REV_LIST','rev-list');
define('GIT_REV_PARSE','rev-parse'); define('GIT_REV_PARSE','rev-parse');
define('GIT_SHOW_REF','show-ref'); define('GIT_SHOW_REF','show-ref');
define('GIT_ARCHIVE','archive'); define('GIT_ARCHIVE','archive');
define('GIT_GREP','grep'); define('GIT_GREP','grep');
define('GIT_BLAME','blame'); define('GIT_BLAME','blame');
define('GIT_NAME_REV','name-rev'); define('GIT_NAME_REV','name-rev');
  define('GIT_FOR_EACH_REF','for-each-ref');
   
/** /**
* Git Executable class * Git Executable class
* *
* @package GitPHP * @package GitPHP
* @subpackage Git * @subpackage Git
*/ */
class GitPHP_GitExe class GitPHP_GitExe
{ {
/** /**
* project * project
* *
* Stores the project internally * Stores the project internally
* *
* @access protected * @access protected
*/ */
protected $project; protected $project;
   
/** /**
* bin * bin
* *
* Stores the binary path internally * Stores the binary path internally
* *
* @access protected * @access protected
*/ */
protected $binary; protected $binary;
   
/** /**
* __construct * __construct
* *
* Constructor * Constructor
* *
* @param string $binary path to git binary * @param string $binary path to git binary
* @param mixed $project project to operate on * @param mixed $project project to operate on
* @return mixed git executable class * @return mixed git executable class
*/ */
public function __construct($project = null) public function __construct($project = null)
{ {
$binary = GitPHP_Config::GetInstance()->GetValue('gitbin'); $binary = GitPHP_Config::GetInstance()->GetValue('gitbin');
if (empty($binary)) { if (empty($binary)) {
// try to pick a reasonable default // try to pick a reasonable default
if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') { if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
$this->binary = 'C:\\Progra~1\\Git\\bin\\git.exe'; $this->binary = 'C:\\Progra~1\\Git\\bin\\git.exe';
} else { } else {
$this->binary = 'git'; $this->binary = 'git';
} }
} else { } else {
$this->binary = $binary; $this->binary = $binary;
} }
   
$this->SetProject($project); $this->SetProject($project);
} }
   
/** /**
* SetProject * SetProject
* *
* Sets the project for this executable * Sets the project for this executable
* *
* @param mixed $project project to set * @param mixed $project project to set
*/ */
public function SetProject($project = null) public function SetProject($project = null)
{ {
$this->project = $project; $this->project = $project;
} }
   
/** /**
* Execute * Execute
* *
* Executes a command * Executes a command
* *
* @param string $command the command to execute * @param string $command the command to execute
* @param array $args arguments * @param array $args arguments
* @return string result of command * @return string result of command
*/ */
public function Execute($command, $args) public function Execute($command, $args)
{ {
$gitDir = ''; $gitDir = '';
if ($this->project) { if ($this->project) {
$gitDir = '--git-dir=' . $this->project->GetPath(); $gitDir = '--git-dir=' . $this->project->GetPath();
} }
$fullCommand = $this->binary . ' ' . $gitDir . ' ' . $command . ' ' . implode(' ', $args); $fullCommand = $this->binary . ' ' . $gitDir . ' ' . $command . ' ' . implode(' ', $args);
   
GitPHP_Log::GetInstance()->Log('Begin executing "' . $fullCommand . '"'); GitPHP_Log::GetInstance()->Log('Begin executing "' . $fullCommand . '"');
   
$ret = shell_exec($fullCommand); $ret = shell_exec($fullCommand);
   
GitPHP_Log::GetInstance()->Log('Finish executing "' . $fullCommand . '"'); GitPHP_Log::GetInstance()->Log('Finish executing "' . $fullCommand . '"');
   
return $ret; return $ret;
} }
   
/** /**
* GetBinary * GetBinary
* *
* Gets the binary for this executable * Gets the binary for this executable
* *
* @return string binary * @return string binary
* @access public * @access public
*/ */
public function GetBinary() public function GetBinary()
{ {
return $this->binary; return $this->binary;
} }
   
/** /**
* GetVersion * GetVersion
* *
* Gets the version of the git binary * Gets the version of the git binary
* *
* @return string version * @return string version
* @access public * @access public
*/ */
public function GetVersion() public function GetVersion()
{ {
$versionCommand = $this->binary . ' --version'; $versionCommand = $this->binary . ' --version';
$ret = trim(shell_exec($versionCommand)); $ret = trim(shell_exec($versionCommand));
if (preg_match('/^git version ([0-9\.]+)$/i', $ret, $regs)) { if (preg_match('/^git version ([0-9\.]+)$/i', $ret, $regs)) {
return $regs[1]; return $regs[1];
} }
return ''; return '';
} }
   
/** /**
* CanSkip * CanSkip
* *
* Tests if this version of git can skip through the revision list * Tests if this version of git can skip through the revision list
* *
* @access public * @access public
* @return boolean true if we can skip * @return boolean true if we can skip
*/ */
public function CanSkip() public function CanSkip()
{ {
$version = $this->GetVersion(); $version = $this->GetVersion();
if (!empty($version)) { if (!empty($version)) {
$splitver = explode('.', $version); $splitver = explode('.', $version);
   
/* Skip only appears in git >= 1.5.0 */ /* Skip only appears in git >= 1.5.0 */
if (($splitver[0] < 1) || (($splitver[0] == 1) && ($splitver[1] < 5))) { if (($splitver[0] < 1) || (($splitver[0] == 1) && ($splitver[1] < 5))) {
return false; return false;
} }
} }
   
return true; return true;
} }
   
/** /**
* CanShowSizeInTree * CanShowSizeInTree
* *
* Tests if this version of git can show the size of a blob when listing a tree * Tests if this version of git can show the size of a blob when listing a tree
* *
* @access public * @access public
* @return true if we can show sizes * @return true if we can show sizes
*/ */
public function CanShowSizeInTree() public function CanShowSizeInTree()
{ {
$version = $this->GetVersion(); $version = $this->GetVersion();
if (!empty($version)) { if (!empty($version)) {
$splitver = explode('.', $version); $splitver = explode('.', $version);
   
/* /*
* ls-tree -l only appears in git 1.5.3 * ls-tree -l only appears in git 1.5.3
* (technically 1.5.3-rc0 but i'm not getting that fancy) * (technically 1.5.3-rc0 but i'm not getting that fancy)
*/ */
if (($splitver[0] < 1) || (($splitver[0] == 1) && ($splitver[1] < 5)) || (($splitver[0] == 1) && ($splitver[1] == 5) && ($splitver[2] < 3))) { if (($splitver[0] < 1) || (($splitver[0] == 1) && ($splitver[1] < 5)) || (($splitver[0] == 1) && ($splitver[1] == 5) && ($splitver[2] < 3))) {
return false; return false;
} }
} }
   
return true; return true;
   
} }
   
/** /**
* CanIgnoreRegexpCase * CanIgnoreRegexpCase
* *
* Tests if this version of git has the regexp tuning option to ignore regexp case * Tests if this version of git has the regexp tuning option to ignore regexp case
* *
* @access public * @access public
* @return true if we can ignore regexp case * @return true if we can ignore regexp case
*/ */
public function CanIgnoreRegexpCase() public function CanIgnoreRegexpCase()
{ {
$version = $this->GetVersion(); $version = $this->GetVersion();
if (!empty($version)) { if (!empty($version)) {
$splitver = explode('.', $version); $splitver = explode('.', $version);
   
/* /*
* regexp-ignore-case only appears in git 1.5.3 * regexp-ignore-case only appears in git 1.5.3
*/ */
if (($splitver[0] < 1) || (($splitver[0] == 1) && ($splitver[1] < 5)) || (($splitver[0] == 1) && ($splitver[1] == 5) && ($splitver[2] < 3))) { if (($splitver[0] < 1) || (($splitver[0] == 1) && ($splitver[1] < 5)) || (($splitver[0] == 1) && ($splitver[1] == 5) && ($splitver[2] < 3))) {
return false; return false;
} }
} }
   
return true; return true;
} }
   
} }
   
<?php <?php
/** /**
* GitPHP Project * GitPHP Project
* *
* Represents a single git project * Represents a single git project
* *
* @author Christopher Han <xiphux@gmail.com> * @author Christopher Han <xiphux@gmail.com>
* @copyright Copyright (c) 2010 Christopher Han * @copyright Copyright (c) 2010 Christopher Han
* @package GitPHP * @package GitPHP
* @subpackage Git * @subpackage Git
*/ */
   
require_once(GITPHP_GITOBJECTDIR . 'GitExe.class.php'); require_once(GITPHP_GITOBJECTDIR . 'GitExe.class.php');
require_once(GITPHP_GITOBJECTDIR . 'Commit.class.php'); require_once(GITPHP_GITOBJECTDIR . 'Commit.class.php');
require_once(GITPHP_GITOBJECTDIR . 'Head.class.php'); require_once(GITPHP_GITOBJECTDIR . 'Head.class.php');
require_once(GITPHP_GITOBJECTDIR . 'Tag.class.php'); require_once(GITPHP_GITOBJECTDIR . 'Tag.class.php');
   
/** /**
* Project class * Project class
* *
* @package GitPHP * @package GitPHP
* @subpackage Git * @subpackage Git
*/ */
class GitPHP_Project class GitPHP_Project
{ {
   
/** /**
* project * project
* *
* Stores the project internally * Stores the project internally
* *
* @access protected * @access protected
*/ */
protected $project; protected $project;
   
/** /**
* owner * owner
* *
* Stores the owner internally * Stores the owner internally
* *
* @access protected * @access protected
*/ */
protected $owner = ""; protected $owner = "";
   
/** /**
* readOwner * readOwner
* *
* Stores whether the file owner has been read * Stores whether the file owner has been read
* *
* @access protected * @access protected
*/ */
protected $readOwner = false; protected $readOwner = false;
   
/** /**
* description * description
* *
* Stores the description internally * Stores the description internally
* *
* @access protected * @access protected
*/ */
protected $description; protected $description;
   
/** /**
* readDescription * readDescription
* *
* Stores whether the description has been * Stores whether the description has been
* read from the file yet * read from the file yet
* *
* @access protected * @access protected
*/ */
protected $readDescription = false; protected $readDescription = false;
   
/** /**
* category * category
* *
* Stores the category internally * Stores the category internally
* *
* @access protected * @access protected
*/ */
protected $category = ''; protected $category = '';
   
  /**
  * epoch
  *
  * Stores the project epoch internally
  *
  * @access protected
  */
  protected $epoch;
   
  /**
  * epochRead
  *
  * Stores whether the project epoch has been read yet
  *
  * @access protected
  */
  protected $epochRead = false;
   
/** /**
* head * head
* *
* Stores the head hash internally * Stores the head hash internally
* *
* @access protected * @access protected
*/ */
protected $head; protected $head;
   
/** /**
* readHeadRef * readHeadRef
* *
* Stores whether the head ref has been read yet * Stores whether the head ref has been read yet
* *
* @access protected * @access protected
*/ */
protected $readHeadRef = false; protected $readHeadRef = false;
   
/** /**
* heads * heads
* *
* Stores the heads for the project * Stores the heads for the project
* *
* @access protected * @access protected
*/ */
protected $heads = array(); protected $heads = array();
   
/** /**
* readHeads * readHeads
* *
* Stores whether heads have been read yet * Stores whether heads have been read yet
* *
* @access protected * @access protected
*/ */
protected $readHeads = false; protected $readHeads = false;
   
/** /**
* tags * tags
* *
* Stores the tags for the project * Stores the tags for the project
* *
* @access protected * @access protected
*/ */
protected $tags = array(); protected $tags = array();
   
/** /**
* readTags * readTags
* *
* Stores whether tags have been read yet * Stores whether tags have been read yet
* *
* @access protected * @access protected
*/ */
protected $readTags = false; protected $readTags = false;
   
/** /**
* cloneUrl * cloneUrl
* *
* Stores the clone url internally * Stores the clone url internally
* *
* @access protected * @access protected
*/ */
protected $cloneUrl = null; protected $cloneUrl = null;
   
/** /**
* pushUrl * pushUrl
* *
* Stores the push url internally * Stores the push url internally
* *
* @access protected * @access protected
*/ */
protected $pushUrl = null; protected $pushUrl = null;
   
/** /**
* bugUrl * bugUrl
* *
* Stores the bug url internally * Stores the bug url internally
* *
* @access protected * @access protected
*/ */
protected $bugUrl = null; protected $bugUrl = null;
   
/** /**
* bugPattern * bugPattern
* *
* Stores the bug pattern internally * Stores the bug pattern internally
* *
* @access protected * @access protected
*/ */
protected $bugPattern = null; protected $bugPattern = null;
   
/** /**
* commitCache * commitCache
* *
* Caches fetched commit objects in case of * Caches fetched commit objects in case of
* repeated requests for the same object * repeated requests for the same object
* *
* @access protected * @access protected
*/ */
protected $commitCache = array(); protected $commitCache = array();
   
/** /**
* __construct * __construct
* *
* Class constructor * Class constructor
* *
* @access public * @access public
* @throws Exception if project is invalid or outside of projectroot * @throws Exception if project is invalid or outside of projectroot
*/ */
public function __construct($project) public function __construct($project)
{ {
$this->SetProject($project); $this->SetProject($project);
} }
   
/** /**
* SetProject * SetProject
* *
* Attempts to set the project * Attempts to set the project
* *
* @access private * @access private
* @throws Exception if project is invalid or outside of projectroot * @throws Exception if project is invalid or outside of projectroot
*/ */
private function SetProject($project) private function SetProject($project)
{ {
$projectRoot = GitPHP_Util::AddSlash(GitPHP_Config::GetInstance()->GetValue('projectroot')); $projectRoot = GitPHP_Util::AddSlash(GitPHP_Config::GetInstance()->GetValue('projectroot'));
   
$realProjectRoot = realpath($projectRoot); $realProjectRoot = realpath($projectRoot);
$path = $projectRoot . $project; $path = $projectRoot . $project;
$fullPath = realpath($path); $fullPath = realpath($path);
   
if (!is_dir($fullPath)) { if (!is_dir($fullPath)) {
throw new Exception(sprintf(__('%1$s is not a directory'), $project)); throw new Exception(sprintf(__('%1$s is not a directory'), $project));
} }
   
if (!is_file($fullPath . '/HEAD')) { if (!is_file($fullPath . '/HEAD')) {
throw new Exception(sprintf(__('%1$s is not a git repository'), $project)); throw new Exception(sprintf(__('%1$s is not a git repository'), $project));
} }
   
if (preg_match('/(^|\/)\.{0,2}(\/|$)/', $project)) { if (preg_match('/(^|\/)\.{0,2}(\/|$)/', $project)) {
throw new Exception(sprintf(__('%1$s is attempting directory traversal'), $project)); throw new Exception(sprintf(__('%1$s is attempting directory traversal'), $project));
} }
   
$pathPiece = substr($fullPath, 0, strlen($realProjectRoot)); $pathPiece = substr($fullPath, 0, strlen($realProjectRoot));
   
if ((!is_link($path)) && (strcmp($pathPiece, $realProjectRoot) !== 0)) { if ((!is_link($path)) && (strcmp($pathPiece, $realProjectRoot) !== 0)) {
throw new Exception(sprintf(__('%1$s is outside of the projectroot'), $project)); throw new Exception(sprintf(__('%1$s is outside of the projectroot'), $project));
} }
   
$this->project = $project; $this->project = $project;
   
} }
   
/** /**
* GetOwner * GetOwner
* *
* Gets the project's owner * Gets the project's owner
* *
* @access public * @access public
* @return string project owner * @return string project owner
*/ */
public function GetOwner() public function GetOwner()
{ {
if (empty($this->owner) && !$this->readOwner) { if (empty($this->owner) && !$this->readOwner) {
$uid = fileowner($this->GetPath()); $uid = fileowner($this->GetPath());
if ($uid > 0) { if ($uid > 0) {
$data = posix_getpwuid($uid); $data = posix_getpwuid($uid);
if (isset($data['gecos']) && !empty($data['gecos'])) { if (isset($data['gecos']) && !empty($data['gecos'])) {
$this->owner = $data['gecos']; $this->owner = $data['gecos'];
} elseif (isset($data['name']) && !empty($data['name'])) { } elseif (isset($data['name']) && !empty($data['name'])) {
$this->owner = $data['name']; $this->owner = $data['name'];
} }
} }
$this->readOwner = true; $this->readOwner = true;
} }
return $this->owner; return $this->owner;
} }
   
/** /**
* SetOwner * SetOwner
* *
* Sets the project's owner (from an external source) * Sets the project's owner (from an external source)
* *
* @access public * @access public
* @param string $owner the owner * @param string $owner the owner
*/ */
public function SetOwner($owner) public function SetOwner($owner)
{ {
$this->owner = $owner; $this->owner = $owner;
} }
   
/** /**
* GetProject * GetProject
* *
* Gets the project * Gets the project
* *
* @access public * @access public
* @return string the project * @return string the project
*/ */
public function GetProject() public function GetProject()
{ {
return $this->project; return $this->project;
} }
   
/** /**
* GetSlug * GetSlug
* *
* Gets the project as a filename/url friendly slug * Gets the project as a filename/url friendly slug
* *
* @access public * @access public
* @return string the slug * @return string the slug
*/ */
public function GetSlug() public function GetSlug()
{ {
$from = array( $from = array(
'/', '/',
'.git' '.git'
); );
$to = array( $to = array(
'-', '-',
'' ''
); );
return str_replace($from, $to, $this->project); return str_replace($from, $to, $this->project);
} }
   
/** /**
* GetPath * GetPath
* *
* Gets the full project path * Gets the full project path
* *
* @access public * @access public
* @return string project path * @return string project path
*/ */
public function GetPath() public function GetPath()
{ {
$projectRoot = GitPHP_Util::AddSlash(GitPHP_Config::GetInstance()->GetValue('projectroot')); $projectRoot = GitPHP_Util::AddSlash(GitPHP_Config::GetInstance()->GetValue('projectroot'));
   
return $projectRoot . $this->project; return $projectRoot . $this->project;
} }
   
/** /**
* GetDescription * GetDescription
* *
* Gets the project description * Gets the project description
* *
* @access public * @access public
* @param $trim length to trim description to (0 for no trim) * @param $trim length to trim description to (0 for no trim)
* @return string project description * @return string project description
*/ */
public function GetDescription($trim = 0) public function GetDescription($trim = 0)
{ {
if (!$this->readDescription) { if (!$this->readDescription) {
$this->description = file_get_contents($this->GetPath() . '/description'); $this->description = file_get_contents($this->GetPath() . '/description');
} }
if (($trim > 0) && (strlen($this->description) > $trim)) { if (($trim > 0) && (strlen($this->description) > $trim)) {
return substr($this->description, 0, $trim) . '…'; return substr($this->description, 0, $trim) . '…';
} }
   
return $this->description; return $this->description;
} }
   
/** /**
* SetDescription * SetDescription
* *
* Overrides the project description * Overrides the project description
* *
* @access public * @access public
* @param string $descr description * @param string $descr description
*/ */
public function SetDescription($descr) public function SetDescription($descr)
{ {
$this->description = $descr; $this->description = $descr;
$this->readDescription = true; $this->readDescription = true;
} }
   
/** /**
* GetDaemonEnabled * GetDaemonEnabled
* *
* Returns whether gitdaemon is allowed for this project * Returns whether gitdaemon is allowed for this project
* *
* @access public * @access public
* @return boolean git-daemon-export-ok? * @return boolean git-daemon-export-ok?
*/ */
public function GetDaemonEnabled() public function GetDaemonEnabled()
{ {
return file_exists($this->GetPath() . '/git-daemon-export-ok'); return file_exists($this->GetPath() . '/git-daemon-export-ok');
} }
   
/** /**
* GetCategory * GetCategory
* *
* Gets the project's category * Gets the project's category
* *
* @access public * @access public
* @return string category * @return string category
*/ */
public function GetCategory() public function GetCategory()
{ {
return $this->category; return $this->category;
} }
   
/** /**
* SetCategory * SetCategory
* *
* Sets the project's category * Sets the project's category
* *
* @access public * @access public
* @param string $category category * @param string $category category
*/ */
public function SetCategory($category) public function SetCategory($category)
{ {
$this->category = $category; $this->category = $category;
} }
   
/** /**
* GetCloneUrl * GetCloneUrl
* *
* Gets the clone URL for this repository, if specified * Gets the clone URL for this repository, if specified
* *
* @access public * @access public
* @return string clone url * @return string clone url
*/ */
public function GetCloneUrl() public function GetCloneUrl()
{ {
if ($this->cloneUrl !== null) if ($this->cloneUrl !== null)
return $this->cloneUrl; return $this->cloneUrl;
   
$cloneurl = GitPHP_Util::AddSlash(GitPHP_Config::GetInstance()->GetValue('cloneurl', ''), false); $cloneurl = GitPHP_Util::AddSlash(GitPHP_Config::GetInstance()->GetValue('cloneurl', ''), false);
if (!empty($cloneurl)) if (!empty($cloneurl))
$cloneurl .= $this->project; $cloneurl .= $this->project;
   
return $cloneurl; return $cloneurl;
} }
   
/** /**
* SetCloneUrl * SetCloneUrl
* *
* Overrides the clone URL for this repository * Overrides the clone URL for this repository
* *
* @access public * @access public
* @param string $cUrl clone url * @param string $cUrl clone url
*/ */
public function SetCloneUrl($cUrl) public function SetCloneUrl($cUrl)
{ {
$this->cloneUrl = $cUrl; $this->cloneUrl = $cUrl;
} }
   
/** /**
* GetPushUrl * GetPushUrl
* *
* Gets the push URL for this repository, if specified * Gets the push URL for this repository, if specified
* *
* @access public * @access public
* @return string push url * @return string push url
*/ */
public function GetPushUrl() public function GetPushUrl()
{ {
if ($this->pushUrl !== null) if ($this->pushUrl !== null)
return $this->pushUrl; return $this->pushUrl;
   
$pushurl = GitPHP_Util::AddSlash(GitPHP_Config::GetInstance()->GetValue('pushurl', ''), false); $pushurl = GitPHP_Util::AddSlash(GitPHP_Config::GetInstance()->GetValue('pushurl', ''), false);
if (!empty($pushurl)) if (!empty($pushurl))
$pushurl .= $this->project; $pushurl .= $this->project;
   
return $pushurl; return $pushurl;
} }
   
/** /**
* SetPushUrl * SetPushUrl
* *
* Overrides the push URL for this repository * Overrides the push URL for this repository
* *
* @access public * @access public
* @param string $pUrl push url * @param string $pUrl push url
*/ */
public function SetPushUrl($pUrl) public function SetPushUrl($pUrl)
{ {
$this->pushUrl = $pUrl; $this->pushUrl = $pUrl;
} }
   
/** /**
* GetBugUrl * GetBugUrl
* *
* Gets the bug URL for this repository, if specified * Gets the bug URL for this repository, if specified
* *
* @access public * @access public
* @return string bug url * @return string bug url
*/ */
public function GetBugUrl() public function GetBugUrl()
{ {
if ($this->bugUrl != null) if ($this->bugUrl != null)
return $this->bugUrl; return $this->bugUrl;
   
return GitPHP_Config::GetInstance()->GetValue('bugurl', ''); return GitPHP_Config::GetInstance()->GetValue('bugurl', '');
} }
   
/** /**
* SetBugUrl * SetBugUrl
* *
* Overrides the bug URL for this repository * Overrides the bug URL for this repository
* *
* @access public * @access public
* @param string $bUrl bug url * @param string $bUrl bug url
*/ */
public function SetBugUrl($bUrl) public function SetBugUrl($bUrl)
{ {
$this->bugUrl = $bUrl; $this->bugUrl = $bUrl;
} }
   
/** /**
* GetBugPattern * GetBugPattern
* *
* Gets the bug pattern for this repository, if specified * Gets the bug pattern for this repository, if specified
* *
* @access public * @access public
* @return string bug pattern * @return string bug pattern
*/ */
public function GetBugPattern() public function GetBugPattern()
{ {
if ($this->bugPattern != null) if ($this->bugPattern != null)
return $this->bugPattern; return $this->bugPattern;
   
return GitPHP_Config::GetInstance()->GetValue('bugpattern', ''); return GitPHP_Config::GetInstance()->GetValue('bugpattern', '');
} }
   
/** /**
* SetBugPattern * SetBugPattern
* *
* Overrides the bug pattern for this repository * Overrides the bug pattern for this repository
* *
* @access public * @access public
* @param string $bPat bug pattern * @param string $bPat bug pattern
*/ */
public function SetBugPattern($bPat) public function SetBugPattern($bPat)
{ {
$this->bugPattern = $bPat; $this->bugPattern = $bPat;
} }
   
/** /**
* GetHeadCommit * GetHeadCommit
* *
* Gets the head commit for this project * Gets the head commit for this project
* Shortcut for getting the tip commit of the HEAD branch * Shortcut for getting the tip commit of the HEAD branch
* *
* @access public * @access public
* @return mixed head commit * @return mixed head commit
*/ */
public function GetHeadCommit() public function GetHeadCommit()
{ {
if (!$this->readHeadRef) if (!$this->readHeadRef)
$this->ReadHeadCommit(); $this->ReadHeadCommit();
   
return $this->GetCommit($this->head); return $this->GetCommit($this->head);
} }
   
/** /**
* ReadHeadCommit * ReadHeadCommit
* *
* Reads the head commit hash * Reads the head commit hash
* *
* @access protected * @access protected
*/ */
public function ReadHeadCommit() public function ReadHeadCommit()
{ {
$this->readHeadRef = true; $this->readHeadRef = true;
   
$exe = new GitPHP_GitExe($this); $exe = new GitPHP_GitExe($this);
$args = array(); $args = array();
$args[] = '--verify'; $args[] = '--verify';
$args[] = 'HEAD'; $args[] = 'HEAD';
$this->head = trim($exe->Execute(GIT_REV_PARSE, $args)); $this->head = trim($exe->Execute(GIT_REV_PARSE, $args));
} }
   
/** /**
* GetCommit * GetCommit
* *
* Get a commit for this project * Get a commit for this project
* *
* @access public * @access public
*/ */
public function GetCommit($hash) public function GetCommit($hash)
{ {
if (empty($hash)) if (empty($hash))
return null; return null;
   
if ($hash === 'HEAD') if ($hash === 'HEAD')
return $this->GetHeadCommit(); return $this->GetHeadCommit();
   
if (substr_compare($hash, 'refs/heads/', 0, 11) === 0) { if (substr_compare($hash, 'refs/heads/', 0, 11) === 0) {
$head = $this->GetHead(substr($hash, 11)); $head = $this->GetHead(substr($hash, 11));
if ($head != null) if ($head != null)
return $head->GetCommit(); return $head->GetCommit();
} else if (substr_compare($hash, 'refs/tags/', 0, 10) === 0) { } else if (substr_compare($hash, 'refs/tags/', 0, 10) === 0) {
$tag = $this->GetTag(substr($hash, 10)); $tag = $this->GetTag(substr($hash, 10));
if ($tag != null) { if ($tag != null) {
$obj = $tag->GetObject(); $obj = $tag->GetObject();
if ($obj != null) { if ($obj != null) {
return $obj; return $obj;
} }
} }
} }
   
if (!isset($this->commitCache[$hash])) { if (!isset($this->commitCache[$hash])) {
$cacheKey = 'project|' . $this->project . '|commit|' . $hash; $cacheKey = 'project|' . $this->project . '|commit|' . $hash;
$cached = GitPHP_Cache::GetInstance()->Get($cacheKey); $cached = GitPHP_Cache::GetInstance()->Get($cacheKey);
if ($cached) if ($cached)
$this->commitCache[$hash] = $cached; $this->commitCache[$hash] = $cached;
else else
$this->commitCache[$hash] = new GitPHP_Commit($this, $hash); $this->commitCache[$hash] = new GitPHP_Commit($this, $hash);
} }
   
return $this->commitCache[$hash]; return $this->commitCache[$hash];
} }
   
/** /**
* CompareProject * CompareProject
* *
* Compares two projects by project name * Compares two projects by project name
* *
* @access public * @access public
* @static * @static
* @param mixed $a first project * @param mixed $a first project
* @param mixed $b second project * @param mixed $b second project
* @return integer comparison result * @return integer comparison result
*/ */
public static function CompareProject($a, $b) public static function CompareProject($a, $b)
{ {
$catCmp = strcmp($a->GetCategory(), $b->GetCategory()); $catCmp = strcmp($a->GetCategory(), $b->GetCategory());
if ($catCmp !== 0) if ($catCmp !== 0)
return $catCmp; return $catCmp;
   
return strcmp($a->GetProject(), $b->GetProject()); return strcmp($a->GetProject(), $b->GetProject());
} }
   
/** /**
* CompareDescription * CompareDescription
* *
* Compares two projects by description * Compares two projects by description
* *
* @access public * @access public
* @static * @static
* @param mixed $a first project * @param mixed $a first project
* @param mixed $b second project * @param mixed $b second project
* @return integer comparison result * @return integer comparison result
*/ */
public static function CompareDescription($a, $b) public static function CompareDescription($a, $b)
{ {
$catCmp = strcmp($a->GetCategory(), $b->GetCategory()); $catCmp = strcmp($a->GetCategory(), $b->GetCategory());
if ($catCmp !== 0) if ($catCmp !== 0)
return $catCmp; return $catCmp;
   
return strcmp($a->GetDescription(), $b->GetDescription()); return strcmp($a->GetDescription(), $b->GetDescription());
} }
   
/** /**
* CompareOwner * CompareOwner
* *
* Compares two projects by owner * Compares two projects by owner
* *
* @access public * @access public
* @static * @static
* @param mixed $a first project * @param mixed $a first project
* @param mixed $b second project * @param mixed $b second project
* @return integer comparison result * @return integer comparison result
*/ */
public static function CompareOwner($a, $b) public static function CompareOwner($a, $b)
{ {
$catCmp = strcmp($a->GetCategory(), $b->GetCategory()); $catCmp = strcmp($a->GetCategory(), $b->GetCategory());
if ($catCmp !== 0) if ($catCmp !== 0)
return $catCmp; return $catCmp;
   
return strcmp($a->GetOwner(), $b->GetOwner()); return strcmp($a->GetOwner(), $b->GetOwner());
} }
   
/** /**
* CompareAge * CompareAge
* *
* Compares two projects by age * Compares two projects by age
* *
* @access public * @access public
* @static * @static
* @param mixed $a first project * @param mixed $a first project
* @param mixed $b second project * @param mixed $b second project
* @return integer comparison result * @return integer comparison result
*/ */
public static function CompareAge($a, $b) public static function CompareAge($a, $b)
{ {
$catCmp = strcmp($a->GetCategory(), $b->GetCategory()); $catCmp = strcmp($a->GetCategory(), $b->GetCategory());
if ($catCmp !== 0) if ($catCmp !== 0)
return $catCmp; return $catCmp;
   
$aCommit = $a->GetHeadCommit(); if ($a->GetAge() === $b->GetAge())
$bCommit = $b->GetHeadCommit();  
   
if (($aCommit == null) && ($bCommit == null))  
return 0; return 0;
else if ($aCommit == null) return ($a->GetAge() < $b->GetAge() ? -1 : 1);
return 1;  
else if ($bCommit == null)  
return -1;  
   
if ($aCommit->GetAge() === $bCommit->GetAge())  
return 0;  
return ($aCommit->GetAge() < $bCommit->GetAge() ? -1 : 1);  
} }
   
/** /**
* GetTags * GetTags
* *
* Gets list of tags for this project * Gets list of tags for this project
* *
* @access public * @access public
* @return array array of tags * @return array array of tags
*/ */
public function GetTags() public function GetTags()
{ {
if (!$this->readTags) if (!$this->readTags)
$this->ReadTagList(); $this->ReadTagList();
   
return $this->tags; return $this->tags;
} }
   
/** /**
* GetTag * GetTag
* *
* Gets a single tag * Gets a single tag
* *
* @access public * @access public
* @param string $tag tag to find * @param string $tag tag to find
* @return mixed tag object * @return mixed tag object
*/ */
public function GetTag($tag) public function GetTag($tag)
{ {
if (empty($tag)) if (empty($tag))
return null; return null;
   
if (!isset($this->tags[$tag])) { if (!isset($this->tags[$tag])) {
$this->LoadTag($tag); $this->LoadTag($tag);
} }
   
return $this->tags[$tag]; return $this->tags[$tag];
} }
   
/** /**
* LoadTag * LoadTag
* *
* Attempts to load a cached tag, or creates a new object * Attempts to load a cached tag, or creates a new object
* *
* @access private * @access private
* @param string $tag tag to find * @param string $tag tag to find
* @return mixed tag object * @return mixed tag object
*/ */
private function LoadTag($tag) private function LoadTag($tag)
{ {
if (empty($tag)) if (empty($tag))
return; return;
   
$cacheKey = 'project|' . $this->project . '|tag|' . $tag; $cacheKey = 'project|' . $this->project . '|tag|' . $tag;
$cached = GitPHP_Cache::GetInstance()->Get($cacheKey); $cached = GitPHP_Cache::GetInstance()->Get($cacheKey);
if ($cached) { if ($cached) {
$this->tags[$tag] = $cached; $this->tags[$tag] = $cached;
} else { } else {
$this->tags[$tag] = new GitPHP_Tag($this, $tag); $this->tags[$tag] = new GitPHP_Tag($this, $tag);
} }
} }
   
/** /**
* ReadTagList * ReadTagList
* *
* Reads tag list * Reads tag list
* *
* @access protected * @access protected
*/ */
protected function ReadTagList() protected function ReadTagList()
{ {
$this->readTags = true; $this->readTags = true;
   
$exe = new GitPHP_GitExe($this); $exe = new GitPHP_GitExe($this);
$args = array(); $args = array();
$args[] = '--tags'; $args[] = '--tags';
$args[] = '--dereference'; $args[] = '--dereference';
$ret = $exe->Execute(GIT_SHOW_REF, $args); $ret = $exe->Execute(GIT_SHOW_REF, $args);
unset($exe); unset($exe);
   
$lines = explode("\n", $ret); $lines = explode("\n", $ret);
   
foreach ($lines as $line) { foreach ($lines as $line) {
if (preg_match('/^([0-9a-fA-F]{40}) refs\/tags\/([^^]+)(\^{})?$/', $line, $regs)) { if (preg_match('/^([0-9a-fA-F]{40}) refs\/tags\/([^^]+)(\^{})?$/', $line, $regs)) {
try { try {
if ((!empty($regs[3])) && ($regs[3] == '^{}')) { if ((!empty($regs[3])) && ($regs[3] == '^{}')) {
$derefCommit = $this->GetCommit($regs[1]); $derefCommit = $this->GetCommit($regs[1]);
if ($derefCommit && isset($this->tags[$regs[2]])) { if ($derefCommit && isset($this->tags[$regs[2]])) {
$this->tags[$regs[2]]->SetCommit($derefCommit); $this->tags[$regs[2]]->SetCommit($derefCommit);
} }
} else if (!isset($this->tags[$regs[2]])) { } else if (!isset($this->tags[$regs[2]])) {
$this->LoadTag($regs[2]); $this->LoadTag($regs[2]);
if (isset($this->tags[$regs[2]])) { if (isset($this->tags[$regs[2]])) {
$tagHash = $this->tags[$regs[2]]->GetHash(); $tagHash = $this->tags[$regs[2]]->GetHash();
if (empty($tagHash)) { if (empty($tagHash)) {
// New non-cached tag object // New non-cached tag object
$this->tags[$regs[2]]->SetHash($regs[1]); $this->tags[$regs[2]]->SetHash($regs[1]);
} }
} }
} }
} catch (Exception $e) { } catch (Exception $e) {
} }
} }
} }
   
uasort($this->tags, array('GitPHP_Tag', 'CompareAge')); uasort($this->tags, array('GitPHP_Tag', 'CompareAge'));
} }
   
/** /**
* GetHeads * GetHeads
* *
* Gets list of heads for this project * Gets list of heads for this project
* *
* @access public * @access public
* @return array array of heads * @return array array of heads
*/ */
public function GetHeads() public function GetHeads()
{ {
if (!$this->readHeads) if (!$this->readHeads)
$this->ReadHeadList(); $this->ReadHeadList();
   
return $this->heads; return $this->heads;
} }
   
/** /**
* GetHead * GetHead
* *
* Gets a single head * Gets a single head
* *
* @access public * @access public
* @param string $head head to find * @param string $head head to find
* @return mixed head object * @return mixed head object
*/ */
public function GetHead($head) public function GetHead($head)
{ {
if (empty($head)) if (empty($head))
return null; return null;
   
if (!$this->readHeads) if (!$this->readHeads)
$this->ReadHeadList(); $this->ReadHeadList();
   
foreach ($this->heads as $h) { foreach ($this->heads as $h) {
if ($h->GetName() === $head) { if ($h->GetName() === $head) {
return $h; return $h;
} }
} }
return null; return null;
} }
   
/** /**
* ReadHeadList * ReadHeadList
* *
* Reads head list * Reads head list
* *
* @access protected * @access protected
*/ */
protected function ReadHeadList() protected function ReadHeadList()
{ {
$this->readHeads = true; $this->readHeads = true;
   
$exe = new GitPHP_GitExe($this); $exe = new GitPHP_GitExe($this);
$args = array(); $args = array();
$args[] = '--heads'; $args[] = '--heads';
$ret = $exe->Execute(GIT_SHOW_REF, $args); $ret = $exe->Execute(GIT_SHOW_REF, $args);
unset($exe); unset($exe);
   
$lines = explode("\n", $ret); $lines = explode("\n", $ret);
   
foreach ($lines as $line) { foreach ($lines as $line) {
if (preg_match('/^([0-9a-fA-F]{40}) refs\/heads\/(.+)$/', $line, $regs)) { if (preg_match('/^([0-9a-fA-F]{40}) refs\/heads\/(.+)$/', $line, $regs)) {
try { try {
$this->heads[] = new GitPHP_Head($this, $regs[2], $regs[1]); $this->heads[] = new GitPHP_Head($this, $regs[2], $regs[1]);
} catch (Exception $e) { } catch (Exception $e) {
} }
} }
} }
   
usort($this->heads, array('GitPHP_Head', 'CompareAge')); usort($this->heads, array('GitPHP_Head', 'CompareAge'));
   
} }
   
/** /**
* GetLogHash * GetLogHash
* *
* Gets log entries as an array of hashes * Gets log entries as an array of hashes
* *
* @access public * @access public
* @param string $hash hash to start the log at * @param string $hash hash to start the log at
* @param integer $count number of entries to get * @param integer $count number of entries to get
* @param integer $skip number of entries to skip * @param integer $skip number of entries to skip
* @return array array of hashes * @return array array of hashes
*/ */
public function GetLogHash($hash, $count = 50, $skip = 0) public function GetLogHash($hash, $count = 50, $skip = 0)
{ {
return $this->RevList($hash, $count, $skip); return $this->RevList($hash, $count, $skip);
} }
   
/** /**
* GetLog * GetLog
* *
* Gets log entries as an array of commit objects * Gets log entries as an array of commit objects
* *
* @access public * @access public
* @param string $hash hash to start the log at * @param string $hash hash to start the log at
* @param integer $count number of entries to get * @param integer $count number of entries to get
* @param integer $skip number of entries to skip * @param integer $skip number of entries to skip
* @return array array of commit objects * @return array array of commit objects
*/ */
public function GetLog($hash, $count = 50, $skip = 0) public function GetLog($hash, $count = 50, $skip = 0)
{ {
$log = $this->GetLogHash($hash, $count, $skip); $log = $this->GetLogHash($hash, $count, $skip);
$len = count($log); $len = count($log);
for ($i = 0; $i < $len; ++$i) { for ($i = 0; $i < $len; ++$i) {
$log[$i] = $this->GetCommit($log[$i]); $log[$i] = $this->GetCommit($log[$i]);
} }
return $log; return $log;
} }
   
/** /**
* GetBlob * GetBlob
* *
* Gets a blob from this project * Gets a blob from this project
* *
* @access public * @access public
* @param string $hash blob hash * @param string $hash blob hash
*/ */
public function GetBlob($hash) public function GetBlob($hash)
{ {
if (empty($hash)) if (empty($hash))
return null; return null;
   
$cacheKey = 'project|' . $this->project . '|blob|' . $hash; $cacheKey = 'project|' . $this->project . '|blob|' . $hash;
$cached = GitPHP_Cache::GetInstance()->Get($cacheKey); $cached = GitPHP_Cache::GetInstance()->Get($cacheKey);
if ($cached) if ($cached)
return $cached; return $cached;
   
return new GitPHP_Blob($this, $hash); return new GitPHP_Blob($this, $hash);
} }
   
/** /**
* GetTree * GetTree
* *
* Gets a tree from this project * Gets a tree from this project
* *
* @access public * @access public
* @param string $hash tree hash * @param string $hash tree hash
*/ */
public function GetTree($hash) public function GetTree($hash)
{ {
if (empty($hash)) if (empty($hash))
return null; return null;
   
$cacheKey = 'project|' . $this->project . '|tree|' . $hash; $cacheKey = 'project|' . $this->project . '|tree|' . $hash;
$cached = GitPHP_Cache::GetInstance()->Get($cacheKey); $cached = GitPHP_Cache::GetInstance()->Get($cacheKey);
if ($cached) if ($cached)
return $cached; return $cached;
   
return new GitPHP_Tree($this, $hash); return new GitPHP_Tree($this, $hash);
} }
   
/** /**
* SearchCommit * SearchCommit
* *
* Gets a list of commits with commit messages matching the given pattern * Gets a list of commits with commit messages matching the given pattern
* *
* @access public * @access public
* @param string $pattern search pattern * @param string $pattern search pattern
* @param string $hash hash to start searching from * @param string $hash hash to start searching from
* @param integer $count number of results to get * @param integer $count number of results to get
* @param integer $skip number of results to skip * @param integer $skip number of results to skip
* @return array array of matching commits * @return array array of matching commits
*/ */
public function SearchCommit($pattern, $hash = 'HEAD', $count = 50, $skip = 0) public function SearchCommit($pattern, $hash = 'HEAD', $count = 50, $skip = 0)
{ {
if (empty($pattern)) if (empty($pattern))
return; return;
   
$args = array(); $args = array();
   
$exe = new GitPHP_GitExe($this); $exe = new GitPHP_GitExe($this);
if ($exe->CanIgnoreRegexpCase()) if ($exe->CanIgnoreRegexpCase())
$args[] = '--regexp-ignore-case'; $args[] = '--regexp-ignore-case';
unset($exe); unset($exe);
   
$args[] = '--grep=\'' . $pattern . '\''; $args[] = '--grep=\'' . $pattern . '\'';
   
$ret = $this->RevList($hash, $count, $skip, $args); $ret = $this->RevList($hash, $count, $skip, $args);
$len = count($ret); $len = count($ret);
   
for ($i = 0; $i < $len; ++$i) { for ($i = 0; $i < $len; ++$i) {
$ret[$i] = $this->GetCommit($ret[$i]); $ret[$i] = $this->GetCommit($ret[$i]);
} }
return $ret; return $ret;
} }
   
/** /**
* SearchAuthor * SearchAuthor
* *
* Gets a list of commits with authors matching the given pattern * Gets a list of commits with authors matching the given pattern
* *
* @access public * @access public
* @param string $pattern search pattern * @param string $pattern search pattern
* @param string $hash hash to start searching from * @param string $hash hash to start searching from
* @param integer $count number of results to get * @param integer $count number of results to get
* @param integer $skip number of results to skip * @param integer $skip number of results to skip
* @return array array of matching commits * @return array array of matching commits
*/ */
public function SearchAuthor($pattern, $hash = 'HEAD', $count = 50, $skip = 0) public function SearchAuthor($pattern, $hash = 'HEAD', $count = 50, $skip = 0)
{ {
if (empty($pattern)) if (empty($pattern))
return; return;
   
$args = array(); $args = array();
   
$exe = new GitPHP_GitExe($this); $exe = new GitPHP_GitExe($this);
if ($exe->CanIgnoreRegexpCase()) if ($exe->CanIgnoreRegexpCase())
$args[] = '--regexp-ignore-case'; $args[] = '--regexp-ignore-case';
unset($exe); unset($exe);
   
$args[] = '--author=\'' . $pattern . '\''; $args[] = '--author=\'' . $pattern . '\'';
   
$ret = $this->RevList($hash, $count, $skip, $args); $ret = $this->RevList($hash, $count, $skip, $args);
$len = count($ret); $len = count($ret);
   
for ($i = 0; $i < $len; ++$i) { for ($i = 0; $i < $len; ++$i) {
$ret[$i] = $this->GetCommit($ret[$i]); $ret[$i] = $this->GetCommit($ret[$i]);
} }
return $ret; return $ret;
} }
   
/** /**
* SearchCommitter * SearchCommitter
* *
* Gets a list of commits with committers matching the given pattern * Gets a list of commits with committers matching the given pattern
* *
* @access public * @access public
* @param string $pattern search pattern * @param string $pattern search pattern
* @param string $hash hash to start searching from * @param string $hash hash to start searching from
* @param integer $count number of results to get * @param integer $count number of results to get
* @param integer $skip number of results to skip * @param integer $skip number of results to skip
* @return array array of matching commits * @return array array of matching commits
*/ */
public function SearchCommitter($pattern, $hash = 'HEAD', $count = 50, $skip = 0) public function SearchCommitter($pattern, $hash = 'HEAD', $count = 50, $skip = 0)
{ {
if (empty($pattern)) if (empty($pattern))
return; return;
   
$args = array(); $args = array();
   
$exe = new GitPHP_GitExe($this); $exe = new GitPHP_GitExe($this);
if ($exe->CanIgnoreRegexpCase()) if ($exe->CanIgnoreRegexpCase())
$args[] = '--regexp-ignore-case'; $args[] = '--regexp-ignore-case';
unset($exe); unset($exe);
   
$args[] = '--committer=\'' . $pattern . '\''; $args[] = '--committer=\'' . $pattern . '\'';
   
$ret = $this->RevList($hash, $count, $skip, $args); $ret = $this->RevList($hash, $count, $skip, $args);
$len = count($ret); $len = count($ret);
   
for ($i = 0; $i < $len; ++$i) { for ($i = 0; $i < $len; ++$i) {
$ret[$i] = $this->GetCommit($ret[$i]); $ret[$i] = $this->GetCommit($ret[$i]);
} }
return $ret; return $ret;
} }
   
/** /**
* RevList * RevList
* *
* Common code for using rev-list command * Common code for using rev-list command
* *
* @access private * @access private
* @param string $hash hash to list from * @param string $hash hash to list from
* @param integer $count number of results to get * @param integer $count number of results to get
* @param integer $skip number of results to skip * @param integer $skip number of results to skip
* @param array $args args to give to rev-list * @param array $args args to give to rev-list
* @return array array of hashes * @return array array of hashes
*/ */
private function RevList($hash, $count = 50, $skip = 0, $args = array()) private function RevList($hash, $count = 50, $skip = 0, $args = array())
{ {
if ($count < 1) if ($count < 1)
return; return;
   
$exe = new GitPHP_GitExe($this); $exe = new GitPHP_GitExe($this);
   
$canSkip = true; $canSkip = true;
if ($skip > 0) if ($skip > 0)
$canSkip = $exe->CanSkip(); $canSkip = $exe->CanSkip();
   
if ($canSkip) { if ($canSkip) {
$args[] = '--max-count=' . $count; $args[] = '--max-count=' . $count;
if ($skip > 0) { if ($skip > 0) {
$args[] = '--skip=' . $skip; $args[] = '--skip=' . $skip;
} }
} else { } else {
$args[] = '--max-count=' . ($count + $skip); $args[] = '--max-count=' . ($count + $skip);
} }
   
$args[] = $hash; $args[] = $hash;
   
$revlist = explode("\n", $exe->Execute(GIT_REV_LIST, $args)); $revlist = explode("\n", $exe->Execute(GIT_REV_LIST, $args));
   
if (!$revlist[count($revlist)-1]) { if (!$revlist[count($revlist)-1]) {
/* the last newline creates a null entry */ /* the last newline creates a null entry */
array_splice($revlist, -1, 1); array_splice($revlist, -1, 1);
} }
   
if (($skip > 0) && (!$exe->CanSkip())) { if (($skip > 0) && (!$exe->CanSkip())) {
return array_slice($revlist, $skip, $count); return array_slice($revlist, $skip, $count);
} }
   
return $revlist; return $revlist;
} }
   
  /**
  * GetEpoch
  *
  * Gets this project's epoch
  * (time of last change)
  *
  * @access public
  * @return integer timestamp
  */
  public function GetEpoch()
  {
  if (!$this->epochRead)
  $this->ReadEpoch();
   
  return $this->epoch;
  }
   
  /**
  * GetAge
  *
  * Gets this project's age
  * (time since most recent change)
  *
  * @access public
  * @return integer age
  */
  public function GetAge()
  {
  if (!$this->epochRead)
  $this->ReadEpoch();
   
  return time() - $this->epoch;
  }
   
  /**
  * ReadEpoch
  *
  * Reads this project's epoch
  * (timestamp of most recent change)
  *
  * @access private
  */
  private function ReadEpoch()
  {
  $this->epochRead = true;
   
  $exe = new GitPHP_GitExe($this);
   
  $args = array();
  $args[] = '--format=\\%\\(committer\\)';
  $args[] = '--sort=-committerdate';
  $args[] = '--count=1';
  $args[] = 'refs/heads';
   
  $epochstr = trim($exe->Execute(GIT_FOR_EACH_REF, $args));
   
  if (preg_match('/ (\d+) [-+][01]\d\d\d$/', $epochstr, $regs)) {
  $this->epoch = $regs[1];
  }
   
  unset($exe);
  }
   
} }
   
{* {*
* projectlist.tpl * projectlist.tpl
* gitphp: A PHP git repository browser * gitphp: A PHP git repository browser
* Component: Project list template * Component: Project list template
* *
* Copyright (C) 2009 Christopher Han <xiphux@gmail.com> * Copyright (C) 2009 Christopher Han <xiphux@gmail.com>
*} *}
{include file='header.tpl'} {include file='header.tpl'}
   
<div class="index_header"> <div class="index_header">
{if file_exists('templates/hometext.tpl') } {if file_exists('templates/hometext.tpl') }
{include file='hometext.tpl'} {include file='hometext.tpl'}
{else} {else}
{* default header *} {* default header *}
<p> <p>
git source code archive git source code archive
</p> </p>
{/if} {/if}
</div> </div>
   
<div class="projectSearch"> <div class="projectSearch">
<form method="get" action="index.php" id="projectSearchForm" enctype="application/x-www-form-urlencoded"> <form method="get" action="index.php" id="projectSearchForm" enctype="application/x-www-form-urlencoded">
{t}Search projects{/t}: <input type="text" name="s" class="projectSearchBox" {if $search}value="{$search}"{/if} /> <a href="index.php" class="clearSearch" {if !$search}style="display: none;"{/if}>X</a> {if $javascript}<img src="images/search-loader.gif" class="searchSpinner" style="display: none;" />{/if} {t}Search projects{/t}: <input type="text" name="s" class="projectSearchBox" {if $search}value="{$search}"{/if} /> <a href="index.php" class="clearSearch" {if !$search}style="display: none;"{/if}>X</a> {if $javascript}<img src="images/search-loader.gif" class="searchSpinner" style="display: none;" />{/if}
</form> </form>
</div> </div>
   
<table cellspacing="0" class="projectList"> <table cellspacing="0" class="projectList">
{foreach name=projects from=$projectlist item=proj} {foreach name=projects from=$projectlist item=proj}
{if $smarty.foreach.projects.first} {if $smarty.foreach.projects.first}
{* Header *} {* Header *}
<tr class="projectHeader"> <tr class="projectHeader">
{if $order == "project"} {if $order == "project"}
<th>{t}Project{/t}</th> <th>{t}Project{/t}</th>
{else} {else}
<th><a class="header" href="{$SCRIPT_NAME}?o=project">{t}Project{/t}</a></th> <th><a class="header" href="{$SCRIPT_NAME}?o=project">{t}Project{/t}</a></th>
{/if} {/if}
{if $order == "descr"} {if $order == "descr"}
<th>{t}Description{/t}</th> <th>{t}Description{/t}</th>
{else} {else}
<th><a class="header" href="{$SCRIPT_NAME}?o=descr">{t}Description{/t}</a></th> <th><a class="header" href="{$SCRIPT_NAME}?o=descr">{t}Description{/t}</a></th>
{/if} {/if}
{if $order == "owner"} {if $order == "owner"}
<th>{t}Owner{/t}</th> <th>{t}Owner{/t}</th>
{else} {else}
<th><a class="header" href="{$SCRIPT_NAME}?o=owner">{t}Owner{/t}</a></th> <th><a class="header" href="{$SCRIPT_NAME}?o=owner">{t}Owner{/t}</a></th>
{/if} {/if}
{if $order == "age"} {if $order == "age"}
<th>{t}Last Change{/t}</th> <th>{t}Last Change{/t}</th>
{else} {else}
<th><a class="header" href="{$SCRIPT_NAME}?o=age">{t}Last Change{/t}</a></th> <th><a class="header" href="{$SCRIPT_NAME}?o=age">{t}Last Change{/t}</a></th>
{/if} {/if}
<th>{t}Actions{/t}</th> <th>{t}Actions{/t}</th>
</tr> </tr>
{/if} {/if}
   
{if $currentcategory != $proj->GetCategory()} {if $currentcategory != $proj->GetCategory()}
{assign var=currentcategory value=$proj->GetCategory()} {assign var=currentcategory value=$proj->GetCategory()}
{if $currentcategory != ''} {if $currentcategory != ''}
<tr class="light categoryRow"> <tr class="light categoryRow">
<th class="categoryName">{$currentcategory}</th> <th class="categoryName">{$currentcategory}</th>
<th></th> <th></th>
<th></th> <th></th>
<th></th> <th></th>
<th></th> <th></th>
</tr> </tr>
{/if} {/if}
{/if} {/if}
   
<tr class="{cycle values="light,dark"} projectRow"> <tr class="{cycle values="light,dark"} projectRow">
<td class="projectName"> <td class="projectName">
<a href="{$SCRIPT_NAME}?p={$proj->GetProject()|urlencode}&amp;a=summary" class="list {if $currentcategory != ''}indent{/if}">{$proj->GetProject()}</a> <a href="{$SCRIPT_NAME}?p={$proj->GetProject()|urlencode}&amp;a=summary" class="list {if $currentcategory != ''}indent{/if}">{$proj->GetProject()}</a>
</td> </td>
<td class="projectDescription"><a href="{$SCRIPT_NAME}?p={$proj->GetProject()|urlencode}&amp;a=summary" class="list">{$proj->GetDescription()}</a></td> <td class="projectDescription"><a href="{$SCRIPT_NAME}?p={$proj->GetProject()|urlencode}&amp;a=summary" class="list">{$proj->GetDescription()}</a></td>
<td class="projectOwner"><em>{$proj->GetOwner()}</em></td> <td class="projectOwner"><em>{$proj->GetOwner()}</em></td>
  {assign var=projecthead value=$proj->GetHeadCommit()}
<td class="projectAge"> <td class="projectAge">
{assign var=projecthead value=$proj->GetHeadCommit()} {if $projecthead}
{if $projecthead} {if $proj->GetAge() < 7200} {* 60*60*2, or 2 hours *}
{if $projecthead->GetAge() < 7200} {* 60*60*2, or 2 hours *} <span class="agehighlight"><strong><em>{$proj->GetAge()|agestring}</em></strong></span>
<span class="agehighlight"><strong><em>{$projecthead->GetAge()|agestring}</em></strong></span> {elseif $proj->GetAge() < 172800} {* 60*60*24*2, or 2 days *}
{elseif $projecthead->GetAge() < 172800} {* 60*60*24*2, or 2 days *} <span class="agehighlight"><em>{$proj->GetAge()|agestring}</em></span>
<span class="agehighlight"><em>{$projecthead->GetAge()|agestring}</em></span>  
{else} {else}
<em>{$projecthead->GetAge()|agestring}</em> <em>{$proj->GetAge()|agestring}</em>
{/if} {/if}
  {else}
  <em class="empty">{t}No commits{/t}</em>
{/if} {/if}
</td> </td>
<td class="link"> <td class="link">
<a href="{$SCRIPT_NAME}?p={$proj->GetProject()|urlencode}&amp;a=summary">{t}summary{/t}</a> <a href="{$SCRIPT_NAME}?p={$proj->GetProject()|urlencode}&amp;a=summary">{t}summary{/t}</a>
{if $projecthead} {if $projecthead}
| |
<a href="{$SCRIPT_NAME}?p={$proj->GetProject()|urlencode}&amp;a=shortlog">{t}shortlog{/t}</a> | <a href="{$SCRIPT_NAME}?p={$proj->GetProject()|urlencode}&amp;a=shortlog">{t}shortlog{/t}</a> |
<a href="{$SCRIPT_NAME}?p={$proj->GetProject()|urlencode}&amp;a=log">{t}log{/t}</a> | <a href="{$SCRIPT_NAME}?p={$proj->GetProject()|urlencode}&amp;a=log">{t}log{/t}</a> |
<a href="{$SCRIPT_NAME}?p={$proj->GetProject()|urlencode}&amp;a=tree">{t}tree{/t}</a> | <a href="{$SCRIPT_NAME}?p={$proj->GetProject()|urlencode}&amp;a=tree">{t}tree{/t}</a> |
<a href="{$SCRIPT_NAME}?p={$proj->GetProject()|urlencode}&amp;a=snapshot&amp;h=HEAD" class="snapshotTip">{t}snapshot{/t}</a> <a href="{$SCRIPT_NAME}?p={$proj->GetProject()|urlencode}&amp;a=snapshot&amp;h=HEAD" class="snapshotTip">{t}snapshot{/t}</a>
{/if} {/if}
</td> </td>
</tr> </tr>
{foreachelse} {foreachelse}
{if $search} {if $search}
<div class="message">{t 1=$search}No matches found for "%1"{/t}</div> <div class="message">{t 1=$search}No matches found for "%1"{/t}</div>
{else} {else}
<div class="message">{t}No projects found{/t}</div> <div class="message">{t}No projects found{/t}</div>
{/if} {/if}
{/foreach} {/foreach}
   
</table> </table>
   
{include file='footer.tpl'} {include file='footer.tpl'}
   
   
comments