view admin/application/libraries/hgphp.php @ 44:46961a5f545c

Pulled the OFL out of hgphp and hgconf2ini method signatures in favor of 'transaction' order of method calls
author joshjcarrier
date Wed, 09 Jun 2010 00:59:15 -0700
parents 2cf4300b3541
children
line wrap: on
line source

<?php if (!defined('BASEPATH')) exit('No direct script access allowed');

/**
 * CodeIgniter Mercurial Repository Management class
 * 
 * Scans file system and Mercurial projects directory, allowing
 * web-based manipulation of the hgweb.config and hgrc configuration files.
 * 
 * Depends on the HgConf2Ini library.
 * 
 * @package        	CodeIgniter
 * @subpackage    	Libraries
 * @category    	Libraries
 * @author 			Josh Carrier
 * @link			blog.joshjcarrier.com
 * 
 */
class HgPHP
{
	private $_ci;
	private $_ofl_lock_hgweb;
	private $_ofl_lock_hgrc;
	
	function __construct($config = array())
	{
		$this->_ci =& get_instance();
		if(!empty($config))
		{
			$this->initialize($config);
		}
		else
		{
			$this->_ci->config->load('hgphp', TRUE);
			$this->initialize($this->_ci->config->item('hgphp'));
		}
		
		$this->_ci->lang->load('hgphp');
		$this->_ci->load->library('hgconf2ini');
        log_message('debug', 'HgPHP class Initialized');
	}
	
	function initialize($config = array())
	{
		foreach($config as $key=>$val)
		{
			$this->{'_'.$key} = $val;
		}
	}
	
	/**
	 * Public accessors - hgweb.config
	 */
	
	/**
	 * Associates OFL locks
	 */
	function start_transaction(&$ofl_lock_hgweb, &$ofl_lock_hgrc)
	{
		$this->_ofl_lock_hgweb = &$ofl_lock_hgweb;
		$this->_ofl_lock_hgrc = &$ofl_lock_hgrc;
	}
	
	/**
	 * Disassociates OFL locks
	 */
	function end_transaction()
	{
		$dummy = '';
		$this->_ofl_lock_hgweb = &$dummy;
		$this->_ofl_lock_hgrc = &$dummy;
	}
	
	/**
	 * Returns a list of available repositories.
	 * Will show up if: detected in repo directory
	 * Will have status "enabled" if: detected in hgweb.config
	 * 
	 * @return an array of 0 or more detected repositories
	 */
	function lsdir()
	{
		$realdir = $this->__realdirscan();
		$this->_ci->hgconf2ini->register_OFL($this->_ofl_lock_hgweb);
		$hgwebdir_compat = $this->_ci->hgconf2ini->getHgWebDirCollections();
				
		$allrepo = $realdir;
		if(!is_integer($hgwebdir_compat))
		{
			$allrepo = array_merge($realdir, $hgwebdir_compat);
		}
		else
		{
			// error code
			return $hgwebdir_compat;
		}
		
		$hgrepos = array();
		foreach($allrepo as $repo)
		{
			$hgrepos[$repo]['name'] = $repo;

			if(isset($realdir[$repo]) && isset($hgwebdir_compat[$repo]))
			{
				$hgrepos[$repo]['status'] = HGPHP_REPO_STATUS_ENABLED;
			}
			else if(isset($realdir[$repo]) && !isset($hgwebdir_compat[$repo]))
			{
				$hgrepos[$repo]['status'] = HGPHP_REPO_STATUS_DISABLED;
			}
			else if(!isset($realdir[$repo]) && isset($hgwebdir_compat[$repo]))
			{
				$hgrepos[$repo]['status'] = HGPHP_REPO_STATUS_MISSING;
			}
		}
		
		return $hgrepos;
	}
	
	/**
	 * Create a Hg repository by:
	 * - adding an entry in hgrc if not present
	 * - creating a bare repository if not present
	 * @param r_name name of the new repository to create, used as folder name
	 * @return status code
	 */
	function create_repository($r_name)
	{
		if(!$this->can_create($r_name))
		{
			return HGPHP_ERR_PERM_USR;
		}
		
		$create_status = HGPHP_OK;
		
		$lsdir = $this->_ci->hgconf2ini->getHgWebDirCollections(); 

		// simplifies repo list into array where name is both key and value
		// this is how hgweb.config wants it
		$existingdir = array();
		
		if(!is_integer($lsdir))
		{
			$existingdir = array_keys($lsdir);
		}
		else
		{
			// error code
			return $lsdir;
		}
		
		$tempexistingdir = array();
		foreach($existingdir as $repo_name)
		{
			$tempexistingdir[$repo_name] = $repo_name;
		}
		$existingdir = $tempexistingdir;
		
		// not registered in hgweb.config
		if(!isset($lsdir[$r_name]))
		{
			// edit the directory
			$existingdir[$r_name] = $r_name;
			$this->_ci->hgconf2ini->register_OFL($this->_ofl_lock_hgweb);
			$create_status = $this->_ci->hgconf2ini->setHgWebDirCollections($existingdir);
			if($create_status == HGPHP_OK)
			{
				// then create the repository
				$this->_ci->hgconf2ini->register_OFL($this->_ofl_lock_hgrc);
				$create_status = $this->create_repository_dir($r_name);
			}
		}
		else
		{
			// repository already exists
			$create_status = HGPHP_ERR_FS_PREEXISTS;
		}
		return $create_status;
	}
	
	/**
	 * update_repository
	 * Update repository's hgrc
	 * @param r_name name of the repository to update hgrc for
	 * @param hgrc_data array representing new hgrc file
	 * @return status code
	 */
	function update_repository($r_name, $hgrc_data)
	{
		if(!$this->can_update($r_name))
		{
			return HGPHP_ERR_PERM_USR;
		}
		$this->_ci->hgconf2ini->register_OFL($this->_ofl_lock_hgrc);
		return $this->_ci->hgconf2ini->setHGRC($r_name, $hgrc_data);
	}
	
	/**
	 * delete_repository
	 * Deletes a repository from the file system and unregisters it from hgweb.config
	 * @param r_name name of the repo to delete permanently
	 * @return status code
	 */
	function delete_repository($r_name)
	{
		if(!$this->can_delete($r_name))
		{
			return HGPHP_ERR_PERM_USR;
		}
		
		$del_status = HGPHP_OK;
		$lsdir = $this->_ci->hgconf2ini->getHgWebDirCollections(); 
		
		// simplifies repo list into array where name is both key and value
		// this is how hgweb.config wants it
		$existingdir = array_keys($lsdir);
		$tempexistingdir = array();
		foreach($existingdir as $repo_name)
		{
			$tempexistingdir[$repo_name] = $repo_name;
		}
		$existingdir = $tempexistingdir;
		
		if(isset($lsdir[$r_name]))
		{
			// edit the directory
			unset($existingdir[$r_name]);
			// remove hgweb.config
			$this->_ci->hgconf2ini->register_OFL($this->_ofl_lock_hgweb);
			$del_status = $this->_ci->hgconf2ini->setHgWebDirCollections($existingdir);
				
			// remove from hgweb.config
			if($del_status == HGPHP_OK)
			{
				// existing filesystem is not missing, thus needs to be deleted
				if($lsdir[$r_name]['status'] != HGPHP_REPO_STATUS_MISSING)
				{
					// unregister hgrc from transaction manager to keep index small
					$this->_ci->hgconf2ini->register_OFL($this->_ofl_lock_hgrc);
					$del_status = $this->_ci->hgconf2ini->unlinkHGRC($r_name);
					
					if($del_status == HGPHP_OK)
					{
						$del_status = $this->delete_repository_dir($r_name);
					}
				}
			}
		}
		else
		{
			$del_status = HGPHP_ERR_FS_PREEXISTS;
		}
		return $del_status;
	}
	
	/**
	 * stat_repository
	 * Returns the HGRC represented as an array for the specified repository
	 * @param r_name name of the project whose hgrc to retrieve
	 * @return array representing hgrc or status code
	 */
	function stat_repository($r_name)
	{
		if(!$this->can_view($r_name))
		{
			return HGPHP_ERR_PERM_USR;
		}
		$this->_ci->hgconf2ini->register_OFL($this->_ofl_lock_hgrc);
		return $this->_ci->hgconf2ini->getHGRC($r_name);
	}
	 

	/**
	 * can_create
	 * Checks if user has permissions to create this repository.
	 * Requires view permission.
	 * @param r_name name of repository wanting to be created
	 * @return true if allowed
	 */
	function can_create($r_name)
	{
		return $this->can_view($r_name) && $this->_hgwebconf_allow_repo_create;
	}
	
	/**
	 * can_update
	 * Checks if user has permissions to update this repository
	 * Requires view permission.
	 * @param r_name name of repository wanting to be updated
	 * @return true if allowed
	 */
	function can_update($r_name)
	{
		return $this->can_view($r_name) && $this->_hgwebconf_allow_repo_update;
	}
	
	/**
	 * can_create
	 * Checks if user has permissions to view this repository
	 * @param r_name name of repository wanting to be created
	 * @return true if allowed
	 */
	function can_view($r_name)
	{
		return $this->_hgwebconf_allow_repo_view;
	}
	
	/**
	 * can_delete
	 * Checks if user has permissions to delete this repository
	 * Requires view permission.
	 * @param r_name name of repository wanting to be deleted
	 * @return true if allowed
	 */
	function can_delete($r_name)
	{
		return $this->can_view($r_name) && $this->_hgwebconf_allow_repo_delete;
	}
	 
	/**
	 * create_repository_dir
	 * Creates a whole repository directory, with hgrc
	 * @param r_name name of repository to create directory for
	 * @return status code
	 */
	function create_repository_dir($r_name)
	{
		if(!$this->can_create($r_name))
		{
			return HGPHP_ERR_PERM_USR;
		}
		
		// create repo directory structure recursively
		$create_status = mkdir($this->_repositories_abs_dir . $r_name . '/.hg/store/data/', 0755, TRUE);
		
		if($create_status == TRUE)
		{
			// create hgrc
			$this->_ci->hgconf2ini->register_OFL($this->_ofl_lock_hgrc);
			$create_status = $this->_ci->hgconf2ini->touchHGRC($r_name);
		}
		else
		{
			// system couldn't make directory
			$create_status = HGPHP_ERR_PERM_SYS_REPODIR;
		}
		return $create_status;
	}
	
	/**
	 * delete_repository_dir
	 * Deletes a whole repository directory including hgrc and data files
	 * @param r_name name of repository to delete directory of
	 * @return status code
	 */
	function delete_repository_dir($repository_name)
	{
		if(!$this->can_delete($repository_name))
		{
			return HGPHP_ERR_PERM_USR;
		}
		
		$cd = $this->_repositories_abs_dir . $repository_name;
		$this->recursiveDelete($cd . '/.hg/');
		$del_status = $this->recursiveDelete($cd);
		if($del_status == TRUE)
		{
			$del_status = HGPHP_OK;
		}
		else
		{
			$del_status = HGPHP_ERR_PERM_SYS_REPODIR;
		}
		return $del_status;
	}
	
	/**
	 * Performs a real directory scan where the projects are suppose to reside.
	 * 
	 * @return the array containing 0 or more valid directories
	 */
	function __realdirscan()
	{
		$this->_ci->load->helper('directory');
		
		$realdir = directory_map($this->_repositories_abs_dir, TRUE);
		
		$verifiedrealdir = array();
		if(is_array($realdir))
		{
			foreach($realdir as $file)
			{
				// checks if we detected a folder
				if(is_dir($this->_repositories_abs_dir . $file))
				{
					$verifiedrealdir[$file] = $file;
				}
			}
		}
		
		return $verifiedrealdir;
	}
	
	/**
     * Delete a file or recursively delete a directory
     *
     * @param string $str Path to file or directory
     * @return true on successful deletion
     */
    function recursiveDelete($str){
        if(is_file($str)){
            return @unlink($str);
        }
        elseif(is_dir($str)){
            $scan = glob(rtrim($str,'/').'/*');
            foreach($scan as $index=>$path){
                $this->recursiveDelete($path);
            }
            return @rmdir($str);
        }
    }
}