Turn script url into a smarty function, fix a few strict warnings
[gitphp.git] / include / controller / ControllerBase.class.php
blob:a/include/controller/ControllerBase.class.php -> blob:b/include/controller/ControllerBase.class.php
--- a/include/controller/ControllerBase.class.php
+++ b/include/controller/ControllerBase.class.php
@@ -1,7 +1,5 @@
 <?php
 /**
- * GitPHP ControllerBase
- *
  * Base class that all controllers extend
  *
  * @author Christopher Han <xiphux@gmail.com
@@ -9,80 +7,110 @@
  * @package GitPHP
  * @subpackage Controller
  */
-
-/**
- * ControllerBase class
- *
- * @package GitPHP
- * @subpackage Controller
- * @abstract
- */
 abstract class GitPHP_ControllerBase
 {
 
 	/**
-	 * tpl
-	 *
+	 * Config handler instance
+	 *
+	 * @var GitPHP_Config
+	 */
+	protected $config;
+
+	/**
 	 * Smarty instance
 	 *
-	 * @access protected
+	 * @var Smarty
 	 */
 	protected $tpl;
 
 	/**
-	 * project
-	 *
+	 * Project list
+	 *
+	 * @var GitPHP_ProjectListBase
+	 */
+	protected $projectList;
+
+	/**
 	 * Current project
 	 *
-	 * @access protected
+	 * @var GitPHP_Project
 	 */
 	protected $project;
 
 	/**
-	 * params
-	 *
+	 * Flag if this is a multi project controller
+	 *
+	 * @var boolean
+	 */
+	protected $multiProject;
+
+	/**
 	 * Parameters
 	 *
-	 * @access protected
+	 * @var array
 	 */
 	protected $params = array();
 
 	/**
-	 * headers
-	 *
-	 * Headers
-	 *
-	 * @access protected
+	 * HTTP Headers
+	 *
+	 * @var string[]
 	 */
 	protected $headers = array();
 
 	/**
-	 * __construct
-	 *
+	 * Flag to preserve whitespace in output (for non-html output)
+	 *
+	 * @var boolean
+	 */
+	protected $preserveWhitespace = false;
+
+	/**
+	 * Logger instance
+	 *
+	 * @var GitPHP_DebugLog
+	 */
+	protected $log;
+
+	/**
+	 * Git executable instance
+	 *
+	 * @var GitPHP_GitExe
+	 */
+	protected $exe;
+
+	/**
 	 * Constructor
-	 *
-	 * @access public
-	 * @return mixed controller object
-	 * @throws Exception on invalid project
 	 */
 	public function __construct()
 	{
-		require_once(GitPHP_Config::GetInstance()->GetValue('smarty_prefix', 'lib/smarty/libs/') . 'Smarty.class.php');
-		$this->tpl = new Smarty;
-		$this->tpl->plugins_dir[] = GITPHP_INCLUDEDIR . 'smartyplugins';
-
-		if (GitPHP_Config::GetInstance()->GetValue('cache', false)) {
-			$this->tpl->caching = 2;
-			if (GitPHP_Config::GetInstance()->HasKey('cachelifetime')) {
-				$this->tpl->cache_lifetime = GitPHP_Config::GetInstance()->GetValue('cachelifetime');
-			}
+		$this->config = GitPHP_Config::GetInstance();
+
+		$log = GitPHP_DebugLog::GetInstance();
+		if ($log && $log->GetEnabled())
+			$this->log = $log;
+
+		$this->InitializeGitExe();
+
+		$this->InitializeProjectList();
+
+		$this->InitializeSmarty();
+
+		if ($this->multiProject) {
+			$this->projectList->LoadProjects();
 		}
 
 		if (isset($_GET['p'])) {
-			$this->project = GitPHP_ProjectList::GetInstance()->GetProject(str_replace(chr(0), '', $_GET['p']));
-			if (!$this->project) {
-				throw new GitPHP_MessageException('Invalid project ' . $_GET['p'], true);
-			}
+			$project = $this->projectList->GetProject(str_replace(chr(0), '', $_GET['p']));
+			if (!$project) {
+				throw new GitPHP_MessageException(sprintf(__('Invalid project %1$s'), $_GET['p']), true);
+			}
+			$this->project = $project->GetProject();
+		}
+
+		if (!($this->project || $this->multiProject)) {
+			throw new GitPHP_MessageException(__('Project is required'), true);
 		}
 
 		if (isset($_GET['s']))
@@ -94,51 +122,139 @@
 	}
 
 	/**
-	 * GetTemplate
-	 *
+	 * Initialize executable
+	 *
+	 * @param boolean $validate whether the exe should be validated
+	 */
+	protected function InitializeGitExe($validate = true)
+	{
+		$this->exe = new GitPHP_GitExe($this->config->GetValue('gitbin'));
+		if ($this->log)
+			$this->exe->AddObserver($this->log);
+		if ($validate && !$this->exe->Valid()) {
+			throw new GitPHP_MessageException(sprintf(__('Could not run the git executable "%1$s".  You may need to set the "%2$s" config value.'), $this->exe->GetBinary(), 'gitbin'), true, 500);
+		}
+	}
+
+	/**
+	 * Initialize project list
+	 */
+	protected function InitializeProjectList()
+	{
+		if (file_exists(GITPHP_CONFIGDIR . 'projects.conf.php')) {
+			$this->projectList = GitPHP_ProjectList::Instantiate(GITPHP_CONFIGDIR . 'projects.conf.php', false);
+		} else {
+			$this->projectList = GitPHP_ProjectList::Instantiate(GITPHP_CONFIGDIR . 'gitphp.conf.php', true);
+		}
+
+		$this->projectList->SetExe($this->exe);
+
+		if ($this->log)
+			$this->projectList->AddObserver($this->log);
+
+	}
+
+	/**
+	 * Initialize smarty
+	 */
+	protected function InitializeSmarty()
+	{
+		require_once(GITPHP_SMARTYDIR . 'Smarty.class.php');
+		$this->tpl = new Smarty;
+		$this->tpl->error_reporting = E_ALL & ~E_NOTICE;
+		$this->tpl->merge_compiled_includes = true;
+		$this->tpl->addPluginsDir(GITPHP_INCLUDEDIR . 'smartyplugins');
+
+		if ($this->config->GetValue('cache', false)) {
+			$this->tpl->caching = Smarty::CACHING_LIFETIME_SAVED;
+			if ($this->config->HasKey('cachelifetime')) {
+				$this->tpl->cache_lifetime = $this->config->GetValue('cachelifetime');
+			}
+
+			$servers = $this->config->GetValue('memcache', null);
+			if (isset($servers) && is_array($servers) && (count($servers) > 0)) {
+				$this->tpl->registerCacheResource('memcache', new GitPHP_CacheResource_Memcache($servers));
+				$this->tpl->caching_type = 'memcache';
+			}
+
+		}
+
+	}
+
+	/**
+	 * Get log instance
+	 *
+	 * @return GitPHP_DebugLog
+	 */
+	public function GetLog()
+	{
+		return $this->log;
+	}
+
+	/**
+	 * Disable logging
+	 */
+	protected function DisableLogging()
+	{
+		if (!$this->log)
+			return;
+
+		$this->projectList->RemoveObserver($this->log);
+
+		$this->log->SetEnabled(false);
+
+		$this->log = null;
+	}
+
+	/**
+	 * Gets the project for this controller
+	 *
+	 * @return GitPHP_Project|null project
+	 */
+	public function GetProject()
+	{
+		if ($this->project)
+			return $this->projectList->GetProject($this->project);
+		return null;
+	}
+
+	/**
 	 * Gets the template for this controller
 	 *
-	 * @access protected
-	 * @abstract
 	 * @return string template filename
 	 */
 	protected abstract function GetTemplate();
 
 	/**
-	 * GetCacheKey
-	 *
 	 * Gets the cache key for this controller
 	 *
-	 * @access protected
-	 * @abstract
 	 * @return string cache key
 	 */
 	protected abstract function GetCacheKey();
 
 	/**
-	 * GetCacheKeyPrefix
-	 *
 	 * Get the prefix for all cache keys
 	 *
-	 * @access private
+	 * @param boolean $projectKeys include project-specific key pieces
 	 * @return string cache key prefix
 	 */
-	private function GetCacheKeyPrefix()
-	{
-		$cacheKeyPrefix = sha1(serialize(GitPHP_ProjectList::GetInstance()->GetConfig()));
-		if ($this->project) {
-			$cacheKeyPrefix .= '|' . sha1($this->project->GetProject());
+	private function GetCacheKeyPrefix($projectKeys = true)
+	{
+		$cacheKeyPrefix = GitPHP_Resource::GetLocale();
+
+		if ($this->projectList) {
+			$cacheKeyPrefix .= '|' . sha1(serialize($this->projectList->GetConfig())) . '|' . sha1(serialize($this->projectList->GetSettings()));
+		}
+		if ($this->project && $projectKeys) {
+			$cacheKeyPrefix .= '|' . sha1($this->project);
 		}
 		
 		return $cacheKeyPrefix;
 	}
 
 	/** 
-	 * GetFullCacheKey
-	 *
 	 * Get the full cache key
 	 *
-	 * @access protected
 	 * @return string full cache key
 	 */
 	protected function GetFullCacheKey()
@@ -150,25 +266,29 @@
 		if (!empty($subCacheKey))
 			$cacheKey .= '|' . $subCacheKey;
 
+		if (strlen($cacheKey) > 100) {
+			$cacheKey = sha1($cacheKey);
+		}
+
 		return $cacheKey;
 	}
 
 	/**
-	 * ReadQuery
-	 *
+	 * Gets the name of this controller's action
+	 *
+	 * @param boolean $local true if caller wants the localized action name
+	 * @return string action name
+	 */
+	public abstract function GetName($local = false);
+
+	/**
 	 * Read query into parameters
-	 *
-	 * @abstract
-	 * @access prtected
 	 */
 	protected abstract function ReadQuery();
 
 	/**
-	 * SetParam
-	 *
 	 * Set a parameter
 	 *
-	 * @access protected
 	 * @param string $key key to set
 	 * @param mixed $value value to set
 	 */
@@ -184,58 +304,91 @@
 	}
 
 	/**
-	 * LoadHeaders
-	 *
 	 * Loads headers for this template
-	 *
-	 * @access protected
 	 */
 	protected function LoadHeaders()
 	{
 	}
 
 	/**
-	 * LoadData
-	 *
 	 * Loads data for this template
-	 *
-	 * @access protected
-	 * @abstract
 	 */
 	protected abstract function LoadData();
 
 	/**
-	 * LoadCommonData
-	 *
 	 * Loads common data used by all templates
-	 *
-	 * @access private
 	 */
 	private function LoadCommonData()
 	{
 		global $gitphp_version, $gitphp_appstring;
 
 		$this->tpl->assign('version', $gitphp_version);
-		$this->tpl->assign('stylesheet', GitPHP_Config::GetInstance()->GetValue('stylesheet', 'gitphp.css'));
-		$this->tpl->assign('pagetitle', GitPHP_Config::GetInstance()->GetValue('title', $gitphp_appstring));
+
+		$stylesheet = $this->config->GetValue('stylesheet', 'gitphpskin.css');
+		if ($stylesheet == 'gitphp.css') {
+			// backwards compatibility
+			$stylesheet = 'gitphpskin.css';
+		}
+		$this->tpl->assign('stylesheet', preg_replace('/\.css$/', '', $stylesheet));
+
+		$this->tpl->assign('javascript', $this->config->GetValue('javascript', true));
+		$this->tpl->assign('googlejs', $this->config->GetValue('googlejs', false));
+		$this->tpl->assign('pagetitle', $this->config->GetValue('title', $gitphp_appstring));
+		$this->tpl->assign('homelink', $this->config->GetValue('homelink', __('projects')));
+		$this->tpl->assign('action', $this->GetName());
+		$this->tpl->assign('actionlocal', $this->GetName(true));
 		if ($this->project)
-			$this->tpl->assign('project', $this->project);
-		if (GitPHP_Config::GetInstance()->GetValue('search', true))
+			$this->tpl->assign('project', $this->GetProject());
+		if ($this->config->GetValue('search', true))
 			$this->tpl->assign('enablesearch', true);
-		if (GitPHP_Config::GetInstance()->GetValue('filesearch', true))
+		if ($this->config->GetValue('filesearch', true))
 			$this->tpl->assign('filesearch', true);
 		if (isset($this->params['search']))
 			$this->tpl->assign('search', $this->params['search']);
 		if (isset($this->params['searchtype']))
 			$this->tpl->assign('searchtype', $this->params['searchtype']);
-	}
-
-	/**
-	 * RenderHeaders
-	 *
+		$this->tpl->assign('currentlocale', GitPHP_Resource::GetLocale());
+		$this->tpl->assign('supportedlocales', GitPHP_Resource::SupportedLocales($this->config->GetValue('debug', false)));
+
+		$scripturl = $_SERVER['SCRIPT_NAME'];
+		$fullscripturl = '';
+		if (isset($_SERVER['HTTPS']) && ($_SERVER['HTTPS'] == 'on'))
+			$fullscripturl = 'https://';
+		else
+			$fullscripturl = 'http://';
+		$fullscripturl .= $_SERVER['HTTP_HOST'] . $scripturl;
+
+		if (GitPHP_Config::GetInstance()->HasKey('self')) {
+			$selfurl = GitPHP_Config::GetInstance()->GetValue('self');
+			if (!empty($selfurl)) {
+				if (substr($selfurl, -4) != '.php') {
+					$selfurl = GitPHP_Util::AddSlash($selfurl);
+				}
+				$fullscripturl = $selfurl;
+			}
+		}
+		$this->tpl->assign('scripturl', $scripturl);
+		$this->tpl->assign('fullscripturl', $fullscripturl);
+
+		$getvars = explode('&', $_SERVER['QUERY_STRING']);
+		$getvarsmapped = array();
+		foreach ($getvars as $varstr) {
+			$eqpos = strpos($varstr, '=');
+			if ($eqpos > 0) {
+				$var = substr($varstr, 0, $eqpos);
+				$val = substr($varstr, $eqpos + 1);
+				if (!(empty($var) || empty($val))) {
+					$getvarsmapped[$var] = urldecode($val);
+				}
+			}
+		}
+		$this->tpl->assign('requestvars', $getvarsmapped);
+
+		$this->tpl->assign('snapshotformats', GitPHP_Archive::SupportedFormats());
+	}
+
+	/**
 	 * Renders any special headers
-	 *
-	 * @access public
 	 */
 	public function RenderHeaders()
 	{
@@ -249,52 +402,62 @@
 	}
 
 	/**
-	 * Render
-	 *
 	 * Renders the output
-	 *
-	 * @access public
 	 */
 	public function Render()
 	{
-		if ((GitPHP_Config::GetInstance()->GetValue('cache', false) == true) && (GitPHP_Config::GetInstance()->GetValue('cacheexpire', true) === true))
+		if (($this->config->GetValue('cache', false) == true) && ($this->config->GetValue('cacheexpire', true) === true))
 			$this->CacheExpire();
 
-		if (!$this->tpl->is_cached($this->GetTemplate(), $this->GetFullCacheKey())) {
-			$this->tpl->clear_all_assign();
+		if (!$this->tpl->isCached($this->GetTemplate(), $this->GetFullCacheKey())) {
+			$this->tpl->clearAllAssign();
+			if ($this->log && $this->log->GetBenchmark())
+				$this->log->Log("Data load begin");
 			$this->LoadCommonData();
 			$this->LoadData();
-		}
-
+			if ($this->log && $this->log->GetBenchmark())
+				$this->log->Log("Data load end");
+		}
+
+		if (!$this->preserveWhitespace) {
+			//$this->tpl->loadFilter('output', 'trimwhitespace');
+		}
+
+		if ($this->log && $this->log->GetBenchmark())
+			$this->log->Log("Smarty render begin");
 		$this->tpl->display($this->GetTemplate(), $this->GetFullCacheKey());
-	}
-
-	/**
-	 * CacheExpire
-	 *
+		if ($this->log && $this->log->GetBenchmark())
+			$this->log->Log("Smarty render end");
+
+		$this->tpl->clearAllAssign();
+
+		if ($this->log)
+			$this->log->Log('MemoryCache count: ' . $this->projectList->GetMemoryCache()->GetCount());
+	}
+
+	/**
 	 * Expires the cache
 	 *
-	 * @access public
 	 * @param boolean $expireAll expire the whole cache
 	 */
 	public function CacheExpire($expireAll = false)
 	{
 		if ($expireAll) {
-			$this->tpl->clear_all_cache();
+			$this->tpl->clearAllCache();
 			return;
 		}
 
 		if (!$this->project)
 			return;
 
-		$headList = $this->project->GetHeads();
-
-		if (count($headlist) > 0) {
-			$age = $headlist[0]->GetCommit()->GetAge();
-
-			$this->tpl->clear_cache(null, $this->GetCacheKeyPrefix(), null, $age);
-			$this->tpl->clear_cache('projectlist.tpl', sha1(serialize(GitPHP_ProjectList::GetInstance()->GetConfig())), null, $age);
-		}
+		$epoch = $this->GetProject()->GetEpoch();
+		if (empty($epoch))
+			return;
+
+		$age = $this->GetProject()->GetAge();
+
+		$this->tpl->clearCache(null, $this->GetCacheKeyPrefix(), null, $age);
+		$this->tpl->clearCache('projectlist.tpl', $this->GetCacheKeyPrefix(false), null, $age);
 	}
 
 }

comments