view admin/application/libraries/hgphp.php @ 7:9790c4a95b14

delete repo&data + basic UI support for delete
author joshjcarrier
date Wed, 12 May 2010 23:51:08 -0700
parents admin/application/libraries/hg_confparser.php@6e512b1d054f
children 6215bb22f3d3
line wrap: on
line source

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

/**
 * CodeIgniter Mercurial Config Parser Class
 * 
 * Scans file system and Mercurial projects directory, allowing
 * web-based manipulation of the hgweb configuration files.
 * 
 * @package        	CodeIgniter
 * @subpackage    	Libraries
 * @category    	Libraries
 * @author 			Josh Carrier
 * @link			blog.joshjcarrier.com
 * 
 */
class HgPHP
{
	private $_ci;
	
	function __construct($config = array())
	{
		if(!empty($config))
		{
			$this->initialize($config);
		}
		
		$this->_ci =& get_instance();
		$this->_ci->lang->load('hgphp');
        log_message('debug', 'Hg_ConfParser class Initialized');
	}
	
	function initialize($config = array())
	{
		foreach($config as $key=>$val)
		{
			$this->{'_'.$key} = $val;
			//echo $key . "<br\>";
		}
	}
	
	/**
	 * Public accessors - hgweb.config
	 */
	
	/**
	 * 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();
		$hgwebdir_compat = $this->__hgwebconfscan();
				
		// FIXME test no directory cases
		$allrepo = array_merge($realdir, $hgwebdir_compat);
		
		$hgrepos = array();
		foreach($allrepo as $repo)
		{
			$hgrepos[$repo]['name'] = $repo;

			if(isset($realdir[$repo]) && isset($hgwebdir_compat[$repo]))
			{
				$hgrepos[$repo]['status'] = 1;
			}
			else if(isset($realdir[$repo]) && !isset($hgwebdir_compat[$repo]))
			{
				$hgrepos[$repo]['status'] = 0;
			}
			else if(!isset($realdir[$repo]) && isset($hgwebdir_compat[$repo]))
			{
				$hgrepos[$repo]['status'] = 2;
			}
		}
		
		return $hgrepos;
	}
	
	function saveconf($collections)
	{
		$this->__hgwebconf_compat_persist($collections);
	}
	
	/**
	 * Public accessors - hgrc
	 */
	 
	 /* TODO */
	 
	 /**
	  * Public accessors - permissions
	  */
	  
	function can_create($repository_name)
	{
		return $this->_hgwebconf_allow_repo_create;
	}
	
	function can_delete($repository_name)
	{
		return $this->_hgwebconf_allow_repo_delete;
	}
	
	/**
	 * Public helpers - filesystem
	 */
	 
	function create_repository($repository_name)
	{
		if(!$this->can_create($repository_name))
		{
			return false;
		}
		
		$cd = $this->_repositories_rel_dir;
		mkdir($cd . $repository_name);
		mkdir($cd . $repository_name . '/.hg/store/data/', 755, TRUE);
		
		// create hgrc
		return $this->__hgrc_persist($repository_name);
	}
	
	function delete_repository($repository_name)
	{
		if(!$this->can_delete($repository_name))
		{
			return false;
		}
		
		$cd = $this->_repositories_rel_dir . $repository_name;
		
		$this->recursiveDelete($cd . '/.hg/');
		return $this->recursiveDelete($cd);
	}
	 
	  /**
	   * Private helpers - hgweb.config
	   */
	
	/**
	 * 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');
		
		// FIXME this doesn't check if file or directory...
		$realdir = directory_map($this->_repositories_rel_dir, TRUE);
		
		$verifiedrealdir = array();
		foreach($realdir as $file)
		{
			// checks if we detected a folder
			if(is_dir($this->_repositories_rel_dir . $file))
			{
				$verifiedrealdir[$file] = $file;
			}
		}
		
		return $verifiedrealdir;
	}
	
	/**
	 * Scans Mercurial's webconf.config configuration file, detecting
	 * registered repositories.
	 */
	function __hgwebconfscan()
	{
		$hgwebconf_all = $this->__hgwebconf_compat_load();
		
		$hgwebconf_collections = array();
		if(isset($hgwebconf_all['collections']))
		{
			foreach($hgwebconf_all['collections'] as $path => $name)
			{
				$hgwebconf_collections[$name] = $name;
			}
		}
		
		return $hgwebconf_collections;
	}
	
	/**
	 * Compatibility layer for Mercurial PHP config files,
	 * which contain forward slashes '\' as key values.
	 * 
	 * @return contents of a php_ini array compatible representation
	 */
	function __hgwebconf_compat_load()
	{
		$hgwebconf_lock_path = $this->_lock_dir . 'hgweb.config.lock';
		
		// need to regenerate lock? (a php ini-parseable file)
		// FIXME also compare timestamps, block other user access?
		if(!is_readable($hgwebconf_lock_path))
		{
			// load the PHP to file
			$hgwebconf = $this->_hgwebconf_path;
			//$fh = fopen($hgwebconf, 'r');
			$hgwebconf_str = file_get_contents($hgwebconf);//fread($fh, filesize($hgwebconf));
			//fclose($fh);
			
			// replace all occurances of the forward slash '/'
			$hgwebconf_str = str_replace('/', $this->_compatability_delimiter, $hgwebconf_str);
			
			// write temp compatible ini file
			//$fh = fopen($hgwebconf_lock_path, 'w+');
			//fwrite($fh, $hgwebconf_str);
			//fclose($fh);
			file_put_contents($hgwebconf_lock_path, $hgwebconf_str);
		}
		
		// load the new compat ini
		// FIXME minimize the number of times we need to do this
		$hgwebconf_all = parse_ini_file($hgwebconf_lock_path, true);
		
		return $hgwebconf_all;
	}
	
	/**
	 * Takes a PHP-compatable php_ini array and converts it back into the original file.
	 *  
	 */
	function __hgwebconf_compat_persist($hgwebconf_collections)
	{
		$hgwebconf_all = $this->__hgwebconf_compat_load();
		
		// replace the 'collections' section with the new one
		$hgwebconf_all['collections'] = $hgwebconf_collections;
		
		$hgwebconf_new_ini = ';Generated by Mercurial Repository Manager';
		// generate ini string
		foreach($hgwebconf_all as $ini_section_name => $ini_section_items)
		{
			// section header
			$hgwebconf_new_ini .= "\n[".$ini_section_name.']';
			
			if($ini_section_name == 'collections')
			{
				foreach($ini_section_items as $ini_item_name => $ini_item_value)
				{
					$hgwebconf_new_ini .= "\n" . $this->_repositories_abs_dir . $ini_item_value . ' = ' . $ini_item_value;
				}
			}
			
			else
			{
				foreach($ini_section_items as $ini_item_name => $ini_item_value)
				{
					$hgwebconf_new_ini .= "\n" . $ini_item_name . ' = ' . $ini_item_value;
				}	
			}
			
			
		}
		$hgwebconf_new_ini .= "\n;End generated file\n";
		
		// undo the compatibility to allow Mercurial
		$hgwebconf_new_ini = str_replace($this->_compatability_delimiter, '/', $hgwebconf_new_ini);
		
		// FIXME double declared lock path..
		$hgwebconf_lock_path = $this->_lock_dir . 'hgweb.config.lock';
		$hgwebconf_tmp_path = $this->_lock_dir . 'hgweb.config.tmp';
		
		// persist to disk in a temporary file
		file_put_contents($hgwebconf_tmp_path, $hgwebconf_new_ini);
		
		// TODO concurrency check
		// overwrite existing hgweb.conf
		rename($hgwebconf_tmp_path, $this->_hgwebconf_path);
		
		// cleanup 
		unlink($hgwebconf_lock_path);
		
		return true;
	}
	
	
	
	
	
	function getProjectParams()
	{
		return array('ui'=>array('config1'=>'value'));
	}
	
	function setProjectParams($params)
	{
		
	}
	
	function __hgrc_persist($project_name, $config = null)
	{
		$cd = $this->_repositories_rel_dir . $project_name . '/.hg/';
		
		// create file
		touch($cd . 'hgrc');
		
		return true;
	}
	
	
	/**
     * Delete a file or recursively delete a directory
     *
     * @param string $str Path to file or directory
     */
    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);
        }
    }
}