. *
* *
************************************************************************
*
* GITManagedWebpage.class.php
*
* Functions to manage a Website using GIT Version Control
*
*/
class GITManagedWebpage {
const ERROR_CRITICAL = 1;
const SESSION_PREFIX = "GITManagedWebpage_";
private $giturl;
private $workdir, $localdir;
private $ready = false;
private $loopedcall = false;
private $config = null;
private $config_changed = true;
private $activeSession = null;
public function __construct($giturl, $workdir = null, $localdir = null) {
if(session_status() != PHP_SESSION_ACTIVE) {
session_start();
}
if(defined("GITMANAGED_EXECUTED")) {
$this->loopedcall = true;
return;
}
if($workdir === null) {
$workdir = dirname(__FILE__);
if(substr($workdir, -1) != "/")
$workdir .= "/";
$workdir .= ".gitmanaged/";
}
else if(substr($workdir, -1) != "/")
$workdir .= "/";
if($localdir === null) {
$localdir = dirname(__FILE__);
if(substr($localdir, -1) != "/")
$localdir .= "/";
}
else if(substr($localdir, -1) != "/")
$localdir .= "/";
$this->giturl = $giturl;
$this->workdir = $workdir;
$this->localdir = $localdir;
if(!file_exists($this->workdir) || !is_dir($this->workdir)) {
if(file_exists($this->workdir) && !is_dir($this->workdir)) {
$this->error(self::ERROR_CRITICAL, "local workdir (".htmlspecialchars($this->workdir).") is not a directory.");
return;
}
$this->setupWorkdir();
}
$this->ready = true;
}
/* private function gitcmd(...)
* Execute a git command and return output
*/
private function gitcmd() {
$args = func_get_args();
$argstr = "";
foreach($args as $arg) {
if(is_array($arg)) {
foreach($arg as $subarg) {
$argstr .= " ".escapeshellarg($subarg);
}
} else
$argstr .= " ".escapeshellarg($arg);
}
$gitcmd = 'git '.escapeshellarg('--git-dir='.$this->workdir.'repository/.git').' '.escapeshellarg('--work-tree='.$this->workdir.'repository').$argstr;
$output = shell_exec($gitcmd);
return $output;
}
/* private function setConfig($name, $value)
* store a option in the configuration
*/
private function setConfig($name, $value) {
$this->config[strtolower($name)] = $value;
$this->config_changed = true;
}
private function checkConfigIntegrity() {
foreach($this->config as $key => $value) {
if(substr($key, 0, strlen('branch_')) == 'branch_') {
if(!file_exists($this->workdir.$key)) {
unset($this->config['key']);
}
}
}
}
/* private function getConfig($name)
* get an option from the configuration
*/
private function getConfig($name) {
if($this->config == null) {
if(!$this->ready)
return null;
if(file_exists($this->workdir."config.txt")) {
$config_txt = @file_get_contents($this->workdir."config.txt");
$this->config = unserialize($config_txt);
$this->checkConfigIntegrity();
} else {
$this->config = array();
return null;
}
}
if(array_key_exists(strtolower($name), $this->config))
return $this->config[strtolower($name)];
else
return null;
}
private function saveConfig() {
if($this->config_changed && $this->ready) {
$fp = fopen($this->workdir."config.txt", "w");
fwrite($fp, serialize($this->config));
fclose($fp);
}
}
/* private function setupWorkdir()
* Setup local GITManagedWebpage Work directory with git repository
*/
private function setupWorkdir() {
// check requirements
$git_exec = shell_exec('which git');
if(!preg_match('#git#', $git_exec)) {
$this->error(self::ERROR_CRITICAL, "git not installed locally.");
return;
}
mkdir($this->workdir);
mkdir($this->workdir.'repository');
shell_exec('git clone '.escapeshellarg($this->giturl).' '.escapeshellarg($this->workdir.'repository'));
$gitok = $this->gitcmd("status");
if(preg_match("#Not a git repository#", $gitok)) {
rmdir($this->workdir.'repository');
rmdir($this->workdir);
$this->error(self::ERROR_CRITICAL, "error cloning git repository.");
return;
}
$fp = fopen($this->workdir.'.htaccess');
fwrite($fp, '
Order deny,allow
Deny from all
');
fclose($fp);
$this->ready = true;
$default_branch = str_replace(array("\r", "\n"), array("", ""), $this->gitcmd("rev-parse", "--abbrev-ref", "HEAD"));
$this->setConfig("defaultbranch", $default_branch);
$this->saveConfig();
}
private function getActiveBranch() {
if($this->activeSession)
return $this->activeSession;
else if(isset($_SESSION[self::SESSION_PREFIX.'branch']))
return $_SESSION[self::SESSION_PREFIX.'branch'];
else
return $this->getConfig("defaultbranch");
}
private function setActiveBranch($branch, $remember) {
$this->activeSession = $branch;
if($remember)
$_SESSION[self::SESSION_PREFIX.'branch'] = $branch;
}
private function getLocalUntrackedFiles() {
$default_branch = $this->getConfig("defaultbranch");
$tracked_files = $this->gitcmd("ls-tree", $default_branch, "--full-name", "--name-only");
$local_files = shell_exec("find ".escapeshellarg($this->localdir));
$untracked_files = array();
$tracked_files = explode("\n", str_replace(array("\r"), array(""), $tracked_files));
$local_files = explode("\n", str_replace(array("\r"), array(""), $local_files));
foreach($local_files as $local_file) {
if(!$local_file)
continue;
if($strip_local || (($strip_local = strlen($this->localdir)) && substr($local_file, 0, $strip_local) == $this->localdir)) {
$local_file = substr($local_file, $strip_local);
}
$tracked = false;
foreach($tracked_files as $tracked_file) {
if($tracked_file == $local_file) {
$tracked = true;
break;
}
}
if(!$tracked) {
$untracked_files[] = $local_file;
}
}
return $untracked_files;
}
private function branchExists($branch) {
//check if branch exists
$gitret = $this->gitcmd("rev-list", "--max-count=1", $branch);
if(!preg_match("#([a-z0-9]{40})#", $gitret, $match))
return false;
else
return $match[1];
}
private function localBranchPath($branch, $create = false) {
$default_branch = $this->getConfig("defaultbranch");
if($branch == $default_branch)
$dir = $this->localdir;
else
$dir = $this->workdir.'branch_'.str_replace(array('/'), array('_'), $branch).'/';
if(file_exists($dir))
return $dir;
else if($create) {
mkdir($dir);
return $dir;
} else
return false;
}
private function updateBranch($branch, $path, $force = false) {
if(substr($path, -1) != '/')
$path .= '/';
$current_branch = str_replace(array("\r", "\n"), array("", ""), $this->gitcmd("rev-parse", "--abbrev-ref", "HEAD"));
$this->gitcmd("fetch");
$this->gitcmd("reset", "--hard", "origin/".$branch);
$gitret = $this->gitcmd("rev-list", "--max-count=1", $branch);
preg_match("#([a-z0-9]{40})#", $gitret, $match);
$newest_version = $match[1];
$deleted_files = array();
if(($current_version = $this->getConfig('version_'.$branch))) {
if($current_version == $newest_version && !$force)
return;
else {
$override_all = true;
$delfiles = $this->gitcmd("diff", "--diff-filter=D", "--name-only", $current_version, $newest_version);
$delfiles = explode("\n", str_replace(array("\r"), array(""), $delfiles));
foreach($delfiles as $file) {
if(!$file)
continue;
$deleted_files[] = $file;
}
}
} else
$override_all = true;
if($override_all) {
$rsync_present = preg_match("#rsync#", `which rsync`);
if($rsync_present)
shell_exec('rsync -avz --exclude ".git" '.escapeshellarg($this->workdir."repository/").' '.escapeshellarg($path));
else
shell_exec('tar -c --exclude ".git" -C '.escapeshellarg($this->workdir."repository").' . | tar -x -C '.escapeshellarg($path));
// remove deleted files
foreach($deleted_files as $file) {
unlink($path.$file);
}
}
$this->setConfig('version_'.$branch, $newest_version);
$this->saveConfig();
}
/* public function update()
* Pulls latest commit of active branch and overwrites files in branch folder
*/
public function update() {
if($this->loopedcall)
return;
$active_branch = $this->getActiveBranch();
if(!$this->branchExists($active_branch))
return false;
$dir = $this->localBranchPath($active_branch, true);
$this->updateBranch($active_branch, $dir);
}
public function setBranch($branch, $remember = false) {
if($this->loopedcall)
return;
if(!$this->branchExists($branch)) {
$this->gitcmd("fetch");
if(!$this->branchExists('origin/'.$branch))
return false;
}
$this->setActiveBranch($branch, $remember);
if(!$this->localBranchPath($branch)) {
$dir = $this->localBranchPath($branch, true);
$this->updateBranch($branch, $dir, true);
}
}
public function getExecFile($file = null) {
if($this->loopedcall)
return;
define("GITMANAGED_EXECUTED", true);
if(!$file)
$file = $_SERVER['PHP_SELF'];
if($file[0] == '/')
$file = substr($file, 1);
$default_branch = $this->getConfig("defaultbranch");
$active_branch = $this->getActiveBranch();
if($active_branch != $default_branch) {
if(!($dir = $this->localBranchPath($active_branch))) {
$dir = $this->localBranchPath($active_branch, true);
$this->updateBranch($active_branch, $dir, true);
}
chdir($dir);
return $dir.$file;
} else {
return $file;
}
}
public function showMngrWindow() {
$html = '';
$html .= '';
$html .= '';
$html .= '';
$html .= '';
$html .= '';
$html .= '';
$current_branch = $this->getActiveBranch();
$current_version = substr($this->getConfig('version_'.$current_branch), 0, 7);
$html .= <<
HTML_END;
return $html;
}
}
?>