Move projectlist classes to their own folder
Move projectlist classes to their own folder

--- a/include/git/ProjectList.class.php
+++ /dev/null
@@ -1,114 +1,1 @@
-<?php
-/**
- * GitPHP ProjectList
- *
- * Project list singleton instance and factory
- *
- * @author Christopher Han <xiphux@gmail.com>
- * @copyright Copyright (c) 2010 Christopher Han
- * @package GitPHP
- * @subpackage Git
- */
 
-require_once(GITPHP_GITOBJECTDIR . 'ProjectListDirectory.class.php');
-require_once(GITPHP_GITOBJECTDIR . 'ProjectListFile.class.php');
-require_once(GITPHP_GITOBJECTDIR . 'ProjectListArray.class.php');
-require_once(GITPHP_GITOBJECTDIR . 'ProjectListArrayLegacy.class.php');
-require_once(GITPHP_GITOBJECTDIR . 'ProjectListScmManager.class.php');
-
-/**
- * ProjectList class
- *
- * @package GitPHP
- * @subpackage Git
- */
-class GitPHP_ProjectList
-{
-
-	/**
-	 * instance
-	 *
-	 * Stores the singleton instance of the projectlist
-	 *
-	 * @access protected
-	 * @static
-	 */
-	protected static $instance = null;
-
-	/**
-	 * GetInstance
-	 *
-	 * Returns the singleton instance
-	 *
-	 * @access public
-	 * @static
-	 * @return mixed instance of projectlist
-	 * @throws Exception if projectlist has not been instantiated yet
-	 */
-	public static function GetInstance()
-	{
-		return self::$instance;
-	}
-
-	/**
-	 * DestroyInstance
-	 *
-	 * Releases the singleton instance
-	 *
-	 * @access public
-	 * @static
-	 */
-	public static function DestroyInstance()
-	{
-		self::$instance = null;
-	}
-
-	/**
-	 * Instantiate
-	 *
-	 * Instantiates the singleton instance
-	 *
-	 * @access private
-	 * @static
-	 * @param string $file config file with git projects
-	 * @param boolean $legacy true if this is the legacy project config
-	 * @throws Exception if there was an error reading the file
-	 */
-	public static function Instantiate($file = null, $legacy = false)
-	{
-		if (self::$instance)
-			return;
-			
-		$projectRoot = GitPHP_Config::GetInstance()->GetValue('projectroot');
-
-
-		if (!empty($file) && is_file($file) && include($file)) {
-			if (isset($git_projects)) {
-				if (is_string($git_projects)) {
-					if (function_exists('simplexml_load_file') && GitPHP_ProjectListScmManager::IsSCMManager($git_projects)) {
-						self::$instance = new GitPHP_ProjectListScmManager($projectRoot, $git_projects);
-					} else {
-						self::$instance = new GitPHP_ProjectListFile($projectRoot, $git_projects);
-					}
-				} else if (is_array($git_projects)) {
-					if ($legacy) {
-						self::$instance = new GitPHP_ProjectListArrayLegacy($projectRoot, $git_projects);
-					} else {
-						self::$instance = new GitPHP_ProjectListArray($projectRoot, $git_projects);
-					}
-				}
-			}
-		}
-
-		if (!self::$instance) {
-
-			self::$instance = new GitPHP_ProjectListDirectory($projectRoot, GitPHP_Config::GetInstance()->GetValue('exportedonly', false));
-		}
-
-		if (isset($git_projects_settings) && !$legacy)
-			self::$instance->SetSettings($git_projects_settings);
-	}
-
-}
-
-

--- a/include/git/ProjectListArray.class.php
+++ /dev/null
@@ -1,143 +1,1 @@
-<?php
-/**
- * GitPHP ProjectListArray
- *
- * Lists all projects in a multidimensional array
- *
- * @author Christopher Han <xiphux@gmail.com>
- * @copyright Copyright (c) 2010 Christopher Han
- * @package GitPHP
- * @subpackage Git
- */
 
-require_once(GITPHP_GITOBJECTDIR . 'ProjectListBase.class.php');
-require_once(GITPHP_GITOBJECTDIR . 'Project.class.php');
-
-/**
- * ProjectListArray class
- *
- * @package GitPHP
- * @subpackage Git
- */
-class GitPHP_ProjectListArray extends GitPHP_ProjectListBase
-{
-
-	/**
-	 * __construct
-	 *
-	 * constructor
-	 *
-	 * @param string $projectRoot project root
-	 * @param mixed $projectArray array to read
-	 * @throws Exception if parameter is not an array
-	 * @access public
-	 */
-	public function __construct($projectRoot, $projectArray)
-	{
-		if (!is_array($projectArray)) {
-			throw new Exception('An array of projects is required');
-		}
-
-		$this->projectConfig = $projectArray;
-
-		parent::__construct($projectRoot);
-	}
-
-	/**
-	 * PopulateProjects
-	 *
-	 * Populates the internal list of projects
-	 *
-	 * @access protected
-	 * @throws Exception if file cannot be read
-	 */
-	protected function PopulateProjects()
-	{
-		foreach ($this->projectConfig as $proj => $projData) {
-			try {
-				if (is_string($projData)) {
-					// Just flat array of project paths
-					$projObj = $this->InstantiateProject($projData);
-					if ($projObj) {
-						$this->projects[$projData] = $projObj;
-						unset($projObj);
-					}
-				} else if (is_array($projData)) {
-					if (is_string($proj) && !empty($proj)) {
-						// Project key pointing to data array
-						$projObj = $this->InstantiateProject($proj);
-						if ($projObj) {
-							$this->projects[$proj] = $projObj;
-							unset($projObj);
-						}
-					} else if (isset($projData['project'])) {
-						// List of data arrays with projects inside
-						$projObj = $this->InstantiateProject($projData['project']);
-						if ($projObj) {
-							$this->projects[$projData['project']] = $projObj;
-							unset($projObj);
-						}
-					}
-				}
-			} catch (Exception $e) {
-				GitPHP_DebugLog::GetInstance()->Log($e->getMessage());
-			}
-		}
-	}
-
-	/**
-	 * InstantiateProject
-	 *
-	 * Instantiates project object
-	 *
-	 * @access protected
-	 * @param string $proj project
-	 * @return mixed project
-	 */
-	protected function InstantiateProject($proj)
-	{
-		$found = false;
-		$projectSettings = null;
-		foreach ($this->projectConfig as $key => $projData) {
-			if (is_string($projData) && ($projData == $proj)) {
-				// Just flat array of project paths
-				$found = true;
-				break;
-			} else if (is_array($projData)) {
-				if (is_string($key) && !empty($key) && ($key == $proj)) {
-					// Project key pointing to data array
-					$found = true;
-					$projectSettings = $projData;
-					break;
-				}
-				if (isset($projData['project']) && ($projData['project'] == $proj)) {
-					// List of data arrays with projects inside
-					$found = true;
-					$projectSettings = $projData;
-					break;
-				}
-			}
-		}
-
-		if (!$found) {
-			return;
-		}
-
-		$projectObj = new GitPHP_Project($this->projectRoot, $proj);
-
-		$this->ApplyGlobalConfig($projectObj);
-
-		$this->ApplyGitConfig($projectObj);
-
-		if ($projectSettings != null)
-			$this->ApplyProjectSettings($projectObj, $projectSettings);
-
-		if ($this->projectSettings && isset($this->projectSettings[$proj])) {
-			$this->ApplyProjectSettings($projectObj, $this->projectSettings[$proj]);
-		}
-
-		return $projectObj;
-	}
-
-}
-

--- a/include/git/ProjectListArrayLegacy.class.php
+++ /dev/null
@@ -1,114 +1,1 @@
-<?php
-/**
- * GitPHP ProjectListArrayLegacy
- *
- * Lists all projects in a multidimensional array
- * Legacy array format
- *
- * @author Christopher Han <xiphux@gmail.com>
- * @copyright Copyright (c) 2010 Christopher Han
- * @package GitPHP
- * @subpackage Git
- */
 
-require_once(GITPHP_GITOBJECTDIR . 'ProjectListBase.class.php');
-require_once(GITPHP_GITOBJECTDIR . 'Project.class.php');
-
-define('GITPHP_NO_CATEGORY', 'none');
-
-/**
- * ProjectListArrayLegacy class
- *
- * @package GitPHP
- * @subpackage Git
- */
-class GitPHP_ProjectListArrayLegacy extends GitPHP_ProjectListBase
-{
-
-	/**
-	 * __construct
-	 *
-	 * constructor
-	 *
-	 * @param string $projectRoot project root
-	 * @param mixed $projectArray array to read
-	 * @throws Exception if parameter is not an array
-	 * @access public
-	 */
-	public function __construct($projectRoot, $projectArray)
-	{
-		if (!is_array($projectArray)) {
-			throw new Exception('An array of projects is required.');
-		}
-
-		$this->projectConfig = $projectArray;
-
-		parent::__construct($projectRoot);
-	}
-
-	/**
-	 * PopulateProjects
-	 *
-	 * Populates the internal list of projects
-	 *
-	 * @access protected
-	 * @throws Exception if file cannot be read
-	 */
-	protected function PopulateProjects()
-	{
-		foreach ($this->projectConfig as $cat => $plist) {
-			if (is_array($plist)) {
-				foreach ($plist as $pname => $ppath) {
-					try {
-						$projObj = $this->InstantiateProject($ppath);
-						if ($projObj) {
-							$this->projects[$ppath] = $projObj;
-							unset($projObj);
-						}
-					} catch (Exception $e) {
-						GitPHP_DebugLog::GetInstance()->Log($e->getMessage());
-					}
-				}
-			}
-		}
-	}
-
-	/**
-	 * InstantiateProject
-	 *
-	 * Instantiates project object
-	 *
-	 * @access protected
-	 * @param string $proj project
-	 * @return mixed project
-	 */
-	protected function InstantiateProject($proj)
-	{
-		$found = false;
-		$projectCat = GITPHP_NO_CATEGORY;
-		foreach ($this->projectConfig as $cat => $plist) {
-			if (is_array($plist) && (array_search($proj, $plist) !== false)) {
-				$found = true;
-				$projectCat = $cat;
-				break;
-			}
-		}
-
-		if (!$found) {
-			return;
-		}
-
-		$projectObj = new GitPHP_Project($this->projectRoot, $proj);
-
-		$this->ApplyGlobalConfig($projectObj);
-
-		$this->ApplyGitConfig($projectObj);
-
-		if ($projectCat != GITPHP_NO_CATEGORY)
-			$projectObj->SetCategory($projectCat);
-
-		return $projectObj;
-	}
-
-}
-

--- a/include/git/ProjectListBase.class.php
+++ /dev/null
@@ -1,527 +1,1 @@
-<?php
-/**
- * GitPHP ProjectListBase
- *
- * Base class that all projectlist classes extend
- *
- * @author Christopher Han <xiphux@gmail.com>
- * @copyright Copyright (c) 2010 Christopher Han
- * @package GitPHP
- * @subpackage Git
- */
 
-require_once(GITPHP_GITOBJECTDIR . 'Project.class.php');
-require_once(GITPHP_GITOBJECTDIR . 'GitConfig.class.php');
-
-define('GITPHP_SORT_PROJECT', 'project');
-define('GITPHP_SORT_DESCRIPTION', 'descr');
-define('GITPHP_SORT_OWNER', 'owner');
-define('GITPHP_SORT_AGE', 'age');
-
-/**
- * ProjectListBase class
- *
- * @package GitPHP
- * @subpackage Git
- * @abstract
- */
-abstract class GitPHP_ProjectListBase implements Iterator
-{
-	/**
-	 * projects
-	 *
-	 * Stores array of projects internally
-	 *
-	 * @access protected
-	 */
-	protected $projects;
-
-	/**
-	 * projectsLoaded
-	 *
-	 * Stores whether the list of projects has been loaded
-	 *
-	 * @access protected
-	 */
-	protected $projectsLoaded = false;
-
-	/**
-	 * projectConfig
-	 *
-	 * Stores the project configuration internally
-	 *
-	 * @access protected
-	 */
-	protected $projectConfig = null;
-
-	/**
-	 * projectSettings
-	 *
-	 * Stores the project settings internally
-	 *
-	 * @access protected
-	 */
-	protected $projectSettings = null;
-
-	/**
-	 * projectRoot
-	 *
-	 * Stores the project root internally
-	 *
-	 * @access protected
-	 */
-	protected $projectRoot = null;
-
-	/**
-	 * __construct
-	 *
-	 * Constructor
-	 *
-	 * @access public
-	 * @param string $projectRoot project root
-	 */
-	public function __construct($projectRoot)
-	{
-		$this->projects = array();
-		$this->projectRoot = GitPHP_Util::AddSlash($projectRoot);
-		if (empty($this->projectRoot)) {
-			throw new GitPHP_MessageException(__('A projectroot must be set in the config'), true, 500);
-		}
-		if (!is_dir($this->projectRoot)) {
-			throw new Exception(sprintf(__('%1$s is not a directory'), $this->projectRoot));
-		}
-
-	}
-
-	/**
-	 * HasProject
-	 *
-	 * Test if the projectlist contains
-	 * the given project
-	 *
-	 * @access public
-	 * @return boolean true if project exists in list
-	 * @param string $project the project string to find
-	 */
-	public function HasProject($project)
-	{
-		if (empty($project))
-			return false;
-
-		return isset($this->projects[$project]);
-	}
-
-	/**
-	 * GetProject
-	 *
-	 * Gets a particular project
-	 *
-	 * @access public
-	 * @return mixed project object or null
-	 * @param string $project the project to find
-	 */
-	public function GetProject($project)
-	{
-		if (empty($project))
-			return null;
-
-		if (isset($this->projects[$project]))
-			return $this->projects[$project];
-
-		if (!$this->projectsLoaded) {
-			$projObj = $this->InstantiateProject($project);
-			$this->projects[$project] = $projObj;
-			return $projObj;
-		}
-
-		return null;
-	}
-
-	/**
-	 * InstantiateProject
-	 *
-	 * Instantiates a project object
-	 *
-	 * @access protected
-	 * @param string $proj project
-	 * @return mixed project object
-	 */
-	protected function InstantiateProject($proj)
-	{
-		$project = new GitPHP_Project(GitPHP_Util::AddSlash($this->projectRoot), $proj);
-
-		$this->ApplyGlobalConfig($project);
-
-		$this->ApplyGitConfig($project);
-
-		if ($this->projectSettings && isset($this->projectSettings[$proj])) {
-			$this->ApplyProjectSettings($project, $this->projectSettings[$proj]);
-		}
-
-		return $project;
-	}
-
-	/**
-	 * GetConfig
-	 *
-	 * Gets the config defined for this ProjectList
-	 *
-	 * @access public
-	 */
-	public function GetConfig()
-	{
-		return $this->projectConfig;
-	}
-
-	/**
-	 * GetSettings
-	 *
-	 * Gets the settings applied to this projectlist
-	 *
-	 * @access public
-	 */
-	public function GetSettings()
-	{
-		return $this->projectSettings;
-	}
-
-	/**
-	 * ApplyGitConfig
-	 *
-	 * Reads the project's git config settings and applies them to the project
-	 *
-	 * @access protected
-	 * @param mixed $project project
-	 */
-	protected function ApplyGitConfig($project)
-	{
-		if (!$project)
-			return;
-
-		$config = null;
-		try {
-			$config = new GitPHP_GitConfig($project->GetPath() . '/config');
-		} catch (Exception $e) {
-			return;
-		}
-
-		if ($config->HasValue('gitphp.owner')) {
-			$project->SetOwner($config->GetValue('gitphp.owner'));
-		} else if ($config->HasValue('gitweb.owner')) {
-			$project->SetOwner($config->GetValue('gitweb.owner'));
-		}
-
-		if ($config->HasValue('gitphp.description')) {
-			$project->SetDescription($config->GetValue('gitphp.description'));
-		}
-
-		if ($config->HasValue('gitphp.category')) {
-			$project->SetCategory($config->GetValue('gitphp.category'));
-		}
-
-		if ($config->HasValue('gitphp.cloneurl')) {
-			$project->SetCloneUrl($config->GetValue('gitphp.cloneurl'));
-		}
-
-		if ($config->HasValue('gitphp.pushurl')) {
-			$project->SetPushUrl($config->GetValue('gitphp.pushurl'));
-		}
-
-		if ($config->HasValue('gitphp.bugurl')) {
-			$project->SetBugUrl($config->GetValue('gitphp.bugurl'));
-		}
-
-		if ($config->HasValue('gitphp.bugpattern')) {
-			$project->SetBugPattern($config->GetValue('gitphp.bugpattern'));
-		}
-
-		if ($config->HasValue('gitphp.website')) {
-			$project->SetWebsite($config->GetValue('gitphp.website'));
-		}
-
-		if ($config->HasValue('gitphp.compat')) {
-			$project->SetCompat($config->GetValue('gitphp.compat'));
-		}
-
-		if ($config->HasValue('core.abbrev')) {
-			$project->SetAbbreviateLength($config->GetValue('core.abbrev'));
-		}
-
-	}
-
-	/**
-	 * ApplyGlobalConfig
-	 *
-	 * Applies global config settings to a project
-	 *
-	 * @access protected
-	 * @param mixed $project project
-	 */
-	protected function ApplyGlobalConfig($project)
-	{
-		if (!$project)
-			return;
-
-		$config = GitPHP_Config::GetInstance();
-
-		if ($config->HasKey('cloneurl')) {
-			$project->SetCloneUrl(GitPHP_Util::AddSlash($config->GetValue('cloneurl'), false) . $project->GetProject());
-		}
-
-		if ($config->HasKey('pushurl')) {
-			$project->SetPushUrl(GitPHP_Util::AddSlash($config->GetValue('pushurl'), false) . $project->GetProject());
-		}
-
-		if ($config->HasKey('bugpattern')) {
-			$project->SetBugPattern($config->GetValue('bugpattern'));
-		}
-
-		if ($config->HasKey('bugurl')) {
-			$project->SetBugUrl($config->GetValue('bugurl'));
-		}
-
-		if ($config->HasKey('compat')) {
-			$project->SetCompat($config->GetValue('compat'));
-		}
-
-		if ($config->HasKey('uniqueabbrev')) {
-			$project->SetUniqueAbbreviation($config->GetValue('uniqueabbrev'));
-		}
-	}
-
-	/**
-	 * LoadProjects
-	 *
-	 * Loads all projects in the list
-	 *
-	 * @access public
-	 */
-	public function LoadProjects()
-	{
-		$this->PopulateProjects();
-
-		$this->projectsLoaded = true;
-
-		$this->Sort();
-
-		$this->ApplySettings();
-	}
-
-	/**
-	 * PopulateProjects
-	 *
-	 * Populates the internal list of projects
-	 *
-	 * @access protected
-	 */
-	abstract protected function PopulateProjects();
-
-	/**
-	 * rewind
-	 *
-	 * Rewinds the iterator
-	 */
-	function rewind()
-	{
-		return reset($this->projects);
-	}
-
-	/**
-	 * current
-	 *
-	 * Returns the current element in the array
-	 */
-	function current()
-	{
-		return current($this->projects);
-	}
-
-	/**
-	 * key
-	 *
-	 * Returns the current key
-	 */
-	function key()
-	{
-		return key($this->projects);
-	}
-
-	/**
-	 * next
-	 * 
-	 * Advance the pointer
-	 */
-	function next()
-	{
-		return next($this->projects);
-	}
-
-	/**
-	 * valid
-	 *
-	 * Test for a valid pointer
-	 */
-	function valid()
-	{
-		return key($this->projects) !== null;
-	}
-
-	/**
-	 * Sort
-	 *
-	 * Sorts the project list
-	 *
-	 * @access public
-	 * @param string $sortBy sort method
-	 */
-	public function Sort($sortBy = GITPHP_SORT_PROJECT)
-	{
-		switch ($sortBy) {
-			case GITPHP_SORT_DESCRIPTION:
-				uasort($this->projects, array('GitPHP_Project', 'CompareDescription'));
-				break;
-			case GITPHP_SORT_OWNER:
-				uasort($this->projects, array('GitPHP_Project', 'CompareOwner'));
-				break;
-			case GITPHP_SORT_AGE:
-				uasort($this->projects, array('GitPHP_Project', 'CompareAge'));
-				break;
-			case GITPHP_SORT_PROJECT:
-			default:
-				uasort($this->projects, array('GitPHP_Project', 'CompareProject'));
-				break;
-		}
-	}
-
-	/**
-	 * Count
-	 *
-	 * Gets the count of projects
-	 *
-	 * @access public
-	 * @return integer number of projects
-	 */
-	public function Count()
-	{
-		return count($this->projects);
-	}
-
-	/**
-	 * Filter
-	 *
-	 * Returns a filtered list of projects
-	 *
-	 * @access public
-	 * @param string $filter filter pattern
-	 * @return array array of filtered projects
-	 */
-	public function Filter($pattern = null)
-	{
-		if (empty($pattern))
-			return $this->projects;
-
-		$matches = array();
-
-		foreach ($this->projects as $proj) {
-			if ((stripos($proj->GetProject(), $pattern) !== false) ||
-			    (stripos($proj->GetDescription(), $pattern) !== false) ||
-			    (stripos($proj->GetOwner(), $pattern) !== false)) {
-			    	$matches[] = $proj;
-			}
-		}
-
-		return $matches;
-	}
-
-	/**
-	 * ApplyProjectSettings
-	 *
-	 * Applies override settings for a project
-	 *
-	 * @access protected
-	 * @param string $project the project object
-	 * @param array $projData project data array
-	 */
-	protected function ApplyProjectSettings($project, $projData)
-	{
-		if (!$project)
-			return;
-
-		if (isset($projData['category']) && is_string($projData['category'])) {
-			$project->SetCategory($projData['category']);
-		}
-		if (isset($projData['owner']) && is_string($projData['owner'])) {
-			$project->SetOwner($projData['owner']);
-		}
-		if (isset($projData['description']) && is_string($projData['description'])) {
-			$project->SetDescription($projData['description']);
-		}
-		if (isset($projData['cloneurl']) && is_string($projData['cloneurl'])) {
-			$project->SetCloneUrl($projData['cloneurl']);
-		}
-		if (isset($projData['pushurl']) && is_string($projData['pushurl'])) {
-			$project->SetPushUrl($projData['pushurl']);
-		}
-		if (isset($projData['bugpattern']) && is_string($projData['bugpattern'])) {
-			$project->SetBugPattern($projData['bugpattern']);
-		}
-		if (isset($projData['bugurl']) && is_string($projData['bugurl'])) {
-			$project->SetBugUrl($projData['bugurl']);
-		}
-		if (isset($projData['compat'])) {
-			$project->SetCompat($projData['compat']);
-		}
-		if (isset($projData['website']) && is_string($projData['website'])) {
-			$project->SetWebsite($projData['website']);
-		}
-	}
-
-	/**
-	 * SetSettings
-	 *
-	 * Sets a list of settings for the project list
-	 *
-	 * @access protected
-	 * @param array $settings the array of settings
-	 */
-	public function SetSettings($settings)
-	{
-		if ((!$settings) || (count($settings) < 1))
-			return;
-
-		$this->projectSettings = $settings;
-
-		$this->ApplySettings();
-	}
-
-	/**
-	 * ApplySettings
-	 *
-	 * Applies project settings to project list
-	 *
-	 * @access protected
-	 */
-	protected function ApplySettings()
-	{
-		if (!$this->projectSettings)
-			return;
-
-		if (count($this->projects) > 0) {
-			foreach ($this->projectSettings as $proj => $setting) {
-
-				if (empty($proj)) {
-					if (isset($setting['project']) && !empty($setting['project'])) {
-						$proj = $setting['project'];
-					}
-				}
-
-				if (!isset($this->projects[$proj]))
-					break;
-
-				$this->ApplyProjectSettings($this->projects[$proj], $setting);
-			}
-		}
-	}
-
-}
-

--- a/include/git/ProjectListDirectory.class.php
+++ /dev/null
@@ -1,159 +1,1 @@
-<?php
-/**
- * GitPHP ProjectListDirectory
- *
- * Lists all projects in a given directory
- *
- * @author Christopher Han <xiphux@gmail.com>
- * @copyright Copyright (c) 2010 Christopher Han
- * @package GitPHP
- * @subpackage Git
- */
 
-require_once(GITPHP_INCLUDEDIR . 'Config.class.php');
-require_once(GITPHP_GITOBJECTDIR . 'ProjectListBase.class.php');
-require_once(GITPHP_GITOBJECTDIR . 'Project.class.php');
-
-/**
- * ProjectListDirectory class
- *
- * @package GitPHP
- * @subpackage Git
- */
-class GitPHP_ProjectListDirectory extends GitPHP_ProjectListBase
-{
-
-	/**
-	 * exportedOnly
-	 *
-	 * Stores whether to only list exported projects
-	 *
-	 * @access protected
-	 */
-	protected $exportedOnly = false;
-
-	/**
-	 * __construct
-	 *
-	 * Constructor
-	 *
-	 * @access public
-	 * @param string $projectRoot project root
-	 * @param bool $exportedOnly whether to only allow exported projects
-	 */
-	public function __construct($projectRoot, $exportedOnly = false)
-	{
-		$this->exportedOnly = $exportedOnly;
-
-		parent::__construct($projectRoot);
-
-	}
-
-	/**
-	 * GetExportedOnly
-	 *
-	 * Gets whether this list only allows exported projects
-	 *
-	 * @access public
-	 */
-	public function GetExportedOnly()
-	{
-		return $this->exportedOnly;
-	}
-	
-	/**
-	 * PopulateProjects
-	 *
-	 * Populates the internal list of projects
-	 *
-	 * @access protected
-	 */
-	protected function PopulateProjects()
-	{
-		$this->RecurseDir(GitPHP_Util::AddSlash($this->projectRoot));
-	}
-
-	/**
-	 * RecurseDir
-	 *
-	 * Recursively searches for projects
-	 *
-	 * @param string $dir directory to recurse into
-	 */
-	private function RecurseDir($dir)
-	{
-		if (!(is_dir($dir) && is_readable($dir)))
-			return;
-
-		GitPHP_DebugLog::GetInstance()->Log(sprintf('Searching directory %1$s', $dir));
-
-		if ($dh = opendir($dir)) {
-			$trimlen = strlen(GitPHP_Util::AddSlash($this->projectRoot)) + 1;
-			while (($file = readdir($dh)) !== false) {
-				$fullPath = $dir . '/' . $file;
-				if ((strpos($file, '.') !== 0) && is_dir($fullPath)) {
-					if (is_file($fullPath . '/HEAD')) {
-						GitPHP_DebugLog::GetInstance()->Log(sprintf('Found project %1$s', $fullPath));
-						$projectPath = substr($fullPath, $trimlen);
-						if (!isset($this->projects[$projectPath])) {
-							$project = $this->InstantiateProject($projectPath);
-							if ($project) {
-								$this->projects[$projectPath] = $project;
-								unset($project);
-							}
-						}
-					} else {
-						$this->RecurseDir($fullPath);
-					}
-				} else {
-					GitPHP_DebugLog::GetInstance()->Log(sprintf('Skipping %1$s', $fullPath));
-				}
-			}
-			closedir($dh);
-		}
-	}
-
-	/**
-	 * InstantiateProject
-	 *
-	 * Instantiates project object
-	 *
-	 * @access protected
-	 * @param string $proj project
-	 * @return mixed project
-	 */
-	protected function InstantiateProject($proj)
-	{
-		try {
-
-			$project = new GitPHP_Project($this->projectRoot, $proj);
-
-			$category = trim(dirname($proj));
-			if (!(empty($category) || (strpos($category, '.') === 0))) {
-				$project->SetCategory($category);
-			}
-
-			if ($this->exportedOnly && !$project->GetDaemonEnabled()) {
-				GitPHP_DebugLog::GetInstance()->Log(sprintf('Project %1$s not enabled for export', $project->GetPath()));
-				return null;
-			}
-
-			$this->ApplyGlobalConfig($project);
-
-			$this->ApplyGitConfig($project);
-
-			if ($this->projectSettings && isset($this->projectSettings[$proj])) {
-				$this->ApplyProjectSettings($project, $this->projectSettings[$proj]);
-			}
-
-			return $project;
-
-		} catch (Exception $e) {
-			GitPHP_DebugLog::GetInstance()->Log($e->getMessage());
-		}
-
-		return null;
-	}
-
-}
-

--- a/include/git/ProjectListFile.class.php
+++ /dev/null
@@ -1,173 +1,1 @@
-<?php
-/**
- * GitPHP ProjectListFile
- *
- * Lists all projects in a given file
- *
- * @author Christopher Han <xiphux@gmail.com>
- * @copyright Copyright (c) 2010 Christopher Han
- * @package GitPHP
- * @subpackage Git
- */
 
-require_once(GITPHP_INCLUDEDIR . 'Config.class.php');
-require_once(GITPHP_GITOBJECTDIR . 'ProjectListBase.class.php');
-require_once(GITPHP_GITOBJECTDIR . 'Project.class.php');
-
-/**
- * ProjectListFile class
- *
- * @package GitPHP
- * @subpackage Git
- */
-class GitPHP_ProjectListFile extends GitPHP_ProjectListBase
-{
-
-	/**
-	 * fileContents
-	 *
-	 * Stores the contents of the project list file
-	 *
-	 * @access protected
-	 */
-	protected $fileContents = array();
-
-	/**
-	 * fileRead
-	 *
-	 * Stores whether the file has been read
-	 *
-	 * @access protected
-	 */
-	protected $fileRead = false;
-	
-	/**
-	 * __construct
-	 *
-	 * constructor
-	 *
-	 * @param string $projectRoot project root
-	 * @param string $projectFile file to read
-	 * @throws Exception if parameter is not a readable file
-	 * @access public
-	 */
-	public function __construct($projectRoot, $projectFile)
-	{
-		if (!(is_string($projectFile) && is_file($projectFile))) {
-			throw new Exception(sprintf(__('%1$s is not a file'), $projectFile));
-		}
-
-		$this->projectConfig = $projectFile;
-
-		parent::__construct($projectRoot);
-	}
-
-	/**
-	 * PopulateProjects
-	 *
-	 * Populates the internal list of projects
-	 *
-	 * @access protected
-	 * @throws Exception if file cannot be read
-	 */
-	protected function PopulateProjects()
-	{
-		if (!$this->fileRead)
-			$this->ReadFile();
-
-		foreach ($this->fileContents as $lineData) {
-			if (isset($lineData['project'])) {
-				$projObj = $this->InstantiateProject($lineData['project']);
-				if ($projObj) {
-					$this->projects[$lineData['project']] = $projObj;
-					unset($projObj);
-				}
-			}
-		}
-	}
-
-	/**
-	 * InstantiateProject
-	 *
-	 * Instantiates the project object
-	 *
-	 * @access protected
-	 * @param string $proj project
-	 * @return mixed project object
-	 */
-	protected function InstantiateProject($proj)
-	{
-		if (!$this->fileRead)
-			$this->ReadFile();
-
-		$found = false;
-		$owner = null;
-		foreach ($this->fileContents as $lineData) {
-			if (isset($lineData['project']) && ($lineData['project'] == $proj)) {
-				$projectRoot = GitPHP_Util::AddSlash($this->projectRoot);
-				if (is_file($projectRoot . $proj . '/HEAD')) {
-					$found = true;
-					if (isset($lineData['owner'])) {
-						$owner = $lineData['owner'];
-					}
-				} else {
-					GitPHP_DebugLog::GetInstance()->Log(sprintf('%1$s is not a git project', $projectRoot . $proj));
-				}
-				break;
-			}
-		}
-
-		if (!$found)
-			return null;
-
-		$projectObj = new GitPHP_Project($this->projectRoot, $proj);
-
-		$this->ApplyGlobalConfig($projectObj);
-
-		$this->ApplyGitConfig($projectObj);
-
-		if (!empty($owner))
-			$projectObj->SetOwner($owner);
-
-		if ($this->projectSettings && isset($this->projectSettings[$proj])) {
-			$this->ApplyProjectSettings($projectObj, $this->projectSettings[$proj]);
-		}
-
-		return $projectObj;
-	}
-
-	/**
-	 * ReadFile
-	 *
-	 * Reads the file contents
-	 *
-	 * @access protected
-	 */
-	protected function ReadFile()
-	{
-		$this->fileRead = true;
-
-		$fileString = file_get_contents($this->projectConfig);
-		
-		if ($fileString === false) {
-			throw new Exception(sprintf(__('Failed to open project list file %1$s'), $this->projectConfig));
-		}
-
-		$this->fileContents = array();
-
-		$fileLines = explode("\n", $fileString);
-		foreach ($fileLines as $line) {
-			if (preg_match('/^([^\s]+)(\s.+)?$/', $line, $regs)) {
-				$data = array();
-				$data['project'] = $regs[1];
-				$owner = trim($regs[2]);
-				if (!empty($owner)) {
-					$data['owner'] = $owner;
-				}
-				$this->fileContents[] = $data;
-			}
-		}
-	}
-
-}
-

--- a/include/git/ProjectListScmManager.class.php
+++ /dev/null
@@ -1,228 +1,1 @@
-<?php
-/**
- * GitPHP ProjectListScmManager
- *
- * Lists all projects in an scm-manager config file
- *
- * @author Christopher Han <xiphux@gmail.com>
- * @copyright Copyright (c) 2011 Christopher Han
- * @package GitPHP
- * @subpackage Git
- */
 
-require_once(GITPHP_INCLUDEDIR . 'Config.class.php');
-require_once(GITPHP_GITOBJECTDIR . 'ProjectListBase.class.php');
-require_once(GITPHP_GITOBJECTDIR . 'Project.class.php');
-
-/**
- * ProjectListScmManager class
- *
- * @package GitPHP
- * @subpackage Git
- */
-class GitPHP_ProjectListScmManager extends GitPHP_ProjectListBase
-{
-	/**
-	 * fileContents
-	 *
-	 * Stores the contents of the project config file
-	 *
-	 * @access protected
-	 */
-	protected $fileContents = array();
-
-	/**
-	 * fileRead
-	 *
-	 * Stores whether the file has been read
-	 *
-	 * @access protected
-	 */
-	protected $fileRead = false;
-	
-	/**
-	 * __construct
-	 *
-	 * constructor
-	 *
-	 * @param string $projectRoot project root
-	 * @param string $projectFile file to read
-	 * @throws Exception if parameter is not a readable file
-	 * @access public
-	 */
-	public function __construct($projectRoot, $projectFile)
-	{
-		if (!(is_string($projectFile) && is_file($projectFile))) {
-			throw new Exception(sprintf(__('%1$s is not a file'), $projectFile));
-		}
-
-		$this->projectConfig = $projectFile;
-
-		parent::__construct($projectRoot);
-	}
-
-	/**
-	 * PopulateProjects
-	 *
-	 * Populates the internal list of projects
-	 *
-	 * @access protected
-	 * @throws Exception if file cannot be read
-	 */
-	protected function PopulateProjects()
-	{
-		if (!$this->fileRead)
-			$this->ReadFile();
-
-		foreach ($this->fileContents as $projData) {
-			$projObj = $this->InstantiateProject($projData['name']);
-			if ($projObj) {
-				$this->projects[$projData['name']] = $projObj;
-				unset($projObj);
-			}
-		}
-	}
-
-	/**
-	 * InstantiateProject
-	 *
-	 * Instantiates the project object
-	 *
-	 * @access protected
-	 * @param string $proj project
-	 * @return mixed project object
-	 */
-	protected function InstantiateProject($proj)
-	{
-		if (!$this->fileRead)
-			$this->ReadFile();
-
-		$data = null;
-		$found = false;
-
-		foreach ($this->fileContents as $projData) {
-			if (isset($projData) && ($proj == $projData['name'])) {
-				$data = $projData;
-				$found = true;
-				break;
-			}
-		}
-
-		if (!$found)
-			return null;
-
-		if (!(isset($data['type']) && ($data['type'] == 'git'))) {
-			GitPHP_DebugLog::GetInstance()->Log(sprintf('%1$s is not a git project', $proj));
-			return null;
-		}
-
-		if (!(isset($data['public']) && ($data['public'] == true))) {
-			GitPHP_DebugLog::GetInstance()->Log(sprintf('%1$s is not public', $proj));
-			return null;
-		}
-
-		if (!is_file(GitPHP_Util::AddSlash($this->projectRoot) . $proj . '/HEAD')) {
-			GitPHP_DebugLog::GetInstance()->Log(sprintf('%1$s is not a git project', $proj));
-		}
-
-		$projectObj = new GitPHP_Project($this->projectRoot, $proj);
-
-		$this->ApplyGlobalConfig($projectObj);
-
-		$this->ApplyGitConfig($projectObj);
-
-		if (isset($data['owner']) && !empty($data['owner'])) {
-			$projectObj->SetOwner($data['owner']);
-		}
-
-		if (isset($data['description']) && !empty($data['description'])) {
-			$projectObj->SetDescription($data['description']);
-		}
-
-		if ($this->projectSettings && isset($this->projectSettings[$proj])) {
-			$this->ApplyProjectSettings($projectObj, $this->projectSettings[$proj]);
-		}
-
-		return $projectObj;
-	}
-
-	/**
-	 * ReadFile
-	 *
-	 * Reads the file contents
-	 *
-	 * @access private
-	 */
-	protected function ReadFile()
-	{
-		$this->fileRead = true;
-
-		$use_errors = libxml_use_internal_errors(true);
-
-		$xml = simplexml_load_file($this->projectConfig);
-
-		libxml_clear_errors();
-		libxml_use_internal_errors($use_errors);
-
-		if (!$xml) {
-			throw new Exception(sprintf('Could not load SCM manager config %1$s', $this->projectConfig));
-		}
-
-		foreach ($xml->repositories->repository as $repository) {
-			
-			$name = trim($repository->name);
-			if (empty($name))
-				continue;
-
-			$data = array();
-			$data['name'] = $name;
-			$data['type'] = $repository->type;
-			$data['public'] = ($repository->public == 'true');
-			
-			$owner = trim($repository->contact);
-			if (!empty($owner))
-				$data['owner'] = $owner;
-
-			$description = trim($repository->description);
-			if (!empty($description))
-				$data['description'] = $description;
-
-			$this->fileContents[] = $data;
-
-		}
-	}
-
-	/**
-	 * IsSCMManager
-	 *
-	 * Tests if this file is an SCM manager config file
-	 *
-	 * @access protected
-	 * @returns true if file is an SCM manager config
-	 */
-	public static function IsSCMManager($file)
-	{
-		if (empty($file))
-			return false;
-
-		if (!(is_string($file) && is_file($file)))
-			return false;
-
-		$use_errors = libxml_use_internal_errors(true);
-
-		$xml = simplexml_load_file($file);
-
-		libxml_clear_errors();
-		libxml_use_internal_errors($use_errors);
-
-		if (!$xml)
-			return false;
-
-		if ($xml->getName() !== 'repository-db')
-			return false;
-
-		return true;
-	}
-
-}
-

--- /dev/null
+++ b/include/git/projectlist/ProjectList.class.php
@@ -1,1 +1,114 @@
+<?php
+/**
+ * GitPHP ProjectList
+ *
+ * Project list singleton instance and factory
+ *
+ * @author Christopher Han <xiphux@gmail.com>
+ * @copyright Copyright (c) 2010 Christopher Han
+ * @package GitPHP
+ * @subpackage Git
+ */
 
+require_once(GITPHP_GITOBJECTDIR . 'projectlist/ProjectListDirectory.class.php');
+require_once(GITPHP_GITOBJECTDIR . 'projectlist/ProjectListFile.class.php');
+require_once(GITPHP_GITOBJECTDIR . 'projectlist/ProjectListArray.class.php');
+require_once(GITPHP_GITOBJECTDIR . 'projectlist/ProjectListArrayLegacy.class.php');
+require_once(GITPHP_GITOBJECTDIR . 'projectlist/ProjectListScmManager.class.php');
+
+/**
+ * ProjectList class
+ *
+ * @package GitPHP
+ * @subpackage Git
+ */
+class GitPHP_ProjectList
+{
+
+	/**
+	 * instance
+	 *
+	 * Stores the singleton instance of the projectlist
+	 *
+	 * @access protected
+	 * @static
+	 */
+	protected static $instance = null;
+
+	/**
+	 * GetInstance
+	 *
+	 * Returns the singleton instance
+	 *
+	 * @access public
+	 * @static
+	 * @return mixed instance of projectlist
+	 * @throws Exception if projectlist has not been instantiated yet
+	 */
+	public static function GetInstance()
+	{
+		return self::$instance;
+	}
+
+	/**
+	 * DestroyInstance
+	 *
+	 * Releases the singleton instance
+	 *
+	 * @access public
+	 * @static
+	 */
+	public static function DestroyInstance()
+	{
+		self::$instance = null;
+	}
+
+	/**
+	 * Instantiate
+	 *
+	 * Instantiates the singleton instance
+	 *
+	 * @access private
+	 * @static
+	 * @param string $file config file with git projects
+	 * @param boolean $legacy true if this is the legacy project config
+	 * @throws Exception if there was an error reading the file
+	 */
+	public static function Instantiate($file = null, $legacy = false)
+	{
+		if (self::$instance)
+			return;
+			
+		$projectRoot = GitPHP_Config::GetInstance()->GetValue('projectroot');
+
+
+		if (!empty($file) && is_file($file) && include($file)) {
+			if (isset($git_projects)) {
+				if (is_string($git_projects)) {
+					if (function_exists('simplexml_load_file') && GitPHP_ProjectListScmManager::IsSCMManager($git_projects)) {
+						self::$instance = new GitPHP_ProjectListScmManager($projectRoot, $git_projects);
+					} else {
+						self::$instance = new GitPHP_ProjectListFile($projectRoot, $git_projects);
+					}
+				} else if (is_array($git_projects)) {
+					if ($legacy) {
+						self::$instance = new GitPHP_ProjectListArrayLegacy($projectRoot, $git_projects);
+					} else {
+						self::$instance = new GitPHP_ProjectListArray($projectRoot, $git_projects);
+					}
+				}
+			}
+		}
+
+		if (!self::$instance) {
+
+			self::$instance = new GitPHP_ProjectListDirectory($projectRoot, GitPHP_Config::GetInstance()->GetValue('exportedonly', false));
+		}
+
+		if (isset($git_projects_settings) && !$legacy)
+			self::$instance->SetSettings($git_projects_settings);
+	}
+
+}
+
+

--- /dev/null
+++ b/include/git/projectlist/ProjectListArray.class.php
@@ -1,1 +1,143 @@
+<?php
+/**
+ * GitPHP ProjectListArray
+ *
+ * Lists all projects in a multidimensional array
+ *
+ * @author Christopher Han <xiphux@gmail.com>
+ * @copyright Copyright (c) 2010 Christopher Han
+ * @package GitPHP
+ * @subpackage Git
+ */
 
+require_once(GITPHP_GITOBJECTDIR . 'projectlist/ProjectListBase.class.php');
+require_once(GITPHP_GITOBJECTDIR . 'Project.class.php');
+
+/**
+ * ProjectListArray class
+ *
+ * @package GitPHP
+ * @subpackage Git
+ */
+class GitPHP_ProjectListArray extends GitPHP_ProjectListBase
+{
+
+	/**
+	 * __construct
+	 *
+	 * constructor
+	 *
+	 * @param string $projectRoot project root
+	 * @param mixed $projectArray array to read
+	 * @throws Exception if parameter is not an array
+	 * @access public
+	 */
+	public function __construct($projectRoot, $projectArray)
+	{
+		if (!is_array($projectArray)) {
+			throw new Exception('An array of projects is required');
+		}
+
+		$this->projectConfig = $projectArray;
+
+		parent::__construct($projectRoot);
+	}
+
+	/**
+	 * PopulateProjects
+	 *
+	 * Populates the internal list of projects
+	 *
+	 * @access protected
+	 * @throws Exception if file cannot be read
+	 */
+	protected function PopulateProjects()
+	{
+		foreach ($this->projectConfig as $proj => $projData) {
+			try {
+				if (is_string($projData)) {
+					// Just flat array of project paths
+					$projObj = $this->InstantiateProject($projData);
+					if ($projObj) {
+						$this->projects[$projData] = $projObj;
+						unset($projObj);
+					}
+				} else if (is_array($projData)) {
+					if (is_string($proj) && !empty($proj)) {
+						// Project key pointing to data array
+						$projObj = $this->InstantiateProject($proj);
+						if ($projObj) {
+							$this->projects[$proj] = $projObj;
+							unset($projObj);
+						}
+					} else if (isset($projData['project'])) {
+						// List of data arrays with projects inside
+						$projObj = $this->InstantiateProject($projData['project']);
+						if ($projObj) {
+							$this->projects[$projData['project']] = $projObj;
+							unset($projObj);
+						}
+					}
+				}
+			} catch (Exception $e) {
+				GitPHP_DebugLog::GetInstance()->Log($e->getMessage());
+			}
+		}
+	}
+
+	/**
+	 * InstantiateProject
+	 *
+	 * Instantiates project object
+	 *
+	 * @access protected
+	 * @param string $proj project
+	 * @return mixed project
+	 */
+	protected function InstantiateProject($proj)
+	{
+		$found = false;
+		$projectSettings = null;
+		foreach ($this->projectConfig as $key => $projData) {
+			if (is_string($projData) && ($projData == $proj)) {
+				// Just flat array of project paths
+				$found = true;
+				break;
+			} else if (is_array($projData)) {
+				if (is_string($key) && !empty($key) && ($key == $proj)) {
+					// Project key pointing to data array
+					$found = true;
+					$projectSettings = $projData;
+					break;
+				}
+				if (isset($projData['project']) && ($projData['project'] == $proj)) {
+					// List of data arrays with projects inside
+					$found = true;
+					$projectSettings = $projData;
+					break;
+				}
+			}
+		}
+
+		if (!$found) {
+			return;
+		}
+
+		$projectObj = new GitPHP_Project($this->projectRoot, $proj);
+
+		$this->ApplyGlobalConfig($projectObj);
+
+		$this->ApplyGitConfig($projectObj);
+
+		if ($projectSettings != null)
+			$this->ApplyProjectSettings($projectObj, $projectSettings);
+
+		if ($this->projectSettings && isset($this->projectSettings[$proj])) {
+			$this->ApplyProjectSettings($projectObj, $this->projectSettings[$proj]);
+		}
+
+		return $projectObj;
+	}
+
+}
+

--- /dev/null
+++ b/include/git/projectlist/ProjectListArrayLegacy.class.php
@@ -1,1 +1,114 @@
+<?php
+/**
+ * GitPHP ProjectListArrayLegacy
+ *
+ * Lists all projects in a multidimensional array
+ * Legacy array format
+ *
+ * @author Christopher Han <xiphux@gmail.com>
+ * @copyright Copyright (c) 2010 Christopher Han
+ * @package GitPHP
+ * @subpackage Git
+ */
 
+require_once(GITPHP_GITOBJECTDIR . 'projectlist/ProjectListBase.class.php');
+require_once(GITPHP_GITOBJECTDIR . 'Project.class.php');
+
+define('GITPHP_NO_CATEGORY', 'none');
+
+/**
+ * ProjectListArrayLegacy class
+ *
+ * @package GitPHP
+ * @subpackage Git
+ */
+class GitPHP_ProjectListArrayLegacy extends GitPHP_ProjectListBase
+{
+
+	/**
+	 * __construct
+	 *
+	 * constructor
+	 *
+	 * @param string $projectRoot project root
+	 * @param mixed $projectArray array to read
+	 * @throws Exception if parameter is not an array
+	 * @access public
+	 */
+	public function __construct($projectRoot, $projectArray)
+	{
+		if (!is_array($projectArray)) {
+			throw new Exception('An array of projects is required.');
+		}
+
+		$this->projectConfig = $projectArray;
+
+		parent::__construct($projectRoot);
+	}
+
+	/**
+	 * PopulateProjects
+	 *
+	 * Populates the internal list of projects
+	 *
+	 * @access protected
+	 * @throws Exception if file cannot be read
+	 */
+	protected function PopulateProjects()
+	{
+		foreach ($this->projectConfig as $cat => $plist) {
+			if (is_array($plist)) {
+				foreach ($plist as $pname => $ppath) {
+					try {
+						$projObj = $this->InstantiateProject($ppath);
+						if ($projObj) {
+							$this->projects[$ppath] = $projObj;
+							unset($projObj);
+						}
+					} catch (Exception $e) {
+						GitPHP_DebugLog::GetInstance()->Log($e->getMessage());
+					}
+				}
+			}
+		}
+	}
+
+	/**
+	 * InstantiateProject
+	 *
+	 * Instantiates project object
+	 *
+	 * @access protected
+	 * @param string $proj project
+	 * @return mixed project
+	 */
+	protected function InstantiateProject($proj)
+	{
+		$found = false;
+		$projectCat = GITPHP_NO_CATEGORY;
+		foreach ($this->projectConfig as $cat => $plist) {
+			if (is_array($plist) && (array_search($proj, $plist) !== false)) {
+				$found = true;
+				$projectCat = $cat;
+				break;
+			}
+		}
+
+		if (!$found) {
+			return;
+		}
+
+		$projectObj = new GitPHP_Project($this->projectRoot, $proj);
+
+		$this->ApplyGlobalConfig($projectObj);
+
+		$this->ApplyGitConfig($projectObj);
+
+		if ($projectCat != GITPHP_NO_CATEGORY)
+			$projectObj->SetCategory($projectCat);
+
+		return $projectObj;
+	}
+
+}
+

--- /dev/null
+++ b/include/git/projectlist/ProjectListBase.class.php
@@ -1,1 +1,527 @@
-
+<?php
+/**
+ * GitPHP ProjectListBase
+ *
+ * Base class that all projectlist classes extend
+ *
+ * @author Christopher Han <xiphux@gmail.com>
+ * @copyright Copyright (c) 2010 Christopher Han
+ * @package GitPHP
+ * @subpackage Git
+ */
+
+require_once(GITPHP_GITOBJECTDIR . 'Project.class.php');
+require_once(GITPHP_GITOBJECTDIR . 'GitConfig.class.php');
+
+define('GITPHP_SORT_PROJECT', 'project');
+define('GITPHP_SORT_DESCRIPTION', 'descr');
+define('GITPHP_SORT_OWNER', 'owner');
+define('GITPHP_SORT_AGE', 'age');
+
+/**
+ * ProjectListBase class
+ *
+ * @package GitPHP
+ * @subpackage Git
+ * @abstract
+ */
+abstract class GitPHP_ProjectListBase implements Iterator
+{
+	/**
+	 * projects
+	 *
+	 * Stores array of projects internally
+	 *
+	 * @access protected
+	 */
+	protected $projects;
+
+	/**
+	 * projectsLoaded
+	 *
+	 * Stores whether the list of projects has been loaded
+	 *
+	 * @access protected
+	 */
+	protected $projectsLoaded = false;
+
+	/**
+	 * projectConfig
+	 *
+	 * Stores the project configuration internally
+	 *
+	 * @access protected
+	 */
+	protected $projectConfig = null;
+
+	/**
+	 * projectSettings
+	 *
+	 * Stores the project settings internally
+	 *
+	 * @access protected
+	 */
+	protected $projectSettings = null;
+
+	/**
+	 * projectRoot
+	 *
+	 * Stores the project root internally
+	 *
+	 * @access protected
+	 */
+	protected $projectRoot = null;
+
+	/**
+	 * __construct
+	 *
+	 * Constructor
+	 *
+	 * @access public
+	 * @param string $projectRoot project root
+	 */
+	public function __construct($projectRoot)
+	{
+		$this->projects = array();
+		$this->projectRoot = GitPHP_Util::AddSlash($projectRoot);
+		if (empty($this->projectRoot)) {
+			throw new GitPHP_MessageException(__('A projectroot must be set in the config'), true, 500);
+		}
+		if (!is_dir($this->projectRoot)) {
+			throw new Exception(sprintf(__('%1$s is not a directory'), $this->projectRoot));
+		}
+
+	}
+
+	/**
+	 * HasProject
+	 *
+	 * Test if the projectlist contains
+	 * the given project
+	 *
+	 * @access public
+	 * @return boolean true if project exists in list
+	 * @param string $project the project string to find
+	 */
+	public function HasProject($project)
+	{
+		if (empty($project))
+			return false;
+
+		return isset($this->projects[$project]);
+	}
+
+	/**
+	 * GetProject
+	 *
+	 * Gets a particular project
+	 *
+	 * @access public
+	 * @return mixed project object or null
+	 * @param string $project the project to find
+	 */
+	public function GetProject($project)
+	{
+		if (empty($project))
+			return null;
+
+		if (isset($this->projects[$project]))
+			return $this->projects[$project];
+
+		if (!$this->projectsLoaded) {
+			$projObj = $this->InstantiateProject($project);
+			$this->projects[$project] = $projObj;
+			return $projObj;
+		}
+
+		return null;
+	}
+
+	/**
+	 * InstantiateProject
+	 *
+	 * Instantiates a project object
+	 *
+	 * @access protected
+	 * @param string $proj project
+	 * @return mixed project object
+	 */
+	protected function InstantiateProject($proj)
+	{
+		$project = new GitPHP_Project(GitPHP_Util::AddSlash($this->projectRoot), $proj);
+
+		$this->ApplyGlobalConfig($project);
+
+		$this->ApplyGitConfig($project);
+
+		if ($this->projectSettings && isset($this->projectSettings[$proj])) {
+			$this->ApplyProjectSettings($project, $this->projectSettings[$proj]);
+		}
+
+		return $project;
+	}
+
+	/**
+	 * GetConfig
+	 *
+	 * Gets the config defined for this ProjectList
+	 *
+	 * @access public
+	 */
+	public function GetConfig()
+	{
+		return $this->projectConfig;
+	}
+
+	/**
+	 * GetSettings
+	 *
+	 * Gets the settings applied to this projectlist
+	 *
+	 * @access public
+	 */
+	public function GetSettings()
+	{
+		return $this->projectSettings;
+	}
+
+	/**
+	 * ApplyGitConfig
+	 *
+	 * Reads the project's git config settings and applies them to the project
+	 *
+	 * @access protected
+	 * @param mixed $project project
+	 */
+	protected function ApplyGitConfig($project)
+	{
+		if (!$project)
+			return;
+
+		$config = null;
+		try {
+			$config = new GitPHP_GitConfig($project->GetPath() . '/config');
+		} catch (Exception $e) {
+			return;
+		}
+
+		if ($config->HasValue('gitphp.owner')) {
+			$project->SetOwner($config->GetValue('gitphp.owner'));
+		} else if ($config->HasValue('gitweb.owner')) {
+			$project->SetOwner($config->GetValue('gitweb.owner'));
+		}
+
+		if ($config->HasValue('gitphp.description')) {
+			$project->SetDescription($config->GetValue('gitphp.description'));
+		}
+
+		if ($config->HasValue('gitphp.category')) {
+			$project->SetCategory($config->GetValue('gitphp.category'));
+		}
+
+		if ($config->HasValue('gitphp.cloneurl')) {
+			$project->SetCloneUrl($config->GetValue('gitphp.cloneurl'));
+		}
+
+		if ($config->HasValue('gitphp.pushurl')) {
+			$project->SetPushUrl($config->GetValue('gitphp.pushurl'));
+		}
+
+		if ($config->HasValue('gitphp.bugurl')) {
+			$project->SetBugUrl($config->GetValue('gitphp.bugurl'));
+		}
+
+		if ($config->HasValue('gitphp.bugpattern')) {
+			$project->SetBugPattern($config->GetValue('gitphp.bugpattern'));
+		}
+
+		if ($config->HasValue('gitphp.website')) {
+			$project->SetWebsite($config->GetValue('gitphp.website'));
+		}
+
+		if ($config->HasValue('gitphp.compat')) {
+			$project->SetCompat($config->GetValue('gitphp.compat'));
+		}
+
+		if ($config->HasValue('core.abbrev')) {
+			$project->SetAbbreviateLength($config->GetValue('core.abbrev'));
+		}
+
+	}
+
+	/**
+	 * ApplyGlobalConfig
+	 *
+	 * Applies global config settings to a project
+	 *
+	 * @access protected
+	 * @param mixed $project project
+	 */
+	protected function ApplyGlobalConfig($project)
+	{
+		if (!$project)
+			return;
+
+		$config = GitPHP_Config::GetInstance();
+
+		if ($config->HasKey('cloneurl')) {
+			$project->SetCloneUrl(GitPHP_Util::AddSlash($config->GetValue('cloneurl'), false) . $project->GetProject());
+		}
+
+		if ($config->HasKey('pushurl')) {
+			$project->SetPushUrl(GitPHP_Util::AddSlash($config->GetValue('pushurl'), false) . $project->GetProject());
+		}
+
+		if ($config->HasKey('bugpattern')) {
+			$project->SetBugPattern($config->GetValue('bugpattern'));
+		}
+
+		if ($config->HasKey('bugurl')) {
+			$project->SetBugUrl($config->GetValue('bugurl'));
+		}
+
+		if ($config->HasKey('compat')) {
+			$project->SetCompat($config->GetValue('compat'));
+		}
+
+		if ($config->HasKey('uniqueabbrev')) {
+			$project->SetUniqueAbbreviation($config->GetValue('uniqueabbrev'));
+		}
+	}
+
+	/**
+	 * LoadProjects
+	 *
+	 * Loads all projects in the list
+	 *
+	 * @access public
+	 */
+	public function LoadProjects()
+	{
+		$this->PopulateProjects();
+
+		$this->projectsLoaded = true;
+
+		$this->Sort();
+
+		$this->ApplySettings();
+	}
+
+	/**
+	 * PopulateProjects
+	 *
+	 * Populates the internal list of projects
+	 *
+	 * @access protected
+	 */
+	abstract protected function PopulateProjects();
+
+	/**
+	 * rewind
+	 *
+	 * Rewinds the iterator
+	 */
+	function rewind()
+	{
+		return reset($this->projects);
+	}
+
+	/**
+	 * current
+	 *
+	 * Returns the current element in the array
+	 */
+	function current()
+	{
+		return current($this->projects);
+	}
+
+	/**
+	 * key
+	 *
+	 * Returns the current key
+	 */
+	function key()
+	{
+		return key($this->projects);
+	}
+
+	/**
+	 * next
+	 * 
+	 * Advance the pointer
+	 */
+	function next()
+	{
+		return next($this->projects);
+	}
+
+	/**
+	 * valid
+	 *
+	 * Test for a valid pointer
+	 */
+	function valid()
+	{
+		return key($this->projects) !== null;
+	}
+
+	/**
+	 * Sort
+	 *
+	 * Sorts the project list
+	 *
+	 * @access public
+	 * @param string $sortBy sort method
+	 */
+	public function Sort($sortBy = GITPHP_SORT_PROJECT)
+	{
+		switch ($sortBy) {
+			case GITPHP_SORT_DESCRIPTION:
+				uasort($this->projects, array('GitPHP_Project', 'CompareDescription'));
+				break;
+			case GITPHP_SORT_OWNER:
+				uasort($this->projects, array('GitPHP_Project', 'CompareOwner'));
+				break;
+			case GITPHP_SORT_AGE:
+				uasort($this->projects, array('GitPHP_Project', 'CompareAge'));
+				break;
+			case GITPHP_SORT_PROJECT:
+			default:
+				uasort($this->projects, array('GitPHP_Project', 'CompareProject'));
+				break;
+		}
+	}
+
+	/**
+	 * Count
+	 *
+	 * Gets the count of projects
+	 *
+	 * @access public
+	 * @return integer number of projects
+	 */
+	public function Count()
+	{
+		return count($this->projects);
+	}
+
+	/**
+	 * Filter
+	 *
+	 * Returns a filtered list of projects
+	 *
+	 * @access public
+	 * @param string $filter filter pattern
+	 * @return array array of filtered projects
+	 */
+	public function Filter($pattern = null)
+	{
+		if (empty($pattern))
+			return $this->projects;
+
+		$matches = array();
+
+		foreach ($this->projects as $proj) {
+			if ((stripos($proj->GetProject(), $pattern) !== false) ||
+			    (stripos($proj->GetDescription(), $pattern) !== false) ||
+			    (stripos($proj->GetOwner(), $pattern) !== false)) {
+			    	$matches[] = $proj;
+			}
+		}
+
+		return $matches;
+	}
+
+	/**
+	 * ApplyProjectSettings
+	 *
+	 * Applies override settings for a project
+	 *
+	 * @access protected
+	 * @param string $project the project object
+	 * @param array $projData project data array
+	 */
+	protected function ApplyProjectSettings($project, $projData)
+	{
+		if (!$project)
+			return;
+
+		if (isset($projData['category']) && is_string($projData['category'])) {
+			$project->SetCategory($projData['category']);
+		}
+		if (isset($projData['owner']) && is_string($projData['owner'])) {
+			$project->SetOwner($projData['owner']);
+		}
+		if (isset($projData['description']) && is_string($projData['description'])) {
+			$project->SetDescription($projData['description']);
+		}
+		if (isset($projData['cloneurl']) && is_string($projData['cloneurl'])) {
+			$project->SetCloneUrl($projData['cloneurl']);
+		}
+		if (isset($projData['pushurl']) && is_string($projData['pushurl'])) {
+			$project->SetPushUrl($projData['pushurl']);
+		}
+		if (isset($projData['bugpattern']) && is_string($projData['bugpattern'])) {
+			$project->SetBugPattern($projData['bugpattern']);
+		}
+		if (isset($projData['bugurl']) && is_string($projData['bugurl'])) {
+			$project->SetBugUrl($projData['bugurl']);
+		}
+		if (isset($projData['compat'])) {
+			$project->SetCompat($projData['compat']);
+		}
+		if (isset($projData['website']) && is_string($projData['website'])) {
+			$project->SetWebsite($projData['website']);
+		}
+	}
+
+	/**
+	 * SetSettings
+	 *
+	 * Sets a list of settings for the project list
+	 *
+	 * @access protected
+	 * @param array $settings the array of settings
+	 */
+	public function SetSettings($settings)
+	{
+		if ((!$settings) || (count($settings) < 1))
+			return;
+
+		$this->projectSettings = $settings;
+
+		$this->ApplySettings();
+	}
+
+	/**
+	 * ApplySettings
+	 *
+	 * Applies project settings to project list
+	 *
+	 * @access protected
+	 */
+	protected function ApplySettings()
+	{
+		if (!$this->projectSettings)
+			return;
+
+		if (count($this->projects) > 0) {
+			foreach ($this->projectSettings as $proj => $setting) {
+
+				if (empty($proj)) {
+					if (isset($setting['project']) && !empty($setting['project'])) {
+						$proj = $setting['project'];
+					}
+				}
+
+				if (!isset($this->projects[$proj]))
+					break;
+
+				$this->ApplyProjectSettings($this->projects[$proj], $setting);
+			}
+		}
+	}
+
+}
+

--- /dev/null
+++ b/include/git/projectlist/ProjectListDirectory.class.php
@@ -1,1 +1,159 @@
+<?php
+/**
+ * GitPHP ProjectListDirectory
+ *
+ * Lists all projects in a given directory
+ *
+ * @author Christopher Han <xiphux@gmail.com>
+ * @copyright Copyright (c) 2010 Christopher Han
+ * @package GitPHP
+ * @subpackage Git
+ */
 
+require_once(GITPHP_INCLUDEDIR . 'Config.class.php');
+require_once(GITPHP_GITOBJECTDIR . 'projectlist/ProjectListBase.class.php');
+require_once(GITPHP_GITOBJECTDIR . 'Project.class.php');
+
+/**
+ * ProjectListDirectory class
+ *
+ * @package GitPHP
+ * @subpackage Git
+ */
+class GitPHP_ProjectListDirectory extends GitPHP_ProjectListBase
+{
+
+	/**
+	 * exportedOnly
+	 *
+	 * Stores whether to only list exported projects
+	 *
+	 * @access protected
+	 */
+	protected $exportedOnly = false;
+
+	/**
+	 * __construct
+	 *
+	 * Constructor
+	 *
+	 * @access public
+	 * @param string $projectRoot project root
+	 * @param bool $exportedOnly whether to only allow exported projects
+	 */
+	public function __construct($projectRoot, $exportedOnly = false)
+	{
+		$this->exportedOnly = $exportedOnly;
+
+		parent::__construct($projectRoot);
+
+	}
+
+	/**
+	 * GetExportedOnly
+	 *
+	 * Gets whether this list only allows exported projects
+	 *
+	 * @access public
+	 */
+	public function GetExportedOnly()
+	{
+		return $this->exportedOnly;
+	}
+	
+	/**
+	 * PopulateProjects
+	 *
+	 * Populates the internal list of projects
+	 *
+	 * @access protected
+	 */
+	protected function PopulateProjects()
+	{
+		$this->RecurseDir(GitPHP_Util::AddSlash($this->projectRoot));
+	}
+
+	/**
+	 * RecurseDir
+	 *
+	 * Recursively searches for projects
+	 *
+	 * @param string $dir directory to recurse into
+	 */
+	private function RecurseDir($dir)
+	{
+		if (!(is_dir($dir) && is_readable($dir)))
+			return;
+
+		GitPHP_DebugLog::GetInstance()->Log(sprintf('Searching directory %1$s', $dir));
+
+		if ($dh = opendir($dir)) {
+			$trimlen = strlen(GitPHP_Util::AddSlash($this->projectRoot)) + 1;
+			while (($file = readdir($dh)) !== false) {
+				$fullPath = $dir . '/' . $file;
+				if ((strpos($file, '.') !== 0) && is_dir($fullPath)) {
+					if (is_file($fullPath . '/HEAD')) {
+						GitPHP_DebugLog::GetInstance()->Log(sprintf('Found project %1$s', $fullPath));
+						$projectPath = substr($fullPath, $trimlen);
+						if (!isset($this->projects[$projectPath])) {
+							$project = $this->InstantiateProject($projectPath);
+							if ($project) {
+								$this->projects[$projectPath] = $project;
+								unset($project);
+							}
+						}
+					} else {
+						$this->RecurseDir($fullPath);
+					}
+				} else {
+					GitPHP_DebugLog::GetInstance()->Log(sprintf('Skipping %1$s', $fullPath));
+				}
+			}
+			closedir($dh);
+		}
+	}
+
+	/**
+	 * InstantiateProject
+	 *
+	 * Instantiates project object
+	 *
+	 * @access protected
+	 * @param string $proj project
+	 * @return mixed project
+	 */
+	protected function InstantiateProject($proj)
+	{
+		try {
+
+			$project = new GitPHP_Project($this->projectRoot, $proj);
+
+			$category = trim(dirname($proj));
+			if (!(empty($category) || (strpos($category, '.') === 0))) {
+				$project->SetCategory($category);
+			}
+
+			if ($this->exportedOnly && !$project->GetDaemonEnabled()) {
+				GitPHP_DebugLog::GetInstance()->Log(sprintf('Project %1$s not enabled for export', $project->GetPath()));
+				return null;
+			}
+
+			$this->ApplyGlobalConfig($project);
+
+			$this->ApplyGitConfig($project);
+
+			if ($this->projectSettings && isset($this->projectSettings[$proj])) {
+				$this->ApplyProjectSettings($project, $this->projectSettings[$proj]);
+			}
+
+			return $project;
+
+		} catch (Exception $e) {
+			GitPHP_DebugLog::GetInstance()->Log($e->getMessage());
+		}
+
+		return null;
+	}
+
+}
+

--- /dev/null
+++ b/include/git/projectlist/ProjectListFile.class.php
@@ -1,1 +1,173 @@
+<?php
+/**
+ * GitPHP ProjectListFile
+ *
+ * Lists all projects in a given file
+ *
+ * @author Christopher Han <xiphux@gmail.com>
+ * @copyright Copyright (c) 2010 Christopher Han
+ * @package GitPHP
+ * @subpackage Git
+ */
 
+require_once(GITPHP_INCLUDEDIR . 'Config.class.php');
+require_once(GITPHP_GITOBJECTDIR . 'projectlist/ProjectListBase.class.php');
+require_once(GITPHP_GITOBJECTDIR . 'Project.class.php');
+
+/**
+ * ProjectListFile class
+ *
+ * @package GitPHP
+ * @subpackage Git
+ */
+class GitPHP_ProjectListFile extends GitPHP_ProjectListBase
+{
+
+	/**
+	 * fileContents
+	 *
+	 * Stores the contents of the project list file
+	 *
+	 * @access protected
+	 */
+	protected $fileContents = array();
+
+	/**
+	 * fileRead
+	 *
+	 * Stores whether the file has been read
+	 *
+	 * @access protected
+	 */
+	protected $fileRead = false;
+	
+	/**
+	 * __construct
+	 *
+	 * constructor
+	 *
+	 * @param string $projectRoot project root
+	 * @param string $projectFile file to read
+	 * @throws Exception if parameter is not a readable file
+	 * @access public
+	 */
+	public function __construct($projectRoot, $projectFile)
+	{
+		if (!(is_string($projectFile) && is_file($projectFile))) {
+			throw new Exception(sprintf(__('%1$s is not a file'), $projectFile));
+		}
+
+		$this->projectConfig = $projectFile;
+
+		parent::__construct($projectRoot);
+	}
+
+	/**
+	 * PopulateProjects
+	 *
+	 * Populates the internal list of projects
+	 *
+	 * @access protected
+	 * @throws Exception if file cannot be read
+	 */
+	protected function PopulateProjects()
+	{
+		if (!$this->fileRead)
+			$this->ReadFile();
+
+		foreach ($this->fileContents as $lineData) {
+			if (isset($lineData['project'])) {
+				$projObj = $this->InstantiateProject($lineData['project']);
+				if ($projObj) {
+					$this->projects[$lineData['project']] = $projObj;
+					unset($projObj);
+				}
+			}
+		}
+	}
+
+	/**
+	 * InstantiateProject
+	 *
+	 * Instantiates the project object
+	 *
+	 * @access protected
+	 * @param string $proj project
+	 * @return mixed project object
+	 */
+	protected function InstantiateProject($proj)
+	{
+		if (!$this->fileRead)
+			$this->ReadFile();
+
+		$found = false;
+		$owner = null;
+		foreach ($this->fileContents as $lineData) {
+			if (isset($lineData['project']) && ($lineData['project'] == $proj)) {
+				$projectRoot = GitPHP_Util::AddSlash($this->projectRoot);
+				if (is_file($projectRoot . $proj . '/HEAD')) {
+					$found = true;
+					if (isset($lineData['owner'])) {
+						$owner = $lineData['owner'];
+					}
+				} else {
+					GitPHP_DebugLog::GetInstance()->Log(sprintf('%1$s is not a git project', $projectRoot . $proj));
+				}
+				break;
+			}
+		}
+
+		if (!$found)
+			return null;
+
+		$projectObj = new GitPHP_Project($this->projectRoot, $proj);
+
+		$this->ApplyGlobalConfig($projectObj);
+
+		$this->ApplyGitConfig($projectObj);
+
+		if (!empty($owner))
+			$projectObj->SetOwner($owner);
+
+		if ($this->projectSettings && isset($this->projectSettings[$proj])) {
+			$this->ApplyProjectSettings($projectObj, $this->projectSettings[$proj]);
+		}
+
+		return $projectObj;
+	}
+
+	/**
+	 * ReadFile
+	 *
+	 * Reads the file contents
+	 *
+	 * @access protected
+	 */
+	protected function ReadFile()
+	{
+		$this->fileRead = true;
+
+		$fileString = file_get_contents($this->projectConfig);
+		
+		if ($fileString === false) {
+			throw new Exception(sprintf(__('Failed to open project list file %1$s'), $this->projectConfig));
+		}
+
+		$this->fileContents = array();
+
+		$fileLines = explode("\n", $fileString);
+		foreach ($fileLines as $line) {
+			if (preg_match('/^([^\s]+)(\s.+)?$/', $line, $regs)) {
+				$data = array();
+				$data['project'] = $regs[1];
+				$owner = trim($regs[2]);
+				if (!empty($owner)) {
+					$data['owner'] = $owner;
+				}
+				$this->fileContents[] = $data;
+			}
+		}
+	}
+
+}
+

--- /dev/null
+++ b/include/git/projectlist/ProjectListScmManager.class.php
@@ -1,1 +1,228 @@
-
+<?php
+/**
+ * GitPHP ProjectListScmManager
+ *
+ * Lists all projects in an scm-manager config file
+ *
+ * @author Christopher Han <xiphux@gmail.com>
+ * @copyright Copyright (c) 2011 Christopher Han
+ * @package GitPHP
+ * @subpackage Git
+ */
+
+require_once(GITPHP_INCLUDEDIR . 'Config.class.php');
+require_once(GITPHP_GITOBJECTDIR . 'projectlist/ProjectListBase.class.php');
+require_once(GITPHP_GITOBJECTDIR . 'Project.class.php');
+
+/**
+ * ProjectListScmManager class
+ *
+ * @package GitPHP
+ * @subpackage Git
+ */
+class GitPHP_ProjectListScmManager extends GitPHP_ProjectListBase
+{
+	/**
+	 * fileContents
+	 *
+	 * Stores the contents of the project config file
+	 *
+	 * @access protected
+	 */
+	protected $fileContents = array();
+
+	/**
+	 * fileRead
+	 *
+	 * Stores whether the file has been read
+	 *
+	 * @access protected
+	 */
+	protected $fileRead = false;
+	
+	/**
+	 * __construct
+	 *
+	 * constructor
+	 *
+	 * @param string $projectRoot project root
+	 * @param string $projectFile file to read
+	 * @throws Exception if parameter is not a readable file
+	 * @access public
+	 */
+	public function __construct($projectRoot, $projectFile)
+	{
+		if (!(is_string($projectFile) && is_file($projectFile))) {
+			throw new Exception(sprintf(__('%1$s is not a file'), $projectFile));
+		}
+
+		$this->projectConfig = $projectFile;
+
+		parent::__construct($projectRoot);
+	}
+
+	/**
+	 * PopulateProjects
+	 *
+	 * Populates the internal list of projects
+	 *
+	 * @access protected
+	 * @throws Exception if file cannot be read
+	 */
+	protected function PopulateProjects()
+	{
+		if (!$this->fileRead)
+			$this->ReadFile();
+
+		foreach ($this->fileContents as $projData) {
+			$projObj = $this->InstantiateProject($projData['name']);
+			if ($projObj) {
+				$this->projects[$projData['name']] = $projObj;
+				unset($projObj);
+			}
+		}
+	}
+
+	/**
+	 * InstantiateProject
+	 *
+	 * Instantiates the project object
+	 *
+	 * @access protected
+	 * @param string $proj project
+	 * @return mixed project object
+	 */
+	protected function InstantiateProject($proj)
+	{
+		if (!$this->fileRead)
+			$this->ReadFile();
+
+		$data = null;
+		$found = false;
+
+		foreach ($this->fileContents as $projData) {
+			if (isset($projData) && ($proj == $projData['name'])) {
+				$data = $projData;
+				$found = true;
+				break;
+			}
+		}
+
+		if (!$found)
+			return null;
+
+		if (!(isset($data['type']) && ($data['type'] == 'git'))) {
+			GitPHP_DebugLog::GetInstance()->Log(sprintf('%1$s is not a git project', $proj));
+			return null;
+		}
+
+		if (!(isset($data['public']) && ($data['public'] == true))) {
+			GitPHP_DebugLog::GetInstance()->Log(sprintf('%1$s is not public', $proj));
+			return null;
+		}
+
+		if (!is_file(GitPHP_Util::AddSlash($this->projectRoot) . $proj . '/HEAD')) {
+			GitPHP_DebugLog::GetInstance()->Log(sprintf('%1$s is not a git project', $proj));
+		}
+
+		$projectObj = new GitPHP_Project($this->projectRoot, $proj);
+
+		$this->ApplyGlobalConfig($projectObj);
+
+		$this->ApplyGitConfig($projectObj);
+
+		if (isset($data['owner']) && !empty($data['owner'])) {
+			$projectObj->SetOwner($data['owner']);
+		}
+
+		if (isset($data['description']) && !empty($data['description'])) {
+			$projectObj->SetDescription($data['description']);
+		}
+
+		if ($this->projectSettings && isset($this->projectSettings[$proj])) {
+			$this->ApplyProjectSettings($projectObj, $this->projectSettings[$proj]);
+		}
+
+		return $projectObj;
+	}
+
+	/**
+	 * ReadFile
+	 *
+	 * Reads the file contents
+	 *
+	 * @access private
+	 */
+	protected function ReadFile()
+	{
+		$this->fileRead = true;
+
+		$use_errors = libxml_use_internal_errors(true);
+
+		$xml = simplexml_load_file($this->projectConfig);
+
+		libxml_clear_errors();
+		libxml_use_internal_errors($use_errors);
+
+		if (!$xml) {
+			throw new Exception(sprintf('Could not load SCM manager config %1$s', $this->projectConfig));
+		}
+
+		foreach ($xml->repositories->repository as $repository) {
+			
+			$name = trim($repository->name);
+			if (empty($name))
+				continue;
+
+			$data = array();
+			$data['name'] = $name;
+			$data['type'] = $repository->type;
+			$data['public'] = ($repository->public == 'true');
+			
+			$owner = trim($repository->contact);
+			if (!empty($owner))
+				$data['owner'] = $owner;
+
+			$description = trim($repository->description);
+			if (!empty($description))
+				$data['description'] = $description;
+
+			$this->fileContents[] = $data;
+
+		}
+	}
+
+	/**
+	 * IsSCMManager
+	 *
+	 * Tests if this file is an SCM manager config file
+	 *
+	 * @access protected
+	 * @returns true if file is an SCM manager config
+	 */
+	public static function IsSCMManager($file)
+	{
+		if (empty($file))
+			return false;
+
+		if (!(is_string($file) && is_file($file)))
+			return false;
+
+		$use_errors = libxml_use_internal_errors(true);
+
+		$xml = simplexml_load_file($file);
+
+		libxml_clear_errors();
+		libxml_use_internal_errors($use_errors);
+
+		if (!$xml)
+			return false;
+
+		if ($xml->getName() !== 'repository-db')
+			return false;
+
+		return true;
+	}
+
+}
+

file:a/index.php -> file:b/index.php
--- a/index.php
+++ b/index.php
@@ -45,7 +45,7 @@
 
 require_once(GITPHP_INCLUDEDIR . 'DebugLog.class.php');
 
-require_once(GITPHP_GITOBJECTDIR . 'ProjectList.class.php');
+require_once(GITPHP_GITOBJECTDIR . 'projectlist/ProjectList.class.php');
 
 require_once(GITPHP_INCLUDEDIR . 'MessageException.class.php');
 

comments