From 6ad4b6352315004fd8c730a00a2c65caa612239e Mon Sep 17 00:00:00 2001
From: hahn <axel.hahn@iml.unibe.ch>
Date: Fri, 18 Oct 2013 12:47:11 +0200
Subject: [PATCH] temp checkin 8

---
 public_html/deployment/.htaccess              |   51 +-
 public_html/deployment/act_accept.php         |  107 +-
 public_html/deployment/act_build.php          |   86 +-
 public_html/deployment/act_cleanup.php        |   20 +
 public_html/deployment/act_deploy.php         |  103 +-
 public_html/deployment/act_doc.php            |   42 +-
 public_html/deployment/act_overview.php       |   58 +-
 public_html/deployment/classes/html.tpl.php   |  342 ++--
 .../deployment/classes/project.class.php      | 1624 +++++++++--------
 .../deployment/classes/projectlist.class.php  |  511 +++---
 .../deployment/config/inc_projects_config.php |  300 +--
 public_html/deployment/gen__htusers.bat       |    9 +
 public_html/deployment/inc_functions.php      |  165 +-
 public_html/deployment/index.php              |  102 +-
 shellscripts/sync_packagedir_to_puppet.php    |   25 +
 15 files changed, 1963 insertions(+), 1582 deletions(-)
 create mode 100644 public_html/deployment/act_cleanup.php
 create mode 100644 public_html/deployment/gen__htusers.bat
 create mode 100644 shellscripts/sync_packagedir_to_puppet.php

diff --git a/public_html/deployment/.htaccess b/public_html/deployment/.htaccess
index 270fd260..d0880edb 100644
--- a/public_html/deployment/.htaccess
+++ b/public_html/deployment/.htaccess
@@ -1,20 +1,31 @@
-RewriteEngine on
-
-# parse htm files
-# RewriteRule ^(.*)\.htm$ /deployment/shared/parse.php [PT,QSA]
-
-# rewrite bootstrap-ressources
-# RewriteCond %{REQUEST_FILENAME} !-f
-# RewriteCond %{REQUEST_FILENAME} !-d
-# RewriteRule /(css|js|img)/(.*)$ /deployment/gfranko-Document-Bootstrap-cf0730d/$1/$2
-# RewriteRule /(css|js|img)/(.*)$ /deployment-gui/bootstrap/$1/$2
-
-RewriteBase /deployment
-RewriteRule ^([a-zA-Z0-9_-]+)$ index.php?prj=$1
-RewriteRule ^([a-zA-Z0-9_-]+)/$ index.php?prj=$1
-RewriteRule ^([a-zA-Z0-9_-]+)/([a-zA-Z0-9_-]+)$ index.php?prj=$1&action=$2
-RewriteRule ^([a-zA-Z0-9_-]+)/([a-zA-Z0-9_-]+)/$ index.php?prj=$1&action=$2
-RewriteRule ^([a-zA-Z0-9_-]+)/([a-zA-Z0-9_-]+)/([a-zA-Z0-9_-]+)$ index.php?prj=$1&action=$2&par3=$3
-RewriteRule ^([a-zA-Z0-9_-]+)/([a-zA-Z0-9_-]+)/([a-zA-Z0-9_-]+)/$ index.php?prj=$1&action=$2&par3=$3
-RewriteRule ^([a-zA-Z0-9_-]+)/([a-zA-Z0-9_-]+)/([a-zA-Z0-9_-]+)/([a-zA-Z0-9_-]+)$ index.php?prj=$1&action=$2&par3=$3&par4=$4
-RewriteRule ^([a-zA-Z0-9_-]+)/([a-zA-Z0-9_-]+)/([a-zA-Z0-9_-]+)/([a-zA-Z0-9_-]+)/$ index.php?prj=$1&action=$2&par3=$3&par4=$4
+
+AuthUserFile /var/www/ci.iml.unibe.ch/public_html/deployment/.htusers
+AuthName "IML DEPLOYMENT-Passwort"
+AuthType Basic
+
+#Allow any valid user
+require valid-user
+
+
+RewriteEngine on
+
+# parse htm files
+# RewriteRule ^(.*)\.htm$ /deployment/shared/parse.php [PT,QSA]
+
+# rewrite bootstrap-ressources
+# RewriteCond %{REQUEST_FILENAME} !-f
+# RewriteCond %{REQUEST_FILENAME} !-d
+# RewriteRule /(css|js|img)/(.*)$ /deployment/gfranko-Document-Bootstrap-cf0730d/$1/$2
+# RewriteRule /(css|js|img)/(.*)$ /deployment-gui/bootstrap/$1/$2
+
+RewriteBase /deployment
+RewriteRule ^([a-zA-Z0-9_-]+)$ index.php?prj=$1
+RewriteRule ^([a-zA-Z0-9_-]+)/$ index.php?prj=$1
+RewriteRule ^([a-zA-Z0-9_-]+)/([a-zA-Z0-9_-]+)$ index.php?prj=$1&action=$2
+RewriteRule ^([a-zA-Z0-9_-]+)/([a-zA-Z0-9_-]+)/$ index.php?prj=$1&action=$2
+RewriteRule ^([a-zA-Z0-9_-]+)/([a-zA-Z0-9_-]+)/([a-zA-Z0-9_-]+)$ index.php?prj=$1&action=$2&par3=$3
+RewriteRule ^([a-zA-Z0-9_-]+)/([a-zA-Z0-9_-]+)/([a-zA-Z0-9_-]+)/$ index.php?prj=$1&action=$2&par3=$3
+RewriteRule ^([a-zA-Z0-9_-]+)/([a-zA-Z0-9_-]+)/([a-zA-Z0-9_-]+)/([a-zA-Z0-9_-]+)$ index.php?prj=$1&action=$2&par3=$3&par4=$4
+RewriteRule ^([a-zA-Z0-9_-]+)/([a-zA-Z0-9_-]+)/([a-zA-Z0-9_-]+)/([a-zA-Z0-9_-]+)/$ index.php?prj=$1&action=$2&par3=$3&par4=$4
+
+# besser alles ohne extension auf index.php routen und dort verarbeiten.
\ No newline at end of file
diff --git a/public_html/deployment/act_accept.php b/public_html/deployment/act_accept.php
index c7c6feb5..d5688052 100644
--- a/public_html/deployment/act_accept.php
+++ b/public_html/deployment/act_accept.php
@@ -1,54 +1,53 @@
-<?php
-
-require_once("./config/inc_projects_config.php");
-require_once("./classes/project.class.php");
-
-
-// --- Checks
-$oPrj=new project($aParams["prj"]);
-
-if (array_key_exists("par3", $aParams)){
-    $sPhase=$aParams["par3"];
-}
-
-    
-$sOut='<h1>Accept :: '.$oPrj->getLabel().' - '.$sPhase.'</h1><hr>';
-if (array_key_exists("confirm", $aParams)) {
-        $sOut.=$oPrj->accept($sPhase);
-
-} else {
-    
-    if (!$sPhase){
-        $sOut.='ERROR: no Phase.';
-    } else {
-        $aPhaseData=$oPrj->getPhaseInfos($sPhase);
-        
-        $sOut.='
-               <p>
-                    auf dem Server installiert:<br>
-                    <pre>'.print_r($aPhaseData["depoyed"], true).'</pre>
-               </p>
-        ';
-        
-        if (!$oPrj->canAcceptPhase($sPhase)){
-            $sOut.="ERROR: the phase $sPhase cannot be accepted.";
-        } else {
-            // Eingabe Kommentare zum Deployment
-            $sOut.='
-
-                 <form action="?" method="post" enctype="multipart/form-data">
-                    <input type="hidden" name="confirm" value="1">
-                    <input type="submit" class="btn btn-primary" value="Deploy">
-                 </form>
-                 ';
-        }
-    }
-}
-
-    
-$sOut.=aHome();
-
-
-// -- Ausgabe
-$sPhpOut=$sOut;
-?>
+<?php
+
+require_once("./config/inc_projects_config.php");
+require_once("./classes/project.class.php");
+
+
+// --- Checks
+$oPrj=new project($aParams["prj"]);
+
+if (array_key_exists("par3", $aParams)){
+    $sPhase=$aParams["par3"];
+}
+
+    
+$sOut='';
+if (array_key_exists("confirm", $aParams)) {
+        $sOut.=$oPrj->accept($sPhase);
+} else {
+    
+    if (!$sPhase){
+        $sOut.='ERROR: no Phase.<br>';
+    } else {
+        $aPhaseData=$oPrj->getPhaseInfos($sPhase);
+        
+        $sOut.='
+               <p>
+                    auf dem Server installiert:<br>
+                    <pre>'.print_r($aPhaseData["depoyed"], true).'</pre>
+               </p>
+        ';
+        
+        if (!$oPrj->canAcceptPhase($sPhase)){
+            $sOut.="ERROR: the phase $sPhase cannot be accepted.";
+        } else {
+            // Eingabe Kommentare zum Deployment
+            $sOut.='
+
+                 <form action="?" method="post" enctype="multipart/form-data">
+                    <input type="hidden" name="confirm" value="1">
+                    <input type="submit" class="btn btn-primary" value="Deploy">
+                 </form>
+                 ';
+        }
+    }
+}
+
+    
+$sOut.=aHome();
+
+
+// -- Ausgabe
+$sPhpOut=$sOut;
+?>
diff --git a/public_html/deployment/act_build.php b/public_html/deployment/act_build.php
index 9703fd8c..e8d22bcc 100644
--- a/public_html/deployment/act_build.php
+++ b/public_html/deployment/act_build.php
@@ -1,43 +1,43 @@
-<?php
-
-require_once("./config/inc_projects_config.php");
-require_once("./classes/project.class.php");
-require_once("./classes/formgen.class.php");
-
-
-// --- Checks
-$oPrj=new project($aParams["prj"]);
-    
-    
-$sOut='<h1>Build :: '.$oPrj->getLabel().'</h1><hr>';
-
-if (array_key_exists("confirm", $aParams)) {
-        $sOut.=$oPrj->build();
-} else {
-        $sNext=$oPrj->getNextPhase();
-        $sOut.='
-            <p>
-                 Es wird ein neues Paket erstellt und f&uuml; die Phase <em class="'.$sNext.'">'.$sNext.'</em> bereitgestellt.<br>
-            </p>
-        ';
-
-        // Eingabe Kommentare zum Deployment
-        $sOut.='
-             <hr>
-             <form action="?" method="post" enctype="multipart/form-data">
-                <input type="hidden" name="confirm" value="1">
-                <!-- ' . enterDeployinfos() . '
-                 <hr>
-                -->
-             <input type="submit" class="btn btn-primary" value="Paket f&uuml;r ['.$sNext.'] erstellen">
-             </form>
-             ';
-}
-
-    
-$sOut.=aHome();
-
-
-// -- Ausgabe
-$sPhpOut=$sOut;
-?>
+<?php
+
+require_once("./config/inc_projects_config.php");
+require_once("./classes/project.class.php");
+require_once("./classes/formgen.class.php");
+
+
+// --- Checks
+$oPrj=new project($aParams["prj"]);
+    
+    
+$sOut='';
+
+if (array_key_exists("confirm", $aParams)) {
+        $sOut.=$oPrj->build();
+} else {
+        $sNext=$oPrj->getNextPhase();
+        $sOut.='
+            <p>
+                 Es wird ein neues Paket erstellt und f&uuml; die Phase <em class="'.$sNext.'">'.$sNext.'</em> bereitgestellt.<br>
+            </p>
+        ';
+
+        // Eingabe Kommentare zum Deployment
+        $sOut.='
+             <hr>
+             <form action="?" method="post" enctype="multipart/form-data">
+                <input type="hidden" name="confirm" value="1">
+                <!-- ' . enterDeployinfos() . '
+                 <hr>
+                -->
+             <input type="submit" class="btn btn-primary" value="Paket f&uuml;r ['.$sNext.'] erstellen">
+             </form>
+             ';
+}
+
+    
+$sOut.=aHome();
+
+
+// -- Ausgabe
+$sPhpOut=$sOut;
+?>
diff --git a/public_html/deployment/act_cleanup.php b/public_html/deployment/act_cleanup.php
new file mode 100644
index 00000000..0a006456
--- /dev/null
+++ b/public_html/deployment/act_cleanup.php
@@ -0,0 +1,20 @@
+<?php
+
+require_once("./config/inc_projects_config.php");
+require_once("./classes/project.class.php");
+
+$oPrj=new project($sPrj);
+
+$sPhpOut='
+    
+    <h3>Archive Dir: '.$aConfig['archiveDir'].'</h3>
+        deleted:<br>
+        <pre>' . print_r($oPrj->cleanupArchive(), true) . '</pre>
+        versions left:<br>
+        <pre>' . print_r($oPrj->getVersions(), true). '</pre>
+            
+    <h3>Builds Dir: '.$aConfig['buildDir'].'</h3>
+        <pre>' . print_r($oPrj->cleanupBuilds(), true). '</pre>
+     ';
+
+?>
diff --git a/public_html/deployment/act_deploy.php b/public_html/deployment/act_deploy.php
index 04e41252..33825e26 100644
--- a/public_html/deployment/act_deploy.php
+++ b/public_html/deployment/act_deploy.php
@@ -1,51 +1,52 @@
-<?php
-
-require_once("./config/inc_projects_config.php");
-require_once("./classes/project.class.php");
-
-
-// --- Checks
-$oPrj=new project($aParams["prj"]);
-
-    
-$sOut='<h1>Deploy :: '.$oPrj->getLabel().'</h1><hr>';
-if (array_key_exists("par3", $aParams)){
-    $sPhase=$aParams["par3"];
-}
-
-
-if (array_key_exists("confirm", $aParams)) {
-        $sOut.=$oPrj->deploy($sPhase);
-} else {
-    if ($sPhase){
-        $sPhase=$aParams["par3"];
-    
-        $aPhaseData=$oPrj->getPhaseInfos($sPhase);
-        
-        $sOut.='
-               <p>
-                    in der Queue:<br>
-                    <pre>'.print_r($aPhaseData["onhold"], true).'</pre>
-               </p>
-        ';
-
-        // Eingabe Kommentare zum Deployment
-        $sOut.='
-             
-             <form action="?" method="post" enctype="multipart/form-data">
-                <input type="hidden" name="confirm" value="1">
-                <input type="submit" class="btn btn-primary" value="Deploy">
-             </form>
-             ';
-    } else {
-        $sOut.='ERROR: missing name of the phase.';
-    }
-}
-
-    
-$sOut.=aHome();
-
-
-// -- Ausgabe
-$sPhpOut=$sOut;
-?>
+<?php
+
+require_once("./config/inc_projects_config.php");
+require_once("./classes/project.class.php");
+require_once("./inc_functions.php");
+
+
+// --- Checks
+$oPrj=new project($aParams["prj"]);
+
+    
+$sOut='';
+if (array_key_exists("par3", $aParams)){
+    $sPhase=$aParams["par3"];
+}
+
+
+if (array_key_exists("confirm", $aParams)) {
+        $sOut.=$oPrj->deploy($sPhase);
+} else {
+    if ($sPhase){
+        $sPhase=$aParams["par3"];
+    
+        $aPhaseData=$oPrj->getPhaseInfos($sPhase);
+        
+        $sOut.='
+               <p>
+                    in der Queue:<br>
+                    <pre>'.print_r($aPhaseData["onhold"], true).'</pre>
+               </p>
+        ';
+
+        // Eingabe Kommentare zum Deployment
+        $sOut.='
+             
+             <form action="?" method="post" enctype="multipart/form-data">
+                <input type="hidden" name="confirm" value="1">
+                <input type="submit" class="btn btn-primary" value="Deploy">
+             </form>
+             ';
+    } else {
+        $sOut.=getBox("error", 'ERROR: missing name of the phase.');
+    }
+}
+
+    
+$sOut.=aHome();
+
+
+// -- Ausgabe
+$sPhpOut=$sOut;
+?>
diff --git a/public_html/deployment/act_doc.php b/public_html/deployment/act_doc.php
index 91c30af2..08c451d8 100644
--- a/public_html/deployment/act_doc.php
+++ b/public_html/deployment/act_doc.php
@@ -1,21 +1,21 @@
-<?php
-
-require_once("./classes/classinfos.class.php");
-$aClasses=array(
-       "project"=>"project", 
-        "projectlist"=>"projectlist",
-    );
-$sClass=$sPrj;
-
-$sPhpOut='';
-foreach ($aClasses as $sClassfile=>$sName){
-    
-    $sPhpOut.='<a href="/deployment/'.$sClassfile.'/doc/">'.$sClassfile.'</a> | ';
-}
-$sPhpOut.='<hr>';
-
-require_once("./classes/$sClass.class.php");
-$o=new classinfos($sPrj);
-$sPhpOut.=$o->render();
-?>
-
+<?php
+
+require_once("./classes/classinfos.class.php");
+$aClasses=array(
+       "project"=>"project", 
+        "projectlist"=>"projectlist",
+    );
+$sClass=$sPrj;
+
+$sPhpOut='';
+foreach ($aClasses as $sClassfile=>$sName){
+    
+    $sPhpOut.='<a href="/deployment/'.$sClassfile.'/doc/">'.$sClassfile.'</a> | ';
+}
+$sPhpOut.= '<hr>';
+
+require_once("./classes/$sClass.class.php");
+$o=new classinfos($sPrj);
+$sPhpOut.=$o->render();
+?>
+
diff --git a/public_html/deployment/act_overview.php b/public_html/deployment/act_overview.php
index 12c2ce90..8ff0e708 100644
--- a/public_html/deployment/act_overview.php
+++ b/public_html/deployment/act_overview.php
@@ -1,11 +1,47 @@
-<?php
-
-require_once("./config/inc_projects_config.php");
-require_once("./classes/projectlist.class.php");
-
-$oPrjList=new projectlist($aProjects);
-// $oPrjList->setConfig($aProjects);
-$sPhpOut='<h1>Overview</h1>'.$oPrjList->renderOverview();
-
-?>
-
+<?php
+
+require_once("./config/inc_projects_config.php");
+
+if (!array_key_exists("prj", $aParams)){
+    
+    // overview over all projects
+    require_once("./classes/projectlist.class.php");
+    $oPrjList=new projectlist($aProjects);
+    // $oPrjList->setConfig($aProjects);
+    
+    $sPhpOut=$oPrjList->renderOverview();
+} else {
+    require_once("./classes/project.class.php");
+    $oPrj=new project($aParams["prj"]);
+    $sPhpOut='
+        <h3>Actions</h3>
+        <a href="./build/" class="btn">build</a> 
+        <a href="./cleanup/" class="btn">cleanup</a>
+        <br>
+        <br>
+    <div class="tabbable"> <!-- Only required for left/right tabs -->
+        <ul class="nav nav-tabs">
+            <li class="active"><a href="#tab1" data-toggle="tab">Config</a></li>
+            <li><a href="#tab2" data-toggle="tab">Queues</a></li>
+            <li><a href="#tab3" data-toggle="tab">Versions</a></li>
+        </ul>
+        <div class="tab-content">
+            <div class="tab-pane active" id="tab1">
+                <h3>Config</h3>
+                <pre>'.print_r($oPrj->getConfig(), true).'</pre>
+            </div>
+            <div class="tab-pane" id="tab2">
+                <h3>Queues</h3>
+                <pre>'.print_r($oPrj->getAllPhaseInfos(), true).'</pre>
+            </div>
+            <div class="tab-pane" id="tab3">
+                <h3>Versions</h3>
+                In the Archive exist these builds:<br>
+                <pre>'.print_r($oPrj->getVersions(), true).'</pre>
+            </div>
+        </div>
+    </div>
+        ';
+}
+?>
+
diff --git a/public_html/deployment/classes/html.tpl.php b/public_html/deployment/classes/html.tpl.php
index 47bbe2e0..d18ee910 100644
--- a/public_html/deployment/classes/html.tpl.php
+++ b/public_html/deployment/classes/html.tpl.php
@@ -1,161 +1,181 @@
-<!doctype html>
-<html>
-	<head>
-    <meta name="robots" content="noindex,nofollow" />
-    
-    <meta charset="utf-8">
-    <!-- Mobile viewport optimized -->
-    <meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0" />
-    <!-- Mobile Internet Explorer allows us to activate ClearType technology for smoothing fonts for easy reading -->
-    <!-- 
-    not html5 valid:
-    <meta http-equiv="cleartype" content="on">
-    -->
-
-    <!-- Le styles -->
-    <link href="/deployment/bootstrap/css/bootstrap.css" rel="stylesheet">
-    <link href="http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.21/themes/black-tie/jquery-ui.css" rel="stylesheet">
-    <link href="/deployment/bootstrap/css/jquery.tocify.css" rel="stylesheet">
-    <link href="/deployment/bootstrap/css/prettify.css" type="text/css" rel="stylesheet" />
-    <link href="/deployment/bootstrap/css/styles.css" type="text/css" rel="stylesheet" />
-
-    <style>
-
-        @media (max-width: 767px) {
-            #toc {
-                position: relative;
-                width: 100%;
-                margin: 0px 0px 20px 0px;
-            }
-        }
-        
-        body{}
-        body, label, input, button, select, textarea, p, .btn {
-            font-size: 12px;
-        }
-        #header{
-            background:#eee; background: linear-gradient(#fff,#eee);
-            padding: 1em; font-size: 300%;
-            color:#a33;
-            text-shadow: 1px 1px 0 #fff, 10px 10px 10px #aaa;
-            border-bottom: 2px solid #ccc;
-        }
-
-        h2{font-size: 16px; margin: 0;}
-        h3{font-size: 14px; margin: 0;}
-
-        ul {}
-        ul li {
-            margin-bottom: 3px;
-        }
-
-        thead{background:#444; color:#eee; font-size: 130%;}
-
-        #tbloverview th{width: 25%;}
-        #tbloverview td{border-left: 1px solid #ccc;}
-        th.preview{background:#358; color:#eee;}
-        th.stage{background:#388; color:#eee;}
-        th.live{background:#3a3; color: #eee; }
-        .preview{background:#f4f8ff; color:#333; background: rgba(210,220,255, 0.7);}
-        .stage{background:#f4ffff; color:#333; background: rgba(210,255,255, 0.7);}
-        .live{background:#f0fff0; color:#333;  background: rgba(210,255,210, 0.7);}
-
-        .preview, .stage, .live{
-            padding: 4px;
-        }
-        
-        .warning{background: #fe8;}
-        .error{background: #fcc;}
-        .center{text-align: center;}
-        .inactive{color:#999; font-style: italic;}
-        
-        .versioninfo{
-            border: 0px solid #ccc;
-            padding: 0px;
-            border-radius: 5px;
-            margin-bottom: 10px;
-        }
-        
-        .navbar-fixed-top{box-shadow: 0 5px 10px rgba(0,0,0,0.1); }
-        .navbar-inner{background-color: #eee; background-image: linear-gradient(to bottom, #fafafa, #ddd, #eee);}
-        .navbar .brand{color:#d62; font-weight: bold; font-size: 150%;}
-        .navbar .nav > li{border-left: 1px solid rgba(255,255,255,0.3);}
-        .active1{
-            background-color: #cef;
-            box-shadow: 0 3px 8px rgba(0, 0, 0, 0.125) inset;
-                border-radius: 3px 3px 0 0;
-        }
-        .nav li:hover{
-            background-color: rgba(0,0,0,0.04);
-        }
-        .container-fluid{padding-top: 6px;}
-        .row-fluid{margin-top: 10px;}
-        .footerbar{background-color: #f8f8f8; background-image: linear-gradient(to bottom, #f8f8f8, #f0f0f0, #fff); padding: 20px;
-                border-radius: 20px 20px 0 0; font-size: 80%; text-align: center; margin-top: 40px;
-                color:#aaa;
-         }
-
- 
-    </style>
-    
-    <!--
-        <meta name="robots" content="noindex, nofollow" />
-		
-		
-		<style type="text/css" title="currentStyle">
-				@import "/shared/js/datatables/media/css/demo_page.css";
-				@import "/shared/js/datatables/media/css/demo_table.css";
-		</style>
-		
-		<script type="text/javascript" src="/shared/js/jquery.js"></script>
-		<script type="text/javascript" src="/shared/js/datatables/media/js/jquery.dataTables.min.js"></script>
-		
-        <link rel="stylesheet" type="text/css" href="main.css" media="screen">
-		<script type="text/javascript" src="main.js"></script>
-    -->
-        {{HEADER}}
-
-
-		</head>
-		<body>
-
-		<div id="divcontent">
-
-			{{CONTENT}}
-
-		</div>
-        {{FOOTER}}{{JSONREADY}}
-        
-    <!-- Placed at the end of the document so the pages load faster -->
-    <script src="/deployment/bootstrap/js/jquery-1.8.0.min.js"></script>
-    <script src="/deployment/bootstrap/js/jquery-ui-1.8.23.custom.min.js"></script>
-    <script src="/deployment/bootstrap/js/bootstrap.min.js"></script>
-    <script src="/deployment/bootstrap/js/history.js"></script>
-    <script src="/deployment/bootstrap/js/jquery.tocify.min.js"></script>
-    <script src="/deployment/bootstrap/js/prettify.js"></script>
-    <!-- <script src="js/githubrepo.js"></script> -->
-    <script>
-        $(function() {
-
-            $("#toc").tocify({ selectors: "h2, h3, h4", scrollTo: 60, highlightOffset: 60, extendPage: false });
-
-            prettyPrint();
-
-            $(".optionName").popover({ trigger: "hover" });
-
-            $("a[href='#']").click(function(event) {
- 
-                event.preventDefault();
-
-            });
-
-        });
-	
-        GetMessages();
-
-    </script>
-	
-        
-	</body>
-</html>
-
+<!doctype html>
+<html>
+	<head>
+    <meta name="robots" content="noindex,nofollow" />
+    
+    <meta charset="utf-8">
+    <!-- Mobile viewport optimized -->
+    <meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0" />
+    <!-- Mobile Internet Explorer allows us to activate ClearType technology for smoothing fonts for easy reading -->
+    <!-- 
+    not html5 valid:
+    <meta http-equiv="cleartype" content="on">
+    -->
+
+    <!-- Le styles -->
+    <link href="/deployment/bootstrap/css/bootstrap.css" rel="stylesheet">
+    <link href="http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.21/themes/black-tie/jquery-ui.css" rel="stylesheet">
+    <link href="/deployment/bootstrap/css/jquery.tocify.css" rel="stylesheet">
+    <link href="/deployment/bootstrap/css/prettify.css" type="text/css" rel="stylesheet" />
+    <link href="/deployment/bootstrap/css/styles.css" type="text/css" rel="stylesheet" />
+
+    <style>
+
+        @media (max-width: 767px) {
+            #toc {
+                position: relative;
+                width: 100%;
+                margin: 0px 0px 20px 0px;
+            }
+        }
+        
+        body{}
+        body, label, input, button, select, textarea, p, .btn {
+            font-size: 12px;
+        }
+        
+        #header,#footer{
+            background:#eee; background: linear-gradient(#fff,#eee);
+            padding: 1em; font-size: 300%;
+            color:#a33;
+            text-shadow: 1px 1px 0 #fff, 10px 10px 10px #aaa;
+            border-bottom: 2px solid #ccc;
+        }
+        #footer{
+            padding: 0.3em; 
+            background: linear-gradient(#eee,#fff);
+            font-size: 100%;
+            color:#a33;
+            text-align: right;
+            text-shadow: 1px 1px 0 #fff, 3px 3px 3px #aaa;
+            border-bottom: 1px solid #eee;
+            margin-top: 5em;
+        }
+        #header2{
+            background: none;
+            padding: 0.5em; margin-bottom: 2em;
+        }
+        #content{
+            margin-top: 2em;
+            border-left: 0px solid #ccc;
+            padding: 1em;
+            box-shadow: 0 0 50px #eee;
+        }
+        h2{font-size: 16px; margin: 0;}
+        h3{font-size: 14px; margin: 0;}
+
+        ul {}
+        ul li {
+            margin-bottom: 3px;
+        }
+
+        thead{background:#444; color:#eee; font-size: 130%;}
+
+        #tbloverview th{width: 25%;}
+        #tbloverview td{border-left: 1px solid #ccc;}
+        th.preview{background:#358; color:#eee;}
+        th.stage{background:#388; color:#eee;}
+        th.live{background:#3a3; color: #eee; }
+        .preview{background:#f4f8ff; color:#333; background: rgba(210,220,255, 0.7);}
+        .stage{background:#f4ffff; color:#333; background: rgba(210,255,255, 0.7);}
+        .live{background:#f0fff0; color:#333;  background: rgba(210,255,210, 0.7);}
+
+        .preview, .stage, .live{
+            padding: 4px;
+        }
+        
+        .warning{background: #fe8;}
+        .error{background: #fcc;}
+        .center{text-align: center;}
+        .inactive{color:#999; font-style: italic;}
+        
+        .versioninfo{
+            border: 0px solid #ccc;
+            padding: 0px;
+            border-radius: 5px;
+            margin-bottom: 10px;
+        }
+        
+        .navbar-fixed-top{box-shadow: 0 5px 10px rgba(0,0,0,0.1); }
+        .navbar-inner{background-color: #eee; background-image: linear-gradient(to bottom, #fafafa, #ddd, #eee);}
+        .navbar .brand{color:#d62; font-weight: bold; font-size: 150%;}
+        .navbar .nav > li{border-left: 1px solid rgba(255,255,255,0.3);}
+        .active1{
+            background-color: #cef;
+            box-shadow: 0 3px 8px rgba(0, 0, 0, 0.125) inset;
+                border-radius: 3px 3px 0 0;
+        }
+        .nav li:hover{
+            background-color: rgba(0,0,0,0.04);
+        }
+        .container-fluid{padding-top: 6px;}
+        .row-fluid{margin-top: 10px;}
+        .footerbar{background-color: #f8f8f8; background-image: linear-gradient(to bottom, #f8f8f8, #f0f0f0, #fff); padding: 20px;
+                border-radius: 20px 20px 0 0; font-size: 80%; text-align: center; margin-top: 40px;
+                color:#aaa;
+         }
+
+ 
+    </style>
+    
+    <!--
+        <meta name="robots" content="noindex, nofollow" />
+		
+		
+		<style type="text/css" title="currentStyle">
+				@import "/shared/js/datatables/media/css/demo_page.css";
+				@import "/shared/js/datatables/media/css/demo_table.css";
+		</style>
+		
+		<script type="text/javascript" src="/shared/js/jquery.js"></script>
+		<script type="text/javascript" src="/shared/js/datatables/media/js/jquery.dataTables.min.js"></script>
+		
+        <link rel="stylesheet" type="text/css" href="main.css" media="screen">
+		<script type="text/javascript" src="main.js"></script>
+    -->
+        {{HEADER}}
+
+
+		</head>
+		<body>
+
+		<div id="divcontent">
+
+			{{CONTENT}}
+
+		</div>
+        {{FOOTER}}{{JSONREADY}}
+        
+    <!-- Placed at the end of the document so the pages load faster -->
+    <script src="/deployment/bootstrap/js/jquery-1.8.0.min.js"></script>
+    <script src="/deployment/bootstrap/js/jquery-ui-1.8.23.custom.min.js"></script>
+    <script src="/deployment/bootstrap/js/bootstrap.min.js"></script>
+    <script src="/deployment/bootstrap/js/history.js"></script>
+    <script src="/deployment/bootstrap/js/jquery.tocify.min.js"></script>
+    <script src="/deployment/bootstrap/js/prettify.js"></script>
+    <!-- <script src="js/githubrepo.js"></script> -->
+    <script>
+        $(function() {
+
+            $("#toc").tocify({ selectors: "h2, h3, h4", scrollTo: 60, highlightOffset: 60, extendPage: false });
+
+            prettyPrint();
+
+            $(".optionName").popover({ trigger: "hover" });
+
+            $("a[href='#']").click(function(event) {
+ 
+                event.preventDefault();
+
+            });
+
+        });
+	
+        GetMessages();
+
+    </script>
+	
+        
+	</body>
+</html>
+
diff --git a/public_html/deployment/classes/project.class.php b/public_html/deployment/classes/project.class.php
index 515d5e25..7d3e6a6a 100644
--- a/public_html/deployment/classes/project.class.php
+++ b/public_html/deployment/classes/project.class.php
@@ -1,726 +1,900 @@
-<?php
-
-/**
- * class for single project
- */
-class project {
-    // ----------------------------------------------------------------------
-    // CONFIG
-    // ----------------------------------------------------------------------
-
-    /**
-     * config file
-     * @var type 
-     */
-    private $_sCfgfile = "../config/inc_projects_config.php";
-
-    /**
-     * configuration ($aConfig in the config file)
-     * @var array
-     */
-    private $_aConfig = array();
-
-    /**
-     * configuration of the project (= $aProjects[ID] in the config file)
-     * @var array
-     */
-    private $_aPrjConfig = array();
-
-    /**
-     * version infos of all phases
-     * @var type 
-     */
-    private $_aData = array();
-
-    /**
-     * places of version infos in each deployment phase
-     * @var type 
-     */
-    private $_aPlaces = array(
-        "onhold" => "Queue",
-        "ready4deployment" => "Repo",
-        "deployed" => "Installiert",
-    );
-    private $_iRcAll = 0;
-
-    // ----------------------------------------------------------------------
-    // constructor
-    // ----------------------------------------------------------------------
-
-    /**
-     * constructor
-     * use setConfig method if you initialize without config
-     * @param array $aConfig  config of the project
-     */
-    public function __construct($sId = false) {
-        $this->_readConfig();
-        if ($sId)
-            $this->setProjectById($sId);
-    }
-
-    // ----------------------------------------------------------------------
-    // private functions
-    // ----------------------------------------------------------------------
-
-    /**
-     * read default config file
-     * @return boolean
-     */
-    private function _readConfig() {
-        require(__dir__ . '/' . $this->_sCfgfile);
-        $this->_aConfig = $aConfig;
-        return true;
-    }
-
-    /**
-     * validate config data
-     * @return boolean
-     */
-    private function _verifyConfig() {
-        if (!count($this->_aPrjConfig))
-            die("ERROR::CONFIG: no config was for found the project. check $aProjects in config file.");
-
-        if (!array_key_exists("packageDir", $this->_aConfig))
-            die("ERROR::CONFIG: packagedir is not defined.");
-        if (!$this->_aConfig["packageDir"])
-            die("ERROR::CONFIG: packagedir is not set.");
-        if (!file_exists($this->_aConfig["packageDir"]))
-            die("ERROR::CONFIG: packagedir does not exist: &quot;" . $this->_aConfig['packageDir'] . "&quot;.");
-
-        if (!array_key_exists("archiveDir", $this->_aConfig))
-            die("ERROR::CONFIG: key &quot;archiveDir&quot; was not found in config.");
-        if (!$this->_aConfig["archiveDir"])
-            die("ERROR::CONFIG: archiveDir is not set.");
-        if (!file_exists($this->_aConfig["archiveDir"]))
-            die("ERROR::CONFIG: archiveDir does not exist: &quot;" . $this->_aConfig['archiveDir'] . "&quot;.");
-
-        if (!array_key_exists("phases", $this->_aPrjConfig))
-            die("ERROR::CONFIG: key &quot;phases&quot; was not found in config.<br><pre>" . print_r($this->_aPrjConfig, true) . "</pre>");
-
-        // TODO: verify ausbauen
-        return true;
-    }
-
-    /**
-     * execute a commandline; returns a string of output of timestamp, command, output and returncode
-     * @param string $sCommand
-     * @return string
-     */
-    private function _execAndSend($sCommand) {
-        $sReturn = '';
-        $bUseHtml = $_SERVER ? true : false;
-
-        ob_implicit_flush(true);
-        // ob_end_flush();
-        $descriptorspec = array(
-            0 => array("pipe", "r"), // stdin is a pipe that the child will read from
-            1 => array("pipe", "w"), // stdout is a pipe that the child will write to
-            2 => array("pipe", "w")    // stderr is a pipe that the child will write to
-        );
-        flush();
-        $process = proc_open($sCommand, $descriptorspec, $pipes, realpath('./'), array());
-
-        $sReturn.="[" . date("H:i:s d.m.Y") . "] ";
-        $sReturn.=$bUseHtml ? "<strong>$sCommand</strong>" : "$sCommand";
-        $sReturn.=$bUseHtml ? "<br>" : "\n";
-
-        $sErrors = false;
-        if (is_resource($process)) {
-            while ($s = fgets($pipes[1])) {
-                $sReturn.=$s;
-                flush();
-            }
-            while ($s = fgets($pipes[2])) {
-                $sErrors.=$s;
-                flush();
-            }
-        }
-        if ($sErrors)
-            $sReturn.="STDERR:\n" . $sErrors;
-        $oStatus = proc_get_status($process);
-        $iRc = $oStatus['exitcode'];
-        $this->_iRcAll += $iRc;
-        $sReturn.="[" . date("H:i:s d.m.Y") . "] exitcode " . $iRc;
-        if ($bUseHtml) {
-            if ($iRc == 0) {
-                $sReturn = "<pre>" . $sReturn;
-            } else {
-                $sReturn = '<pre class="error">' . $sReturn;
-            }
-            $sReturn.='</pre>';
-        }
-
-        flush();
-        return $sReturn;
-    }
-
-    // ----------------------------------------------------------------------
-    // GETTER
-    // ----------------------------------------------------------------------
-
-    /**
-     * get a temp directory
-     * @return string
-     */
-    private function _getTempDir() {
-        $s = $this->_aConfig['workDir'] . "/build/" . $this->_aPrjConfig["fileprefix"] . "_" . date("Ymd-His");
-        // TODO: auskommentieren
-        $s = $this->_aConfig['workDir'] . "/build/" . $this->_aPrjConfig["fileprefix"] . "_temp";
-        return $s;
-    }
-
-    /**
-     * get directory for infofile and package (without extension)
-     * @param string $sPhase  one of preview|stage|live ...
-     * @param string $sPlace  one of onhold|ready4deployment|deployed
-     * @return string
-     */
-    private function _getFileBase($sPhase, $sPlace) {
-        if (!array_key_exists($sPhase, $this->_aConfig["phases"])) {
-            die("ERROR: _getFileBase - this phase does not exist: $sPhase.");
-        }
-        if (!array_key_exists($sPlace, $this->_aPlaces)) {
-            die("ERROR: _getFileBase - this place does not exist: $sPhase.");
-        }
-
-
-        // local file for onhold|ready4deployment
-        $sBase = $this->_aConfig['packageDir'] . "/" . $sPhase . "/" . $this->_aPrjConfig["fileprefix"];
-
-        if ($sPlace == "onhold")
-            $sBase.="_onhold";
-        // $sBase .= "/" . $this->_aPrjConfig["fileprefix"];
-
-        // url for deployed
-        if ($sPlace == "deployed") {
-            if ($this->isActivePhase($sPhase) && array_key_exists("url", $this->_aPrjConfig["phases"][$sPhase])) {
-                $sBase = $this->_aPrjConfig["phases"][$sPhase]["url"] . $this->_aPrjConfig["fileprefix"];
-            } else {
-                $sBase = '';
-            }
-        }
-
-        return $sBase;
-    }
-
-    /**
-     * get filename for info file
-     * @param string $sPhase  one of preview|stage|live ...
-     * @param string $sPlace  one of onhold|ready4deployment|deployed
-     * @return string
-     */
-    private function _getInfofile($sPhase, $sPlace) {
-        $sBase = $this->_getFileBase($sPhase, $sPlace);
-        return $sBase ? $sBase .'/'.$this->_aPrjConfig["fileprefix"] . '.json' : false;
-    }
-
-    /**
-     * get filename for package file
-     * @param string $sPhase  one of preview|stage|live ...
-     * @param string $sPlace  one of onhold|ready4deployment|deployed
-     * @return string
-     */
-    private function _getPackagefile($sPhase, $sPlace) {
-        $sBase = $this->_getFileBase($sPhase, $sPlace);
-        return $sBase ? $sBase .'/'.$this->_aPrjConfig["fileprefix"] . '.tgz' : false;
-    }
-
-    private function _getArchiveDir($sTimestamp) {
-        if (!$sTimestamp) {
-            die("ERROR: getArchiveDir timestamp is required");
-        }
-        return $this->_aConfig["archiveDir"] . '/' . $this->_aConfig["id"] . '/' . $sTimestamp;
-    }
-
-    /**
-     * get conmplete config of the project
-     * @return array
-     */
-    public function getConfig() {
-        return $this->_aPrjConfig;
-    }
-
-    /**
-     * get name/ label of the project
-     * @return string
-     */
-    public function getLabel() {
-        return $this->_aPrjConfig["label"];
-    }
-
-    /**
-     * get description of the project
-     * @return string
-     */
-    public function getDescription() {
-        return $this->_aPrjConfig["description"];
-    }
-
-    /**
-     * get deploy and queue infos for all phases
-     * @return type
-     */
-    public function getAllPhaseInfos() {
-        if (!array_key_exists("phases", $this->_aData))
-            $this->_aData["phases"] = array();
-
-        foreach (array_keys($this->_aConfig["phases"]) as $sPhase) {
-            if (!array_key_exists($sPhase, $this->_aData["phases"])) {
-                $this->getPhaseInfos($sPhase);
-            }
-        }
-        return $this->_aData["phases"];
-    }
-
-    /**
-     * get statusinfos of a named phase
-     * @param string $sPhase name of the phase; one of preview|stage|live
-     * @return array
-     */
-    public function getPhaseInfos($sPhase) {
-        if (!array_key_exists("phases", $this->_aData))
-            $this->_aData["phases"] = array();
-
-        if (!array_key_exists($sPhase, $this->_aData["phases"])) {
-
-            $this->_aData["phases"][$sPhase] = array();
-            $aTmp = array();
-
-            // a blocked package is waiting for deployment timeslot?
-            $sKey = "onhold";
-            $sJsonfile = $this->_getInfofile($sPhase, $sKey);
-            $aTmp[$sKey] = array();
-            if (file_exists($sJsonfile)) {
-                $aJson = json_decode(file_get_contents($sJsonfile), true);
-                if (array_key_exists("timestamp", $aJson)) {
-                    $aTmp[$sKey] = json_decode(file_get_contents($sJsonfile), true);
-                    $aTmp[$sKey]["infofile"] = $sJsonfile;
-                    $aTmp[$sKey]["ok"] = 1;
-                } else {
-                    $aTmp[$sKey]["error"] = "info file $sJsonfile exists but is corrupt (no timestamp)." . print_r($aJson, true);
-                }
-            } else {
-                $aTmp[$sKey]["info"] = "No package is waiting in the queue.";
-                $aTmp[$sKey]["ok"] = 1;
-            }
-
-            // package for puppet
-            $sKey = "ready4deployment";
-            $sJsonfile = $this->_getInfofile($sPhase, $sKey);
-            $aTmp[$sKey] = array();
-            if (file_exists($sJsonfile)) {
-                $sPkgfile = $this->_getPackagefile($sPhase, $sKey);
-                if (file_exists($sPkgfile)) {
-                    $aJson = json_decode(file_get_contents($sJsonfile), true);
-                    if (array_key_exists("timestamp", $aJson)) {
-                        $aTmp[$sKey] = json_decode(file_get_contents($sJsonfile), true);
-                        $aTmp[$sKey]["infofile"] = $sJsonfile;
-                        $aTmp[$sKey]["packagefile"] = $sPkgfile;
-                        $aTmp[$sKey]["ok"] = 1;
-                    } else {
-                        $aTmp[$sKey]["error"] = "info file $sJsonfile exists but is corrupt (no timestamp)." . print_r($aJson, true);
-                    }
-                } else {
-                    $aTmp[$sKey]["error"] = "package file was not found: $sPkgfile";
-                }
-            } else {
-                $aTmp[$sKey]["error"] = "info file was not found: $sJsonfile";
-            }
-
-            // published data
-            $sKey = "deployed";
-            $sJsonfile = $this->_getInfofile($sPhase, $sKey);
-            $aTmp[$sKey] = array();
-            if ($this->isActivePhase($sPhase)) {
-                $sJsonUrl = $this->_aPrjConfig["phases"][$sPhase]["url"] . $this->_aPrjConfig["fileprefix"] . ".json";
-                $sJsonData = @file_get_contents($sJsonUrl);
-                if ($sJsonData) {
-                    $aJson = json_decode($sJsonData, true);
-                    if (array_key_exists("timestamp", $aJson)) {
-                        $aTmp[$sKey] = $aJson;
-                        $aTmp[$sKey]["infofile"] = $sJsonUrl;
-                        $aTmp[$sKey]["ok"] = 1;
-                    } else {
-                        $aTmp[$sKey]["error"] = "json url was readable <a href=$sJsonUrl>$sJsonUrl</a> but is corrupt (no timestamp)." . print_r($aJson, true);
-                    }
-                } else {
-                    $aTmp[$sKey]["error"] = "json url not readable <a href=$sJsonUrl>$sJsonUrl</a>";
-                }
-            } else {
-                $aTmp[$sKey]["warning"] = "this phase is not active or has no url";
-            }
-
-            $this->_aData["phases"][$sPhase] = $aTmp;
-        }
-        return $this->_aData["phases"][$sPhase];
-    }
-
-    /**
-     * check if the given phase is active for this project
-     * @param type $sPhase
-     * @return type
-     */
-    public function isActivePhase($sPhase) {
-        return (
-                array_key_exists($sPhase, $this->_aPrjConfig["phases"]) && array_key_exists("url", $this->_aPrjConfig["phases"][$sPhase]) && $this->_aPrjConfig["phases"][$sPhase]["url"]
-                );
-    }
-
-    /**
-     * find the next active phase of a project
-     * @param string $sPhase current phase; if empty the function sends back the first phase
-     */
-    public function getNextPhase($sPhase = false) {
-        if ($sPhase) {
-            if (!array_key_exists($sPhase, $this->_aConfig["phases"])) {
-                die("ERROR: this phase does not exist: $sPhase.");
-            }
-        }
-
-        $sNextPhase = false;
-        $bUseNextPhase = $sPhase ? false : true;
-        foreach (array_keys($this->_aConfig["phases"]) as $s) {
-            if ($bUseNextPhase) {
-                if ($this->isActivePhase($s)) {
-                    $sNextPhase = $s;
-                    $bUseNextPhase = false;
-                    continue;
-                }
-            }
-            if ($sPhase == $s) {
-                $bUseNextPhase = true;
-            }
-        }
-
-        return $sNextPhase;
-    }
-
-    /**
-     * check: is the deployment to the next phase enabled for this phase?
-     * @param type $sPhase
-     */
-    public function canAcceptPhase($sPhase = false) {
-
-        if (!$sPhase) {
-            $sNext = $this->getNextPhase($sPhase);
-            return $sNext > '';
-        }
-
-
-        if (!array_key_exists($sPhase, $this->_aConfig["phases"])) {
-            die("ERROR: in canDeploy this phase does not exist: $sPhase.");
-        }
-        if (!$this->isActivePhase($sPhase)) {
-            // die("ERROR: the phase $sPhase is not active in this project.");
-            return false;
-        }
-        $sNext = $this->getNextPhase($sPhase);
-        if (!$sNext)
-            return false;
-
-        // ensure that _aData is filled
-        $this->getPhaseInfos($sPhase);
-
-        // array key "ok" must be in the ready4deployment and deployed info
-        if (
-                array_key_exists($sPhase, $this->_aData["phases"]) && array_key_exists("onhold", $this->_aData["phases"][$sPhase]) && array_key_exists("ready4deployment", $this->_aData["phases"][$sPhase]) && array_key_exists("deployed", $this->_aData["phases"][$sPhase]) && array_key_exists("ok", $this->_aData["phases"][$sPhase]["onhold"]) && array_key_exists("ok", $this->_aData["phases"][$sPhase]["ready4deployment"]) && array_key_exists("ok", $this->_aData["phases"][$sPhase]["deployed"])
-        )
-            return true;
-
-        return false;
-    }
-
-    // ----------------------------------------------------------------------
-    // SETTER
-    // ----------------------------------------------------------------------
-
-    /**
-     * apply a config
-     * @param array $aConfig
-     * @return boolean
-      public function setConfig($aConfig) {
-      $this->_aConfig = $aConfig;
-      $this->_verifyConfig();
-      return true;
-      }
-     */
-
-    /**
-     * apply a config
-     * @param array $aConfig
-     * @return boolean
-     */
-    public function setProjectById($sId) {
-        $this->_aPrjConfig = array();
-        require(__dir__ . '/' . $this->_sCfgfile);
-        if (!array_key_exists("$sId", $aProjects)) {
-            die("ERROR: a project with ID $sId does not exist.");
-        }
-        $this->_aPrjConfig = $aProjects[$sId];
-        $this->_aConfig["id"] = $sId;
-        $this->_verifyConfig();
-        return true;
-    }
-
-    // ----------------------------------------------------------------------
-    // ACTIONS
-    // ----------------------------------------------------------------------
-
-    public function build() {
-        global $aParams;
-        $sReturn = false;
-
-        $this->_iRcAll = 0;
-
-        // --------------------------------------------------
-        // create workdir
-        // --------------------------------------------------
-        $sTempDir = $this->_getTempDir();
-        $sFirstLevel = $this->getNextPhase();
-        if (!$sFirstLevel) return false;
-
-        $sReturn.="<h2>Create a temporary build dir</h2>";
-        if (!file_exists($sTempDir)) {
-            $sReturn.="* create " . $sTempDir . "<br>";
-            mkdir($sTempDir);
-        }
-        $sReturn.=$this->_execAndSend("ls -ld " . $sTempDir);
-        if (!file_exists($sTempDir)) {
-            $sReturn.="ERROR: build - $sTempDir was not created.";
-            return $sReturn;
-        }
-
-
-        // --------------------------------------------------
-        // checkout
-        // --------------------------------------------------
-        switch ($this->_aPrjConfig["build"]["type"]) {
-            case "git":
-
-                $sReturn.="<h2>Checkout a GIT project</h2>";
-                // $sReturn.=$this->_execAndSend("find " . $this->_aConfig["workDir"]);
-                $sReturn.=$this->_execAndSend("cd $sTempDir && git init");
-
-                $sKeyfile = dirname(dirname(__file__)) . "/" . $this->_aPrjConfig["build"]["keyfile"];
-                $sWrapper = dirname(dirname(dirname(dirname(__file__)))) . "/shellscripts/gitsshwrapper.sh";
-                $sReturn.=$this->_execAndSend("ls -l " . $sWrapper);
-                // $sReturn.=$this->_execAndSend("ls -l ".$sKeyfile);
-                $sReturn.=$this->_execAndSend("cd $sTempDir && export GIT_SSH=$sWrapper ; export PKEY=$sKeyfile; git pull " . $this->_aPrjConfig["build"]["ssh"]);
-
-                $sVersion=$this->_execAndSend("cd $sTempDir && export GIT_SSH=$sWrapper ; export PKEY=$sKeyfile; git pull " . $this->_aPrjConfig["build"]["ssh"]);
-
-                // control: directory listing after checkout
-                $sReturn.=$this->_execAndSend("ls -lisa $sTempDir");
-
-                // fetch version infos
-                $sVersioninfo = str_replace("\n", "<br>", shell_exec("cd $sTempDir && git log -1"));
-                $sReturn.=$sVersioninfo;
-
-                break;
-
-            default:
-                die("ERROR: Build Type not supported: " . $this->_aPrjConfig["build"]["type"]);
-                break;
-        }
-
-
-        // --------------------------------------------------
-        // execute hook
-        // --------------------------------------------------
-        $sHookfile = $this->_aConfig['hooks']['build-aftercheckout'];
-        $sReturn.='<h2>Execute Hook ' . $sHookfile . '</h2>';
-        if (file_exists($sTempDir . '/' . $sHookfile)) {
-            $sReturn.=$this->_execAndSend('cd ' . $sTempDir . ' && bash "' . $sHookfile . '"');
-        } else {
-            $sReturn.='SKIP. Hook was not found.<br>';
-        }
-
-        // --------------------------------------------------
-        // TODO: cleanup .git, .svn, ...?
-        // --------------------------------------------------
-        // --------------------------------------------------
-        // create package
-        // --------------------------------------------------
-        $sReturn.='<h2>Create package</h2>';
-
-        // public_html must exist
-        $sWebroot = $sTempDir . '/public_html';
-        if (!file_exists($sWebroot)) {
-            $sReturn.='ERROR: a subdir public_html does not exist.';
-            return $sReturn;
-        }
-
-        // generate info file
-        $sTs = date("Y-m-d H:i:s");
-        $sTs2 = date("Ymd_His");
-        $sInfoFileWebroot = $sWebroot . '/' . basename($this->_getInfofile($sFirstLevel, "deployed"));
-        $sInfoFileArchiv = $this->_getArchiveDir($sTs2) . '/' . basename($this->_getInfofile($sFirstLevel, "deployed"));
-        $sPackageFileArchiv = $this->_getArchiveDir($sTs2) . '/' . basename($this->_getPackagefile($sFirstLevel, "deployed"));
-
-        $sInfos = '{
-            "date": "' . $sTs . '",
-            "timestamp": "' . $sTs2 . '",
-            "revision": "' . $sVersioninfo . '",
-            "user": "' . $aParams["inputUser"] . '",
-            "remark": "' . $aParams["inputComment"] . '"
-        }';
-
-        
-        
-        $sReturn.="writing info file into webroot...<br>";
-        file_put_contents($sInfoFileWebroot, $sInfos);
-        $sReturn.=$this->_execAndSend("ls -l $sInfoFileWebroot");
-
-        if (!file_exists(dirname($sPackageFileArchiv))) {
-            $sReturn.="* create " . dirname($sPackageFileArchiv) . "<br>";
-            mkdir(dirname($sPackageFileArchiv), 0775, true);
-        }
-        $sReturn.=$this->_execAndSend("ls -ld " . dirname($sPackageFileArchiv));
-        if (!file_exists(dirname($sPackageFileArchiv))) {
-            $sReturn.="ERROR: directory was not created.";
-            return $sReturn;
-        }
-
-        // TODO: cleanup
-
-        $sReturn.="create archive $sPackageFileArchiv<br>";
-        $sReturn.=$this->_execAndSend("cd $sTempDir && tar -czf $sPackageFileArchiv .");
-        $sReturn.="writing info file into archive...<br>";
-        file_put_contents($sInfoFileArchiv, $sInfos);
-        $sReturn.="<br>Created Archive files:<br>";
-        $sReturn.=$this->_execAndSend("ls -l $sPackageFileArchiv $sInfoFileArchiv");
-
-        if (!$this->_iRcAll == 0) {
-            $sReturn.='<h3>ERROR: creation failed</h3>One of the commands failed (see above).<br>You can ask the sysadmins and analyze with them the created temp directory &quot;' . $sTempDir . '&quot;.';
-            return $sReturn;
-        }
-
-        // TODO: force synch archive to puppet master
-
-        $sReturn.=$this->queue($sFirstLevel, $sTs2);
-
-
-        $sReturn.="<h2>cleanup $sTempDir</h2>";
-        $sReturn.=$this->_execAndSend("rm -rf $sTempDir");
-
-        $sReturn.="* Move [Paketarchiv]/[prj].[tgz+json] zu [RepoDir-von-" . $sFirstLevel . "]<br>";
-        $sReturn.="<br>";
-
-
-        return $sReturn;
-    }
-
-    public function queue($sTargetphase, $sVersion) {
-
-        if (!$this->isActivePhase($sTargetphase)) return false;
-        
-        $sReturn="<h2>Queue to $sTargetphase</h2>";
-        $sPlace="onhold";
-
-        $sLinkTarget = $this->_getArchiveDir($sVersion);
-        $sLinkName = $this->_getFileBase($sTargetphase, $sPlace);
-
-        // --------------------------------------------------
-        // Checks
-        // --------------------------------------------------
-        if (!$sLinkName) {
-            die("ERROR: Queuing failed - sLinkName is empty.");
-        }
-        if (!$sLinkTarget) {
-            die("ERROR: Queuing  failed - sLinkTarget is empty.");
-        }
-        if (!file_exists($sLinkTarget)) {
-            die("ERROR: Queuing failed - version $sVersion is invalid. The directory $sLinkTarget does not exist.");
-        }
-
-        // --------------------------------------------------
-        // create the new link
-        // --------------------------------------------------
-        $this->_iRcAll = 0;
-        if (file_exists($sLinkName)) {
-            $sReturn.="removing existing version<br>";
-            $sReturn.=$this->_execAndSend("rm -f $sLinkName");
-        }
-        $sReturn.="linking to new version <br>";
-        $sReturn.=$this->_execAndSend("ln -s $sLinkTarget $sLinkName");
-        $sReturn.=$this->_execAndSend("ls -l $sLinkName | fgrep $sLinkTarget");
-
-
-        if (!$this->_iRcAll == 0) {
-            $sReturn.='<h3>ERROR: Queuing failed</h3>One of the commands failed (see above).';
-            return $sReturn;
-        }
-        
-        // TODO: force synch to puppet master
-        
-        $sReturn.="<br>SUCCESS: the version $sVersion was set to place $sPlace<br>";
-
-
-        $sReturn.=$this->deploy($sTargetphase);
-        // $sReturn.=$this->_execAndSend("");
-
-        return $sReturn;
-    }
-    
-    public function deploy($sTargetphase) {
-        if (!$this->isActivePhase($sTargetphase)) return false;
-        
-        $sReturn="<h2>Deploy to $sTargetphase</h2>";
-        
-        $sQueueLink = $this->_getFileBase($sTargetphase, "onhold");
-        $sRepoLink = $this->_getFileBase($sTargetphase, "ready4deployment");
-        
-        if (array_key_exists("deploytimes", $this->_aConfig["phases"][$sTargetphase])){
-            // check if the a deploy time is reached
-            $sNow=date("D H:i:s");
-            $sReturn.="check if one of the deployment times is reached and matches $sNow<br>";
-            $bCanDeploy=false;
-            foreach ($this->_aConfig["phases"][$sTargetphase]["deploytimes"] as $sRegex){
-                $sReturn.="... $sRegex<br>";
-                if (preg_match($sRegex, $sNow)){
-                    $bCanDeploy=true;
-                }
-            }
-            if (!$bCanDeploy){
-                $sReturn.="SKIP: deployment time was not reached.<br>";
-                return $sReturn;
-            }
-            $sReturn.="OK, deployment time was reached.<br>";
-            // if ()
-        }
-        if (!file_exists($sQueueLink)){
-            $sReturn.="SKIP: no current queue - $sQueueLink does not exist.";
-            return $sReturn;
-        }
-        if (!file_exists($sQueueLink)){
-            $sReturn.="SKIP: no current queue - $sQueueLink does not exist.";
-            return $sReturn;
-        }
-        
-
-        // --------------------------------------------------
-        // move the queue link to the repo name
-        // --------------------------------------------------
-        $this->_iRcAll = 0;
-        if (file_exists($sRepoLink)) {
-            $sReturn.="removing existing version<br>";
-            $sReturn.=$this->_execAndSend("rm -f $sRepoLink");
-        }
-        $sReturn.="moving queue to repo<br>";
-        $sReturn.=$this->_execAndSend("mv $sQueueLink $sRepoLink");
-
-
-        if (!$this->_iRcAll == 0) {
-            $sReturn.='<h3>ERROR: Deployment failed</h3>One of the commands failed (see above).';
-            return $sReturn;
-        }
-        
-        // TODO: force synch to puppet master
-        
-        $sReturn.="<br>SUCCESS: deployment was done and will be installed soon<br>";
-        return $sReturn;
-    }
-
-}
-
+<?php
+
+/**
+ * class for single project
+ */
+class project {
+    // ----------------------------------------------------------------------
+    // CONFIG
+    // ----------------------------------------------------------------------
+
+    /**
+     * config file
+     * @var type 
+     */
+    private $_sCfgfile = "../config/inc_projects_config.php";
+
+    /**
+     * configuration ($aConfig in the config file)
+     * @var array
+     */
+    private $_aConfig = array();
+
+    /**
+     * configuration of the project (= $aProjects[ID] in the config file)
+     * @var array
+     */
+    private $_aPrjConfig = array();
+
+    /**
+     * version infos of all phases
+     * @var type 
+     */
+    private $_aData = array();
+
+    /**
+     * existing versions in the archive dir
+     * @var type 
+     */
+    private $_aVersions = array();
+    
+    /**
+     * places of version infos in each deployment phase
+     * @var type 
+     */
+    private $_aPlaces = array(
+        "onhold" => "Queue",
+        "ready4deployment" => "Repo",
+        "deployed" => "Installiert",
+    );
+    private $_iRcAll = 0;
+
+    // ----------------------------------------------------------------------
+    // constructor
+    // ----------------------------------------------------------------------
+    
+    /**
+     * constructor
+     * @param string $sId  id of the project
+     */
+    public function __construct($sId = false) {
+        $this->_readConfig();
+        if ($sId)
+            $this->setProjectById($sId);
+    }
+
+    // ----------------------------------------------------------------------
+    // private functions
+    // ----------------------------------------------------------------------
+
+    /**
+     * read default config file
+     * @return boolean
+     */
+    private function _readConfig() {
+        require(__dir__ . '/' . $this->_sCfgfile);
+        $this->_aConfig = $aConfig;
+        return true;
+    }
+
+    /**
+     * validate config data
+     * @return boolean
+     */
+    private function _verifyConfig() {
+        if (!count($this->_aPrjConfig))
+            die("ERROR::CONFIG: no config was for found the project. check $aProjects in config file.");
+
+        if (!array_key_exists("packageDir", $this->_aConfig))
+            die("ERROR::CONFIG: packagedir is not defined.");
+        if (!$this->_aConfig["packageDir"])
+            die("ERROR::CONFIG: packagedir is not set.");
+        if (!file_exists($this->_aConfig["packageDir"]))
+            die("ERROR::CONFIG: packagedir does not exist: &quot;" . $this->_aConfig['packageDir'] . "&quot;.");
+
+        if (!array_key_exists("archiveDir", $this->_aConfig))
+            die("ERROR::CONFIG: key &quot;archiveDir&quot; was not found in config.");
+        if (!$this->_aConfig["archiveDir"])
+            die("ERROR::CONFIG: archiveDir is not set.");
+        if (!file_exists($this->_aConfig["archiveDir"]))
+            die("ERROR::CONFIG: archiveDir does not exist: &quot;" . $this->_aConfig['archiveDir'] . "&quot;.");
+
+        if (!array_key_exists("phases", $this->_aPrjConfig))
+            die("ERROR::CONFIG: key &quot;phases&quot; was not found in config.<br><pre>" . print_r($this->_aPrjConfig, true) . "</pre>");
+
+        // TODO: verify ausbauen
+        return true;
+    }
+
+    /**
+     * execute a commandline; returns a string of output of timestamp, command, output and returncode
+     * @param string $sCommand
+     * @return string
+     */
+    private function _execAndSend($sCommand) {
+        $sReturn = '';
+        $bUseHtml = $_SERVER ? true : false;
+
+        ob_implicit_flush(true);
+        // ob_end_flush();
+        $descriptorspec = array(
+            0 => array("pipe", "r"), // stdin is a pipe that the child will read from
+            1 => array("pipe", "w"), // stdout is a pipe that the child will write to
+            2 => array("pipe", "w")    // stderr is a pipe that the child will write to
+        );
+        flush();
+        $process = proc_open($sCommand, $descriptorspec, $pipes, realpath('./'), array());
+
+        $sReturn.="[" . date("H:i:s d.m.Y") . "] ";
+        $sReturn.=$bUseHtml ? "<strong>$sCommand</strong>" : "$sCommand";
+        $sReturn.=$bUseHtml ? "<br>" : "\n";
+
+        $sErrors = false;
+        if (is_resource($process)) {
+            while ($s = fgets($pipes[1])) {
+                $sReturn.=$s;
+                flush();
+            }
+            while ($s = fgets($pipes[2])) {
+                $sErrors.=$s;
+                flush();
+            }
+        }
+        if ($sErrors)
+            $sReturn.="STDERR:\n" . $sErrors;
+        $oStatus = proc_get_status($process);
+        $iRc = $oStatus['exitcode'];
+        $this->_iRcAll += $iRc;
+        $sReturn.="[" . date("H:i:s d.m.Y") . "] exitcode " . $iRc;
+        if ($bUseHtml) {
+            if ($iRc == 0) {
+                $sReturn = "<pre>" . $sReturn;
+            } else {
+                $sReturn = '<pre class="error">' . $sReturn;
+            }
+            $sReturn.='</pre>';
+        }
+
+        flush();
+        return $sReturn;
+    }
+
+    // ----------------------------------------------------------------------
+    // GETTER
+    // ----------------------------------------------------------------------
+
+    /**
+     * get a temp directory
+     * @return string
+     */
+    private function _getTempDir() {
+        return $s = $this->_getBuildDir() . '/' . $this->_aPrjConfig["fileprefix"] . "_" . date("Ymd-His");
+    }
+
+    private function _getBuildDir(){
+        return $this->_aConfig['buildDir'].'/'.$this->_aConfig["id"];
+    }
+    
+    /**
+     * get directory for infofile and package (without extension)
+     * @param string $sPhase  one of preview|stage|live ...
+     * @param string $sPlace  one of onhold|ready4deployment|deployed
+     * @return string
+     */
+    private function _getFileBase($sPhase, $sPlace) {
+        if (!array_key_exists($sPhase, $this->_aConfig["phases"])) {
+            die("ERROR: _getFileBase - this phase does not exist: $sPhase.");
+        }
+        if (!array_key_exists($sPlace, $this->_aPlaces)) {
+            die("ERROR: _getFileBase - this place does not exist: $sPhase.");
+        }
+
+
+        // local file for onhold|ready4deployment
+        $sBase = $this->_aConfig['packageDir'] . "/" . $sPhase . "/" . $this->_aPrjConfig["fileprefix"];
+
+        if ($sPlace == "onhold")
+            $sBase.="_onhold";
+        // $sBase .= "/" . $this->_aPrjConfig["fileprefix"];
+
+        // url for deployed
+        if ($sPlace == "deployed") {
+            if ($this->isActivePhase($sPhase) && array_key_exists("url", $this->_aPrjConfig["phases"][$sPhase])) {
+                $sBase = $this->_aPrjConfig["phases"][$sPhase]["url"] . $this->_aPrjConfig["fileprefix"];
+            } else {
+                $sBase = '';
+            }
+        }
+
+        return $sBase;
+    }
+
+    /**
+     * get filename for info file
+     * @param string $sPhase  one of preview|stage|live ...
+     * @param string $sPlace  one of onhold|ready4deployment|deployed
+     * @return string
+     */
+    private function _getInfofile($sPhase, $sPlace) {
+        $sBase = $this->_getFileBase($sPhase, $sPlace);
+        return $sBase ? $sBase .'/'.$this->_aPrjConfig["fileprefix"] . '.json' : false;
+    }
+
+    /**
+     * get filename for package file
+     * @param string $sPhase  one of preview|stage|live ...
+     * @param string $sPlace  one of onhold|ready4deployment|deployed
+     * @return string
+     */
+    private function _getPackagefile($sPhase, $sPlace) {
+        $sBase = $this->_getFileBase($sPhase, $sPlace);
+        return $sBase ? $sBase .'/'.$this->_aPrjConfig["fileprefix"] . '.tgz' : false;
+    }
+
+    private function _getArchiveDir($sTimestamp) {
+        if (!$sTimestamp) {
+            die("ERROR: getArchiveDir timestamp is required");
+        }
+        return $this->_getProjectArchiveDir() . '/' . $sTimestamp;
+    }
+    
+    /**
+     * get the directory for archive files of this project
+     * @return string
+     */
+    public function _getProjectArchiveDir() {
+        return $this->_aConfig["archiveDir"] . '/' . $this->_aConfig["id"];
+    }
+
+    /**
+     * get all existing versions in archive and its usage
+     * versions are array keys; where they are used is written in values
+     * @return array
+     */
+    public function getVersions(){
+        
+        // --- read all file entries
+        $aReturn=array();
+        $sDir=$this->_getProjectArchiveDir();
+        foreach (scandir($this->_getProjectArchiveDir()) as $sEntry){
+            if (is_dir($sDir.'/'.$sEntry) && $sEntry != '.' && $sEntry != '..') $aReturn[$sEntry]=false;
+        }
+        $this->_aVersions=$aReturn;
+
+        // --- check version in all phases 
+        $this->getAllPhaseInfos();
+        foreach ($this->_aData["phases"] as $sPhase=>$aData){
+            foreach(array_keys($this->_aPlaces) as $sPlace){
+                if (array_key_exists($sPlace, $aData) && array_key_exists("version", $aData[$sPlace])){
+                    $this->_aVersions[$aData[$sPlace]["version"]][]=$sPlace;
+                }
+            }
+        }
+        ksort($this->_aVersions);
+        return $this->_aVersions;
+    }
+
+    /**
+     * recursive delete 
+     * @param type $dir
+     * @return type
+     */
+    private function _rmdir($dir) {
+        foreach (scandir($dir) as $sEntry){
+            if (is_dir($dir.'/'.$sEntry) && $sEntry != '.' && $sEntry != '..') {
+                $this->_rmdir($dir.'/'.$sEntry);
+            }
+            else
+                unlink($dir.'/'.$sEntry);
+        }
+        return rmdir($dir);
+    }
+
+    /**
+     * cleanup of archive directory; it returns the list of deleted
+     * directories as array
+     * @return array
+     */
+    public function cleanupArchive(){
+        $aUnused=array();
+        $sDir=$this->_getProjectArchiveDir();
+        $this->getVersions();
+        
+        $aDelete=array();
+        // find unused versions
+        foreach ($this->_aVersions as $sVersion=>$aUsage){
+            if (!$aUsage || count($aUsage)==0){
+                $aUnused[]=$sVersion;
+            }
+        }
+        
+        // keep a few
+        while (count($aUnused)>$this->_aConfig["versionsToKeep"]){
+            $sVersion=array_shift($aUnused);
+            $sDir2=$sDir.'/'.$sVersion;
+            if ($this->_rmdir($sDir2)){
+                $aDelete[]=$sDir2;
+            } else {
+                echo "Warning: unable to delete Archive $sDir2<br>";
+            };
+        }
+        
+        // rescan versions
+        if (count($aDelete)) {
+            $this->getVersions();
+        }
+        
+        return $aDelete;
+    }
+    
+    /**
+     * cleanup of archive directory; it returns the list of deleted
+     * directories as array
+     * @return array
+     */
+    public function cleanupBuilds(){
+        $sDir=$this->_getBuildDir();
+        $aDirlist=array();
+        $aDelete=array();
+        foreach (scandir($sDir) as $sEntry){
+            if (is_dir($sDir.'/'.$sEntry) && $sEntry != '.' && $sEntry != '..') $aDirlist[]=$sEntry;
+        }
+        
+        // keep a few
+        while (count($aDirlist)>$this->_aConfig["builtsToKeep"]){
+            $sVersion=array_shift($aDirlist);
+            $sDir2=$sDir.'/'.$sVersion;
+            if ($this->_rmdir($sDir2)){
+                $aDelete[]=$sDir2;
+            } else {
+                echo "Warning: unable to delete Build $sDir2<br>";
+            };
+        }
+        
+        return $aDelete;
+    }
+    
+    /**
+     * get conmplete config of the project
+     * @return array
+     */
+    public function getConfig() {
+        return $this->_aPrjConfig;
+    }
+
+    /**
+     * get name/ label of the project
+     * @return string
+     */
+    public function getLabel() {
+        return $this->_aPrjConfig["label"];
+    }
+
+    /**
+     * get description of the project
+     * @return string
+     */
+    public function getDescription() {
+        return $this->_aPrjConfig["description"];
+    }
+
+    /**
+     * get deploy and queue infos for all phases
+     * @return type
+     */
+    public function getAllPhaseInfos() {
+        if (!array_key_exists("phases", $this->_aData))
+            $this->_aData["phases"] = array();
+
+        foreach (array_keys($this->_aConfig["phases"]) as $sPhase) {
+            if (!array_key_exists($sPhase, $this->_aData["phases"])) {
+                $this->getPhaseInfos($sPhase);
+            }
+        }
+        return $this->_aData["phases"];
+    }
+
+    /**
+     * get statusinfos of a named phase
+     * @param string $sPhase name of the phase; one of preview|stage|live
+     * @return array
+     */
+    public function getPhaseInfos($sPhase) {
+        if (!$sPhase){
+            die("ERROR: project->getPhaseInfos - parameter for phase is required");
+        }
+        if (!array_key_exists("phases", $this->_aData))
+            $this->_aData["phases"] = array();
+
+        if (!array_key_exists($sPhase, $this->_aData["phases"])) {
+
+            $this->_aData["phases"][$sPhase] = array();
+            $aTmp = array();
+
+            // a blocked package is waiting for deployment timeslot?
+            $sKey = "onhold";
+            $sJsonfile = $this->_getInfofile($sPhase, $sKey);
+            $aTmp[$sKey] = array();
+            if (file_exists($sJsonfile)) {
+                $aJson = json_decode(file_get_contents($sJsonfile), true);
+                if (array_key_exists("timestamp", $aJson)) {
+                    $aTmp[$sKey] = $aJson;
+                    $aTmp[$sKey]["infofile"] = $sJsonfile;
+                    $aTmp[$sKey]["version"] = $aJson["timestamp"];
+                    $aTmp[$sKey]["ok"] = 1;
+                } else {
+                    $aTmp[$sKey]["error"] = "info file $sJsonfile exists but is corrupt (no timestamp)." . print_r($aJson, true);
+                }
+            } else {
+                $aTmp[$sKey]["info"] = "No package is waiting in the queue.";
+                $aTmp[$sKey]["ok"] = 1;
+            }
+
+            // package for puppet
+            $sKey = "ready4deployment";
+            $sJsonfile = $this->_getInfofile($sPhase, $sKey);
+            $aTmp[$sKey] = array();
+            if (file_exists($sJsonfile)) {
+                $sPkgfile = $this->_getPackagefile($sPhase, $sKey);
+                if (file_exists($sPkgfile)) {
+                    $aJson = json_decode(file_get_contents($sJsonfile), true);
+                    if (array_key_exists("timestamp", $aJson)) {
+                        $aTmp[$sKey] = $aJson;
+                        $aTmp[$sKey]["infofile"] = $sJsonfile;
+                        $aTmp[$sKey]["packagefile"] = $sPkgfile;
+                        $aTmp[$sKey]["version"] = $aJson["timestamp"];
+                        $aTmp[$sKey]["ok"] = 1;
+                    } else {
+                        $aTmp[$sKey]["error"] = "info file $sJsonfile exists but is corrupt (no timestamp)." . print_r($aJson, true);
+                    }
+                } else {
+                    $aTmp[$sKey]["error"] = "package file was not found: $sPkgfile";
+                }
+            } else {
+                $aTmp[$sKey]["error"] = "info file was not found: $sJsonfile";
+            }
+
+            // published data
+            $sKey = "deployed";
+            $sJsonfile = $this->_getInfofile($sPhase, $sKey);
+            $aTmp[$sKey] = array();
+            if ($this->isActivePhase($sPhase)) {
+                $sJsonUrl = $this->_aPrjConfig["phases"][$sPhase]["url"] . $this->_aPrjConfig["fileprefix"] . ".json";
+                $sJsonData = @file_get_contents($sJsonUrl);
+                if ($sJsonData) {
+                    $aJson = json_decode($sJsonData, true);
+                    if (array_key_exists("timestamp", $aJson)) {
+                        $aTmp[$sKey] = $aJson;
+                        $aTmp[$sKey]["infofile"] = $sJsonUrl;
+                        $aTmp[$sKey]["version"] = $aJson["timestamp"];
+                        $aTmp[$sKey]["ok"] = 1;
+                    } else {
+                        $aTmp[$sKey]["error"] = "json url was readable <a href=$sJsonUrl>$sJsonUrl</a> but is corrupt (no timestamp)." . print_r($aJson, true);
+                    }
+                } else {
+                    $aTmp[$sKey]["error"] = "json url not readable <a href=$sJsonUrl>$sJsonUrl</a>";
+                }
+            } else {
+                $aTmp[$sKey]["warning"] = "this phase is not active or has no url";
+            }
+
+            $this->_aData["phases"][$sPhase] = $aTmp;
+        }
+        return $this->_aData["phases"][$sPhase];
+    }
+
+    /**
+     * check if the given phase is active for this project
+     * @param type $sPhase
+     * @return type
+     */
+    public function isActivePhase($sPhase) {
+        return (
+                array_key_exists($sPhase, $this->_aPrjConfig["phases"]) 
+                && array_key_exists("url", $this->_aPrjConfig["phases"][$sPhase]) 
+                && $this->_aPrjConfig["phases"][$sPhase]["url"]
+                );
+    }
+
+    /**
+     * find the next active phase of a project
+     * @param string $sPhase current phase; if empty the function sends back the first phase
+     */
+    public function getNextPhase($sPhase = false) {
+        if ($sPhase) {
+            if (!array_key_exists($sPhase, $this->_aConfig["phases"])) {
+                die("ERROR: this phase does not exist: $sPhase.");
+            }
+        }
+
+        $sNextPhase = false;
+        $bUseNextPhase = $sPhase ? false : true;
+        foreach (array_keys($this->_aConfig["phases"]) as $s) {
+            if ($bUseNextPhase) {
+                if ($this->isActivePhase($s)) {
+                    $sNextPhase = $s;
+                    $bUseNextPhase = false;
+                    continue;
+                }
+            }
+            if ($sPhase == $s) {
+                $bUseNextPhase = true;
+            }
+        }
+
+        return $sNextPhase;
+    }
+
+    /**
+     * check: is the deployment to the next phase enabled for this phase?
+     * @param type $sPhase
+     */
+    public function canAcceptPhase($sPhase = false) {
+
+        if (!$sPhase) {
+            $sNext = $this->getNextPhase($sPhase);
+            return $sNext > '';
+        }
+
+
+        if (!array_key_exists($sPhase, $this->_aConfig["phases"])) {
+            die("ERROR: in canDeploy this phase does not exist: $sPhase.");
+        }
+        if (!$this->isActivePhase($sPhase)) {
+            // die("ERROR: the phase $sPhase is not active in this project.");
+            return false;
+        }
+        $sNext = $this->getNextPhase($sPhase);
+        if (!$sNext)
+            return false;
+
+        // ensure that _aData is filled
+        $this->getPhaseInfos($sPhase);
+
+        // array key "ok" must be in the ready4deployment and deployed info
+        if (
+                array_key_exists($sPhase, $this->_aData["phases"]) && array_key_exists("onhold", $this->_aData["phases"][$sPhase]) && array_key_exists("ready4deployment", $this->_aData["phases"][$sPhase]) && array_key_exists("deployed", $this->_aData["phases"][$sPhase]) && array_key_exists("ok", $this->_aData["phases"][$sPhase]["onhold"]) && array_key_exists("ok", $this->_aData["phases"][$sPhase]["ready4deployment"]) && array_key_exists("ok", $this->_aData["phases"][$sPhase]["deployed"])
+        )
+            return true;
+
+        return false;
+    }
+
+    // ----------------------------------------------------------------------
+    // SETTER
+    // ----------------------------------------------------------------------
+
+    /**
+     * apply a config
+     * @param array $aConfig
+     * @return boolean
+      public function setConfig($aConfig) {
+      $this->_aConfig = $aConfig;
+      $this->_verifyConfig();
+      return true;
+      }
+     */
+
+    /**
+     * apply a config
+     * @param array $aConfig
+     * @return boolean
+     */
+    public function setProjectById($sId) {
+        $this->_aPrjConfig = array();
+        require(__dir__ . '/' . $this->_sCfgfile);
+        if (!array_key_exists("$sId", $aProjects)) {
+            die("ERROR: a project with ID $sId does not exist.");
+        }
+        $this->_aPrjConfig = $aProjects[$sId];
+        $this->_aConfig["id"] = $sId;
+        $this->_verifyConfig();
+        return true;
+    }
+
+    // ----------------------------------------------------------------------
+    // ACTIONS
+    // ----------------------------------------------------------------------
+/**
+ * get html code of a div around a message
+ * @param string $sWarnlevel one of error|success|info|warning to get a colored box
+ * @param string $sMessage   message txt
+ * @return string
+ */
+public function getBox($sWarnlevel, $sMessage){
+    $aCfg=array(
+        "error"=>array("class"=>"alert alert-error", "prefix"=>"ERROR :-("),
+        "success"=>array("class"=>"alert alert-success", "prefix"=>"SUCCESS :-)"),
+        "info"=>array("class"=>"alert alert-info", "prefix"=>"INFO"),
+        "warning"=>array("class"=>"alert alert-block", "prefix"=>"WARNING"),
+    );
+    $sClass="";
+    $sPrefix="";
+    if (array_key_exists($sWarnlevel, $aCfg)){
+        $sClass=$aCfg[$sWarnlevel]["class"];
+        $sPrefix=$aCfg[$sWarnlevel]["prefix"];
+        $sMessage='<strong>'.$aCfg[$sWarnlevel]["prefix"].'</strong> ' . $sMessage;
+    }
+    return '
+        <div class="'.$sClass.'">
+            '.$sMessage.'
+        </div>';
+    
+}
+
+    /**
+     * Build a new package for the deployment. It will be put to the queue
+     * of the first active phase (i.e. preview).
+     * If there is no deployment time range it will be deployed too.
+     * @global type $aParams
+     * @return boolean|string
+     */
+    public function build() {
+        global $aParams;
+        $sReturn = false;
+
+        $this->_iRcAll = 0;
+
+        // --------------------------------------------------
+        // create workdir
+        // --------------------------------------------------
+        $aDirs=$this->cleanupBuilds();
+        if (count($aDirs))
+            $sReturn.='<h2>Cleanup older failed builds</h2><pre>'.print_r($aDirs, true).'</pre>';
+        
+        $sTempDir = $this->_getTempDir();
+        $sFirstLevel = $this->getNextPhase();
+        if (!$sFirstLevel) return false;
+        
+        $sReturn.="<h2>Create a temporary build dir</h2>";
+        if (!file_exists($sTempDir)) {
+            $sReturn.=$this->_execAndSend("mkdir -p " . $sTempDir);
+        }
+        $sReturn.=$this->_execAndSend("ls -ld " . $sTempDir);
+        if (!file_exists($sTempDir)) {
+            return $this->getBox("error", "$sTempDir was not created.". $sReturn);
+        }
+
+
+        // --------------------------------------------------
+        // checkout
+        // --------------------------------------------------
+        switch ($this->_aPrjConfig["build"]["type"]) {
+            case "git":
+
+                $sReturn.="<h2>Checkout a GIT project</h2>";
+                // $sReturn.=$this->_execAndSend("find " . $this->_aConfig["workDir"]);
+                $sReturn.=$this->_execAndSend("cd $sTempDir && git init");
+
+                $sKeyfile = dirname(dirname(__file__)) . "/" . $this->_aPrjConfig["build"]["keyfile"];
+                $sWrapper = dirname(dirname(dirname(dirname(__file__)))) . "/shellscripts/gitsshwrapper.sh";
+                // $sReturn.=$this->_execAndSend("ls -l " . $sWrapper);
+                // $sReturn.=$this->_execAndSend("ls -l ".$sKeyfile);
+                $sReturn.=$this->_execAndSend("cd $sTempDir && export GIT_SSH=$sWrapper ; export PKEY=$sKeyfile; git pull " . $this->_aPrjConfig["build"]["ssh"]);
+
+                // $sVersion=$this->_execAndSend("cd $sTempDir && export GIT_SSH=$sWrapper ; export PKEY=$sKeyfile; git pull " . $this->_aPrjConfig["build"]["ssh"]);
+
+                // control: directory listing after checkout
+                $sReturn.=$this->_execAndSend("ls -lisa $sTempDir");
+
+                // fetch version infos
+                $sVersioninfo = str_replace("\n", "<br>", shell_exec("cd $sTempDir && git log -1"));
+                $sReturn.=$this->getBox("info", $sVersioninfo);
+
+                break;
+
+            default:
+                return $this->getBox("error", "Build Type not supported: " . $this->_aPrjConfig["build"]["type"]. $sReturn);
+        }
+        if (!$this->_iRcAll == 0) {
+            return $this->getBox("error", "checkout failed.</h3>One of the commands failed (see above).<br>You can ask the sysadmins and analyze with them the created temp directory &quot;' . $sTempDir . '&quot;.". $sReturn);
+        }
+        $sReturn.=$this->getBox("success", "Checkout OK!");
+
+        // --------------------------------------------------
+        // execute hook
+        // --------------------------------------------------
+        $sHookfile = $this->_aConfig['hooks']['build-aftercheckout'];
+        $sReturn.='<h2>Execute Hook ' . $sHookfile . '</h2>';
+        if (file_exists($sTempDir . '/' . $sHookfile)) {
+            $sReturn.=$this->_execAndSend('cd ' . $sTempDir . ' && chmod 755 hooks/on*');
+            $sReturn.=$this->_execAndSend('cd ' . $sTempDir . ' && "' . $sHookfile . '"');
+            if (!$this->_iRcAll == 0) {
+                return $this->getBox("error", "executing hook failed. One of the commands failed.<br>You can ask the sysadmins and analyze with them the created temp directory &quot;' . $sTempDir . '&quot;.". $sReturn);
+            }
+        } else {
+            $sReturn.='SKIP. Hook was not found.<br>';
+        }
+        
+
+        // --------------------------------------------------
+        // TODO: cleanup .git, .svn, ...?
+        // wenn es kein .git gibt, bricht er ab...
+        // --------------------------------------------------
+        $sReturn.="<h2>cleanup project</h2>";
+        $sReturn.=$this->_execAndSend("cd $sTempDir && rm -rf .git");
+        $sReturn.=$this->_execAndSend("cd $sTempDir && rm -rf .svn");
+
+
+        // public_html must exist
+        $sWebroot = $sTempDir . '/public_html';
+        if (!file_exists($sWebroot)) {
+            return $this->getBox("error", "a subdir &quot;public_html&quot; does not exist.". $sReturn);
+        }
+        
+        if (!$this->_iRcAll == 0) {
+            return $this->getBox("error", "build failed - working directory has errors and is not ready to create package.<br>You can ask the sysadmins and analyze with them the created temp directory &quot;$sTempDir&quot;". $sReturn);
+        }
+        $sReturn.=$this->getBox("success", "preparations ok - directory is ready for packaging now.");
+        
+        // --------------------------------------------------
+        // create package
+        // --------------------------------------------------
+        $sReturn.='<h2>Create package</h2>';
+
+        // generate info file
+        $sTs = date("Y-m-d H:i:s");
+        $sTs2 = date("Ymd_His");
+        $sInfoFileWebroot = $sWebroot . '/' . basename($this->_getInfofile($sFirstLevel, "deployed"));
+        $sInfoFileArchiv = $this->_getArchiveDir($sTs2) . '/' . basename($this->_getInfofile($sFirstLevel, "deployed"));
+        $sPackageFileArchiv = $this->_getArchiveDir($sTs2) . '/' . basename($this->_getPackagefile($sFirstLevel, "deployed"));
+
+        $sInfos = '{
+            "date": "' . $sTs . '",
+            "timestamp": "' . $sTs2 . '",
+            "revision": "' . $sVersioninfo . '",
+            "user": "' . $aParams["inputUser"] . '",
+            "remark": "' . $aParams["inputComment"] . '"
+        }';
+
+        
+        
+        $sReturn.="writing info file into webroot...<br>";
+        file_put_contents($sInfoFileWebroot, $sInfos);
+        $sReturn.=$this->_execAndSend("ls -l $sInfoFileWebroot");
+
+        if (!file_exists(dirname($sPackageFileArchiv))) {
+            $sReturn.="* create " . dirname($sPackageFileArchiv) . "<br>";
+            mkdir(dirname($sPackageFileArchiv), 0775, true);
+        }
+        $sReturn.=$this->_execAndSend("ls -ld " . dirname($sPackageFileArchiv));
+        if (!file_exists(dirname($sPackageFileArchiv))) {
+            return $this->getBox("error", "directory was not created: " . dirname($sPackageFileArchiv) . $sReturn);
+            return $sReturn;
+        }
+
+        $sReturn.="create archive $sPackageFileArchiv<br>";
+        $sReturn.=$this->_execAndSend("cd $sTempDir && tar -czf $sPackageFileArchiv .");
+        $sReturn.="writing info file into archive...<br>";
+        file_put_contents($sInfoFileArchiv, $sInfos);
+        $sReturn.="<br>Created Archive files:<br>";
+        $sReturn.=$this->_execAndSend("ls -l $sPackageFileArchiv $sInfoFileArchiv");
+
+        if (!$this->_iRcAll == 0) {
+            return $this->getBox("error", 'creation failed One of the commands failed (see below).<br>You can ask the sysadmins and analyze with them the created temp directory &quot;' . $sTempDir . '&quot;.' . $sReturn);
+        }
+        
+        
+        $sReturn.="<h2>cleanup $sTempDir</h2>";
+        $sReturn.="<h3>cleanup $sTempDir</h3>";
+        $sReturn.=$this->_execAndSend("rm -rf $sTempDir");
+        $sReturn.="<h3>cleanup Archive</h3>removing the oldest unused packages ...";
+        $sReturn.='<pre>'. print_r($this->cleanupArchive(), true).'</pre>';
+        
+        $sReturn.=$this->getBox("success", "Build finished successfully.");
+
+        // TODO: force synch archive to puppet master
+
+        $sReturn.=$this->queue($sFirstLevel, $sTs2);
+
+
+        
+        return $sReturn;
+    }
+
+    public function queue($sTargetphase, $sVersion) {
+
+        if (!$this->isActivePhase($sTargetphase)) return false;
+        
+        $sReturn="<h2>Queue to $sTargetphase</h2>";
+        $sPlace="onhold";
+
+        $sLinkTarget = $this->_getArchiveDir($sVersion);
+        $sLinkName = $this->_getFileBase($sTargetphase, $sPlace);
+
+        // --------------------------------------------------
+        // Checks
+        // --------------------------------------------------
+        if (!$sLinkName) {
+            die("ERROR: Queuing failed - sLinkName is empty.");
+        }
+        if (!$sLinkTarget) {
+            die("ERROR: Queuing  failed - sLinkTarget is empty.");
+        }
+        if (!file_exists($sLinkTarget)) {
+            die("ERROR: Queuing failed - version $sVersion is invalid. The directory $sLinkTarget does not exist.");
+        }
+
+        // --------------------------------------------------
+        // create the new link
+        // --------------------------------------------------
+        $this->_iRcAll = 0;
+        if (file_exists($sLinkName)) {
+            $sReturn.="removing existing version<br>";
+            $sReturn.=$this->_execAndSend("rm -f $sLinkName");
+        }
+        $sReturn.="linking to new version <br>";
+        $sReturn.=$this->_execAndSend("ln -s $sLinkTarget $sLinkName");
+        $sReturn.=$this->_execAndSend("ls -l $sLinkName | fgrep $sLinkTarget");
+
+
+        if (!$this->_iRcAll == 0) {
+            return $this->getBox("error", 'Queuing failed One of the commands failed.' . $sReturn);
+        }
+        $sReturn.=$this->getBox("success", "the version $sVersion was set to place $sPlace");
+        $sReturn.=$this->deploy($sTargetphase);
+        
+        return $sReturn;
+    }
+    
+    public function deploy($sTargetphase) {
+        if (!$this->isActivePhase($sTargetphase)) return false;
+        
+        $sReturn="<h2>Deploy to $sTargetphase</h2>";
+        
+        $sQueueLink = $this->_getFileBase($sTargetphase, "onhold");
+        $sRepoLink = $this->_getFileBase($sTargetphase, "ready4deployment");
+        
+        if (array_key_exists("deploytimes", $this->_aConfig["phases"][$sTargetphase])){
+            // check if the a deploy time is reached
+            $sNow=date("D H:i:s");
+            $sReturn.="check if one of the deployment times is reached and matches $sNow<br>";
+            $bCanDeploy=false;
+            foreach ($this->_aConfig["phases"][$sTargetphase]["deploytimes"] as $sRegex){
+                $sReturn.="... $sRegex<br>";
+                if (preg_match($sRegex, $sNow)){
+                    $bCanDeploy=true;
+                }
+            }
+            if (!$bCanDeploy){
+                $sReturn.="SKIP: deployment time was not reached.<br>";
+                return $sReturn;
+            }
+            $sReturn.="OK, deployment time was reached.<br>";
+            // if ()
+        }
+        if (!file_exists($sQueueLink)){
+            $sReturn.=$this->getBox("warning", "SKIP: nothing to do - the current queue is empty ($sQueueLink does not exist).");
+            return $sReturn;
+        }
+        if (!file_exists($sQueueLink)){
+            $sReturn.="SKIP: no current queue - $sQueueLink does not exist.";
+            return $sReturn;
+        }
+        
+
+        // --------------------------------------------------
+        // move the queue link to the repo name
+        // --------------------------------------------------
+        $this->_iRcAll = 0;
+        if (file_exists($sRepoLink)) {
+            $sReturn.="removing existing version<br>";
+            $sReturn.=$this->_execAndSend("rm -f $sRepoLink");
+        }
+        $sReturn.="moving queue to repo<br>";
+        $sReturn.=$this->_execAndSend("mv $sQueueLink $sRepoLink");
+
+
+        if (!$this->_iRcAll == 0) {
+            return $this->getBox("error", "Deployment failed - One of the commands failed." . $sReturn);
+        }
+        
+        // TODO: force synch to puppet master
+        
+        $sReturn.=$this->getBox("success", "SUCCESS: deployment was done and will be installed soon.");
+        return $sReturn;
+    }
+
+}
+
 ?>
\ No newline at end of file
diff --git a/public_html/deployment/classes/projectlist.class.php b/public_html/deployment/classes/projectlist.class.php
index 7a628404..bc43c487 100644
--- a/public_html/deployment/classes/projectlist.class.php
+++ b/public_html/deployment/classes/projectlist.class.php
@@ -1,254 +1,259 @@
-<?php
-
-require_once 'project.class.php';
-
-/**
- * class for project overview
- */
-class projectlist {
-
-    // ----------------------------------------------------------------------
-    // CONFIG
-    // ----------------------------------------------------------------------
-    protected $_aProjects = array();
-    protected $_aPhases = array("preview", "stage", "live");
-    protected $_sCfgfile="../config/inc_projects_config.php";
-
-    // ----------------------------------------------------------------------
-    // constructor
-    // ----------------------------------------------------------------------
-
-    /**
-     * constructor
-     * @param array $aProjects array with all projects (overrides config data)
-     */
-    public function __construct($aProjects = false) {
-        $this->_readConfig();
-        if ($aProjects) {
-            $this->setConfig($aProjects);
-        }
-    }
-
-    // ----------------------------------------------------------------------
-    // private functions
-    // ----------------------------------------------------------------------
-
-    /**
-     * read config file
-     * @return boolean
-     */
-    private function _readConfig() {
-        $this->_aProjects = array();
-        require(__dir__ . '/' . $this->_sCfgfile);
-        $this->_aProjects = $aProjects;
-        $this->_aPhases = $aConfig["phases"];
-        $this->_verifyConfig();
-        return true;
-    }
-
-    /**
-     * validate config data
-     * @return boolean
-     */
-    private function _verifyConfig() {
-        if (!count($this->_aProjects)) {
-            die("ERROR: no project was found.");
-        }
-        // TODO: verify 
-        return true;
-    }
-
-    // ----------------------------------------------------------------------
-    // GETTER
-    // ----------------------------------------------------------------------
-
-    /**
-     * get list of all project IDs
-     * @return type
-     */
-    public function getProjects() {
-        return array_keys($this->_aProjects);
-    }
-
-    // ----------------------------------------------------------------------
-    // SETTER
-    // ----------------------------------------------------------------------
-
-    /**
-     * applay a custom project array - TODO: DLETE??
-     * @param array $aProjects
-     * @return boolean
-     */
-    public function setConfig($aProjects) {
-        $this->_aProjects = $aProjects;
-        $this->_verifyConfig();
-        return true;
-    }
-
-    // ----------------------------------------------------------------------
-    // ACTIONS
-    // ----------------------------------------------------------------------
-
-    /**
-     * render html for overview table
-     * @return string
-     */
-    public function renderOverview() {
-        $sOut = '';
-        $oPrj = false;
-        
-        $sDebug = '';
-
-        // loop over projects
-        foreach ($this->_aProjects as $sPrj => $aData) {
-            $oPrj = new project($sPrj);
-
-            $aPrjData = $oPrj->getAllPhaseInfos();
-            $sDebug .= "<h2>$sPrj</h2>CONFIG: <pre>".print_r($oPrj->getConfig(), true)."</pre> DATA: <pre>" . print_r($aPrjData, true) . "</pre><hr>";
-
-            $sOutPhases = '';
-
-            // loop over phases ...
-            foreach (array_keys($this->_aPhases) as $sPhase) {
-                $aTmp = $aPrjData[$sPhase];
-                $h = "onhold";
-                $q = "ready4deployment";
-                $d = "deployed";
-
-                if (!$oPrj->isActivePhase($sPhase)) {
-                    $sOutPhases.='
-                        <td class="' . $sPhase . '" colspan="3">
-                            <div class="versioninfo center inactive"><i class="icon-ban-circle"></i> inactive</div>
-                        </td>';
-                } else {
-
-                    // --- on hold
-                    $sInfoH = '';
-                    if (array_key_exists("ok", $aTmp[$h]) && array_key_exists("timestamp", $aTmp[$h])) {
-                            $sInfoH = '<div class="versioninfo">
-                                        <i class="icon-calendar"></i> ' . $aTmp[$h]["date"] . '<br>
-                                        <i class="icon-tag"></i> ' . $aTmp[$h]["revision"] . '<br>
-                                        Anm.: ' . $aTmp[$h]["remark"] . '<br>';
-                        if (array_key_exists("deploytimes", $this->_aPhases[$sPhase]) || true){
-                            $sInfoH .= '<br>Deployment: '.  implode(",", array_values($this->_aPhases[$sPhase]["deploytimes"]));
-                        }
-                        $sInfoH .= '
-                                <br>
-                                <a href="/deployment/'.$sPrj.'/deploy/'.$sPhase.'/" class="btn '.$sPhase.'"><i class=" icon-forward"></i> Deploy to '.$sPhase.'-Queue</a>
-                            </div>';
-                    } else {
-                        if (array_key_exists("error", $aTmp[$h])) {
-                            $sInfoH = '<div class="error"><i class="icon-exclamation-sign"></i> FEHLER:<br>' . $aTmp[$h]["error"] . '</div>';
-                        } else {
-                            $sInfoH = 'empty';
-                        }
-                    }
-                    
-                    // --- ready4deployment have the same version?
-                    $sInfoQ = '';
-                    if (array_key_exists("ok", $aTmp[$q]) && array_key_exists("ok", $aTmp[$d]) && $aTmp[$q]["timestamp"] > " " && $aTmp[$q]["timestamp"] == $aTmp[$d]["timestamp"]) {
-                        $sInfoQ = "deployed";
-                    } else {
-                        if (array_key_exists("ok", $aTmp[$q])) {
-                            $sInfoQ = '<div class="versioninfo">
-                                        <i class="icon-calendar"></i> ' . $aTmp[$q]["date"] . '<br>
-                                        <i class="icon-tag"></i> ' . $aTmp[$q]["revision"] . '<br>
-                                        Anm.: ' . $aTmp[$q]["remark"] . '
-                                    </div>';
-                        } else {
-                            if (array_key_exists("error", $aTmp[$q])) {
-                                $sInfoQ = '<div class="error"><i class="icon-exclamation-sign"></i> FEHLER:<br>' . $aTmp[$q]["error"] . '</div>';
-                            }
-                        }
-                    }
-
-                    // -- deployment infos
-                    $sInfoD = '';
-                    if (array_key_exists("ok", $aTmp[$d])){
-                        $sInfoD = '
-                            <i class="icon-calendar"></i> ' . $aTmp[$d]["date"] . '<br>
-                            <i class="icon-tag"></i> ' . $aTmp[$d]["revision"] . '<br>
-                            Anm.: ' . $aTmp[$d]["remark"] . '
-                        ';
-                    }
-                    if (array_key_exists("warning", $aTmp[$d])) {
-                        $sInfoD = '<div class="warning"><i class="icon-info-sign"></i> WARNUNG:<br>' . $aTmp[$d]["warning"] . '</div>';
-                    }
-                    if (array_key_exists("error", $aTmp[$d])) {
-                        $sInfoD = '<div class="error"><i class="icon-exclamation-sign"></i> FEHLER:<br>' . $aTmp[$d]["error"] . '</div>';
-                    }
-
-                    if ($oPrj->canAcceptPhase($sPhase)){
-                        $sNext=$oPrj->getNextPhase($sPhase);
-                        $sInfoD.='<br><a href="/deployment/'.$sPrj.'/accept/'.$sPhase.'/" class="btn '.$sNext.'"><i class=" icon-forward"></i> Accept f&uuml;r ['.$sNext.']</a>';
-                    } 
-
-                    // output
-                    $sOutPhases.='
-                        <td class="' . $sPhase . '">
-                            <div class="versioninfo">
-                                ' . $sInfoH . '
-                            </div>
-                        </td>
-                        <td class="' . $sPhase . '">
-                            <div class="versioninfo">
-                                ' . $sInfoQ . '
-                            </div>
-                        </td>
-                        <td class="' . $sPhase . '">
-                            <div class="versioninfo">
-                                ' . $sInfoD . '
-                            </div>
-                        </td>
-                        ';
-                }
-            }
-
-
-            // render output
-            $sOut.='
-                <tr>
-                    <td class="prj">
-                        <strong>' . $oPrj->getLabel() . '</strong><br>
-                        ' . $oPrj->getDescription() . '<br>';
-            if ($oPrj->canAcceptPhase()){
-                $sNext=$oPrj->getNextPhase();
-                $sOut.='<a href="/deployment/'.$sPrj.'/build/" class="btn '.$sNext.'"><i class=" icon-forward"></i> Build f&uuml;r ['.$sNext.']</a>';
-            }
-            $sOut.='</td>
-                ' . $sOutPhases . '
-                </tr>';
-        }
-        if ($sOut)
-            $sOut = '
-		<table class="table" id="tbloverview">
-			<thead>
-				<tr>
-					<th class="prj">Projekt</th>
-					<th class="preview" colspan="3" >Preview</th>
-					<th class="stage" colspan="3"   >Stage</th>
-					<th class="live" colspan="3"    >Live</th>
-				</tr>
-                                <tr>
-                                    <td></td>
-                                    <td class="preview">Queue</td>
-                                    <td class="preview">Repo</td>
-                                    <td class="preview">Installiert</td>
-                                    <td class="stage">Queue</td>
-                                    <td class="stage">Repo</td>
-                                    <td class="stage">Installiert</td>
-                                    <td class="live">Queue</td>
-                                    <td class="live">Repo</td>
-                                    <td class="live">Installiert</td>
-                                </tr>
-			</thead>
-			<tbody>
-		' . $sOut . '</tbody></table>';
-
-        $sOut.=$sDebug;
-        return $sOut;
-    }
-
-}
-
+<?php
+
+require_once 'project.class.php';
+
+/**
+ * class for project overview
+ */
+class projectlist {
+
+    // ----------------------------------------------------------------------
+    // CONFIG
+    // ----------------------------------------------------------------------
+    protected $_aProjects = array();
+    protected $_aPhases = array("preview", "stage", "live");
+    protected $_sCfgfile="../config/inc_projects_config.php";
+
+    // ----------------------------------------------------------------------
+    // constructor
+    // ----------------------------------------------------------------------
+
+    /**
+     * constructor
+     * @param array $aProjects array with all projects (overrides config data)
+     */
+    public function __construct($aProjects = false) {
+        $this->_readConfig();
+        if ($aProjects) {
+            $this->setConfig($aProjects);
+        }
+    }
+
+    // ----------------------------------------------------------------------
+    // private functions
+    // ----------------------------------------------------------------------
+
+    /**
+     * read config file
+     * @return boolean
+     */
+    private function _readConfig() {
+        $this->_aProjects = array();
+        require(__dir__ . '/' . $this->_sCfgfile);
+        $this->_aProjects = $aProjects;
+        $this->_aPhases = $aConfig["phases"];
+        $this->_verifyConfig();
+        return true;
+    }
+
+    /**
+     * validate config data
+     * @return boolean
+     */
+    private function _verifyConfig() {
+        if (!count($this->_aProjects)) {
+            die("ERROR: no project was found.");
+        }
+        // TODO: verify 
+        return true;
+    }
+
+    // ----------------------------------------------------------------------
+    // GETTER
+    // ----------------------------------------------------------------------
+
+    /**
+     * get list of all project IDs
+     * @return type
+     */
+    public function getProjects() {
+        return array_keys($this->_aProjects);
+    }
+
+    // ----------------------------------------------------------------------
+    // SETTER
+    // ----------------------------------------------------------------------
+
+    /**
+     * applay a custom project array - TODO: DLETE??
+     * @param array $aProjects
+     * @return boolean
+     */
+    public function setConfig($aProjects) {
+        $this->_aProjects = $aProjects;
+        $this->_verifyConfig();
+        return true;
+    }
+
+    // ----------------------------------------------------------------------
+    // ACTIONS
+    // ----------------------------------------------------------------------
+
+    /**
+     * render html for overview table
+     * @return string
+     */
+    public function renderOverview() {
+        $sOut = '';
+        $oPrj = false;
+
+        $sNavi='';
+        $sNavi.='<option value="trproject">Alle</option>';
+        // loop over projects
+        foreach ($this->_aProjects as $sPrj => $aData) {
+            
+            
+            $oPrj = new project($sPrj);
+
+            $sNavi.='<option value="'.$sPrj.'">'.$oPrj->getLabel().'</option>';
+            $aPrjData = $oPrj->getAllPhaseInfos();
+
+            $sOutPhases = '';
+
+            // loop over phases ...
+            foreach (array_keys($this->_aPhases) as $sPhase) {
+                $aTmp = $aPrjData[$sPhase];
+                $h = "onhold";
+                $q = "ready4deployment";
+                $d = "deployed";
+
+                if (!$oPrj->isActivePhase($sPhase)) {
+                    $sOutPhases.='
+                        <td class="' . $sPhase . '" colspan="3">
+                            <div class="versioninfo center inactive"><i class="icon-ban-circle"></i> inactive</div>
+                        </td>';
+                } else {
+
+                    // --- on hold
+                    $sInfoH = '';
+                    if (array_key_exists("ok", $aTmp[$h]) && array_key_exists("timestamp", $aTmp[$h])) {
+                            $sInfoH = '<div class="versioninfo">
+                                        <i class="icon-calendar"></i> ' . $aTmp[$h]["date"] . '<br>
+                                        <i class="icon-tag"></i> ' . $aTmp[$h]["revision"] . '<br>
+                                        Anm.: ' . $aTmp[$h]["remark"] . '<br>';
+                        if (array_key_exists("deploytimes", $this->_aPhases[$sPhase]) || true){
+                            $sInfoH .= '<br>Deployment: '.  implode(",", array_values($this->_aPhases[$sPhase]["deploytimes"]));
+                        }
+                        $sInfoH .= '
+                                <br>
+                                <a href="/deployment/'.$sPrj.'/deploy/'.$sPhase.'/" class="btn '.$sPhase.'"><i class=" icon-forward"></i> Deploy to '.$sPhase.'-Queue</a>
+                            </div>';
+                    } else {
+                        if (array_key_exists("error", $aTmp[$h])) {
+                            $sInfoH = '<div class="error"><i class="icon-exclamation-sign"></i> FEHLER:<br>' . $aTmp[$h]["error"] . '</div>';
+                        } else {
+                            $sInfoH = 'empty';
+                        }
+                    }
+                    
+                    // --- ready4deployment have the same version?
+                    $sInfoQ = '';
+                    if (array_key_exists("ok", $aTmp[$q]) && array_key_exists("ok", $aTmp[$d]) && $aTmp[$q]["timestamp"] > " " && $aTmp[$q]["timestamp"] == $aTmp[$d]["timestamp"]) {
+                        $sInfoQ = "deployed";
+                    } else {
+                        if (array_key_exists("ok", $aTmp[$q])) {
+                            $sInfoQ = '<div class="versioninfo">
+                                        <i class="icon-calendar"></i> ' . $aTmp[$q]["date"] . '<br>
+                                        <i class="icon-tag"></i> ' . $aTmp[$q]["revision"] . '<br>
+                                        Anm.: ' . $aTmp[$q]["remark"] . '
+                                    </div>';
+                        } else {
+                            if (array_key_exists("error", $aTmp[$q])) {
+                                $sInfoQ = '<div class="error"><i class="icon-exclamation-sign"></i> FEHLER:<br>' . $aTmp[$q]["error"] . '</div>';
+                            }
+                        }
+                    }
+
+                    // -- deployment infos
+                    $sInfoD = '';
+                    if (array_key_exists("ok", $aTmp[$d])){
+                        $sInfoD = '
+                            <i class="icon-calendar"></i> ' . $aTmp[$d]["date"] . '<br>
+                            <i class="icon-tag"></i> ' . $aTmp[$d]["revision"] . '<br>
+                            Anm.: ' . $aTmp[$d]["remark"] . '
+                        ';
+                    }
+                    if (array_key_exists("warning", $aTmp[$d])) {
+                        $sInfoD = '<div class="warning"><i class="icon-info-sign"></i> WARNUNG:<br>' . $aTmp[$d]["warning"] . '</div>';
+                    }
+                    if (array_key_exists("error", $aTmp[$d])) {
+                        $sInfoD = '<div class="error"><i class="icon-exclamation-sign"></i> FEHLER:<br>' . $aTmp[$d]["error"] . '</div>';
+                    }
+
+                    if ($oPrj->canAcceptPhase($sPhase)){
+                        $sNext=$oPrj->getNextPhase($sPhase);
+                        $sInfoD.='<br><a href="/deployment/'.$sPrj.'/accept/'.$sPhase.'/" class="btn '.$sNext.'"><i class=" icon-forward"></i> Accept f&uuml;r ['.$sNext.']</a>';
+                    } 
+
+                    // output
+                    $sOutPhases.='
+                        <td class="' . $sPhase . '">
+                            <div class="versioninfo">
+                                ' . $sInfoH . '
+                            </div>
+                        </td>
+                        <td class="' . $sPhase . '">
+                            <div class="versioninfo">
+                                ' . $sInfoQ . '
+                            </div>
+                        </td>
+                        <td class="' . $sPhase . '">
+                            <div class="versioninfo">
+                                ' . $sInfoD . '
+                            </div>
+                        </td>
+                        ';
+                }
+            }
+
+
+            // render output
+            $sOut.='
+                <tr class="'.$sPrj.' trproject">
+                    <td class="prj">
+                        <a href="/deployment/'.$sPrj.'/" class="btn "><i class=" icon-book"></i> '.$oPrj->getLabel().'</a><br>
+                        ' . $oPrj->getDescription() . '<br>';
+            if ($oPrj->canAcceptPhase()){
+                $sNext=$oPrj->getNextPhase();
+                $sOut.='<a href="/deployment/'.$sPrj.'/build/" class="btn '.$sNext.'"><i class=" icon-forward"></i> Build f&uuml;r ['.$sNext.']</a>';
+            }
+            $sOut.='</td>
+                ' . $sOutPhases . '
+                </tr>';
+        }
+        if ($sOut)
+            $sOut = '
+                Projekt-Filter: 
+                <select onchange="$(\'.trproject\').hide(); $(\'.\' + this.value).show();">
+                    '.$sNavi.'
+                </select>
+		<table class="table" id="tbloverview">
+			<thead>
+				<tr>
+					<th class="prj">Projekt</th>
+					<th class="preview" colspan="3" >Preview</th>
+					<th class="stage" colspan="3"   >Stage</th>
+					<th class="live" colspan="3"    >Live</th>
+				</tr>
+                                <tr>
+                                    <td></td>
+                                    <td class="preview">Queue</td>
+                                    <td class="preview">Repo</td>
+                                    <td class="preview">Installiert</td>
+                                    <td class="stage">Queue</td>
+                                    <td class="stage">Repo</td>
+                                    <td class="stage">Installiert</td>
+                                    <td class="live">Queue</td>
+                                    <td class="live">Repo</td>
+                                    <td class="live">Installiert</td>
+                                </tr>
+			</thead>
+			<tbody>
+		' . $sOut . '</tbody></table>';
+
+        return $sOut;
+    }
+
+}
+
 ?>
\ No newline at end of file
diff --git a/public_html/deployment/config/inc_projects_config.php b/public_html/deployment/config/inc_projects_config.php
index a554ba65..4a4cbf6d 100644
--- a/public_html/deployment/config/inc_projects_config.php
+++ b/public_html/deployment/config/inc_projects_config.php
@@ -1,146 +1,154 @@
-<?php
-
-
-// ----------------------------------------------------------------------
-// fetch status infos von den einzelnen Phasen
-// ----------------------------------------------------------------------
-
-$aConfig=array(
-    
-    // Basispfad:
-    'workDir'=>'/var/imldeployment',
-    'hooks'=>array(
-        'build-aftercheckout'=>'hooks/build.sh',
-    ),
-    
-    
-    'phases'=>array(
-        "preview"=>array(
-            // "deploytimes"=>array('/(Mon|Tue|Wed|Thu)\ 4/'),
-        ),
-        "stage"=>array(),
-        "live"=>array(
-            // Als Idee ... Datum mit Regex filtern
-            // wenn deploytimes existiert, dann wird nach dem Deploy das Paket 
-            // in einer Queue zurueckgehalten
-            "deploytimes"=>array('/(Mon|Tue|Wed|Thu)\ 14\:/'),
-        ),
-     ),
-);
-
-
-// ----------------------------------------------------------------------
-// Projekte
-// ----------------------------------------------------------------------
-$aProjects=array(
-	"ci"=>array(
-		"label"=>"CI Webgui",
-		"fileprefix"=>"ci-webgui",
-		"description" => 'Webgui zum Deployen von Web-Projekten',
-		"contact" => 'axel.hahn@iml.unibe.ch',
-		"build"=>array(
-			"type"=>"git", // one of git, svn, ...
-			"ssh"=>"git@gitlab.iml.unibe.ch:admins/imldeployment.git",
-                    
-                        // der public Key muss beim Git-Repo hinterlegt sein
-			"keyfile"=>"config/sshkeys/git@gitlab.iml.unibe.ch",
-                ),
-
-		"phases"=>array(
-                    "preview"=>array(
-                        // "url"=>"http://preview.scrudu.iml.unibe.ch/",
-                        "url"=>"http://ci.iml.unibe.ch/deployment/",
-                    ),
-                    "stage"=>array(
-                    ),
-                    "live"=>array(
-                        // "url"=>"http://www.scrudu.iml.unibe.ch/"
-                    ),
-                ),
-	),
-	"scrudu"=>array(
-		"label"=>"SCRUDU",
-		"fileprefix"=>"scrudu",
-		"description" => 'Das sagenumwogene Scrudu Dings',
-		"contact" => 'Entwickler, Verantwortliche etc.',
-		"build"=>array(
-			"type"=>"git", // one of git, svn, ...
-                        // for github:
-                        // generate ssh keypair
-                        // https://help.github.com/articles/generating-ssh-keys
-			"ssh"=>"ssh gituser@gitserver:/path/to/project",
-                        // der public Key muss beim Git-Repo hinterlegt sein
-			"keyfile"=>"config/sshkeys/git@gitlab.iml.unibe.ch",
-                ),
-
-		"phases"=>array(
-                    "preview"=>array(
-                        // "url"=>"http://preview.scrudu.iml.unibe.ch/",
-                    ),
-                    "stage"=>array(
-                        // "url"=>"http://stage.scrudu.iml.unibe.ch/"
-                    ),
-                    "live"=>array(
-                        // "url"=>"http://www.scrudu.iml.unibe.ch/"
-                    ),
-                ),
-	),
-	"prj1"=>array(
-		"label"=>"Test-Projekt.",
-		"fileprefix"=>"testprj",
-		"description" => 'Eine kurze Beschreibung, um was für eine Applikation es sich handelt',
-		"contact" => 'Entwickler, Verantwortliche etc.',
-		"build"=>array(
-			"type"=>"git", // one of git, svn, ...
-			"url"=>"ssh gituser@gitserver:/path/to/project",
-		),
-		
-		"phases"=>array(),
-	),
-);
-
-// ----------------------------------------------------------------------
-// Checkout-Informationen fuer versch. Sourcen
-// ----------------------------------------------------------------------
-$aTypeAbstraction=array(
-
-	"git"=>array(
-		"checkout"=>"git pull [url]",
-		"getrevision"=>"",
-		"built"=>"[workdir]/buildpackage.sh",
-		"deleteDirs"=>array(
-			".git",
-			"buildpackage.sh"
-		),
-	),
-	/*
-	"svn"=>array(
-		"checkout"=>"svn co [url]",
-		"getrevision"=>"svn info | grep '^Revision' | cut -f 2 -d ':' ",
-		"built"=>false,
-		"deleteDirs"=>".svn",
-	),
-	*/
-	
-);
-
-// ----------------------------------------------------------------------
-// override for local development
-// ----------------------------------------------------------------------
-
-switch ($_SERVER["SERVER_NAME"]) {
-    case "localhost":
-    case "dev.ci.iml.unibe.ch":
-        $aConfig['workDir']="D:\imldeployment";
-        $aProjects["scrudu"]["phases"]["preview"]["url"]="http://localhost/deployment/";
-        break;
-
-    default:
-        break;
-}
-
-
-$aConfig=array_merge($aConfig, array(
-    'packageDir'=>$aConfig['workDir'].'/packages',
-    'archiveDir'=>$aConfig['workDir'].'/packages/_files',
-));
+<?php
+
+
+// ----------------------------------------------------------------------
+// fetch status infos von den einzelnen Phasen
+// ----------------------------------------------------------------------
+
+$aConfig=array(
+    
+    // Basispfad:
+    'workDir'=>'/var/imldeployment',
+    'versionsToKeep'=>10, // for cleanup: keep n unused versions
+    'builtsToKeep'=>3,    // for cleanup: keep n failed builds
+    'hooks'=>array(
+        'build-aftercheckout'=>'hooks/onbuild',
+    ),
+    
+    // rsync of archives
+    'mirrorPackages'=>array(
+        'puppet'=>'ladmin@calcium.iml.unibe.ch:/share/imldeployment',
+    ),
+    
+    
+    'phases'=>array(
+        "preview"=>array(
+            // "deploytimes"=>array('/(Mon|Tue|Wed|Thu)\ 4/'),
+        ),
+        "stage"=>array(),
+        "live"=>array(
+            // wenn deploytimes existiert, dann wird nach dem Deploy das Paket 
+            // in einer Queue zurueckgehalten
+            "deploytimes"=>array('/(Mon|Tue|Wed|Thu)\ 14\:/'),
+        ),
+     ),
+);
+
+
+// ----------------------------------------------------------------------
+// Projekte
+// ----------------------------------------------------------------------
+$aProjects=array(
+	"ci"=>array(
+		"label"=>"CI Webgui",
+		"fileprefix"=>"ci-webgui",
+		"description" => 'Webgui zum Deployen von Web-Projekten',
+		"contact" => 'axel.hahn@iml.unibe.ch',
+		"build"=>array(
+			"type"=>"git", // one of git, svn, ...
+			"ssh"=>"git@gitlab.iml.unibe.ch:admins/imldeployment.git",
+                    
+                        // der public Key muss beim Git-Repo hinterlegt sein
+			"keyfile"=>"config/sshkeys/git@gitlab.iml.unibe.ch",
+                ),
+
+		"phases"=>array(
+                    "preview"=>array(
+                        // "url"=>"http://preview.scrudu.iml.unibe.ch/",
+                        "url"=>"http://ci.iml.unibe.ch/deployment/",
+                    ),
+                    "stage"=>array(
+                    ),
+                    "live"=>array(
+                        // "url"=>"http://www.scrudu.iml.unibe.ch/"
+                    ),
+                ),
+	),
+	"scrudu"=>array(
+		"label"=>"SCRUDU",
+		"fileprefix"=>"scrudu",
+		"description" => 'Das sagenumwogene Scrudu Dings',
+		"contact" => 'Entwickler, Verantwortliche etc.',
+		"build"=>array(
+			"type"=>"git", // one of git, svn, ...
+                        // for github:
+                        // generate ssh keypair
+                        // https://help.github.com/articles/generating-ssh-keys
+			"ssh"=>"git@github.com:iml-it/scrudu.git",
+                        // der public Key muss beim Git-Repo hinterlegt sein
+			"keyfile"=>"config/sshkeys/git@gitlab.iml.unibe.ch",
+                ),
+
+		"phases"=>array(
+                    "preview"=>array(
+                        "url"=>"http://preview.scrudu.iml.unibe.ch/",
+                    ),
+                    "stage"=>array(
+                        // "url"=>"http://stage.scrudu.iml.unibe.ch/"
+                    ),
+                    "live"=>array(
+                        // "url"=>"http://www.scrudu.iml.unibe.ch/"
+                    ),
+                ),
+	),
+	"prj1"=>array(
+		"label"=>"Test-Projekt.",
+		"fileprefix"=>"testprj",
+		"description" => 'Eine kurze Beschreibung, um was für eine Applikation es sich handelt',
+		"contact" => 'Entwickler, Verantwortliche etc.',
+		"build"=>array(
+			"type"=>"git", // one of git, svn, ...
+			"url"=>"ssh gituser@gitserver:/path/to/project",
+		),
+		
+		"phases"=>array(),
+	),
+);
+
+// ----------------------------------------------------------------------
+// Checkout-Informationen fuer versch. Sourcen
+// ----------------------------------------------------------------------
+$aTypeAbstraction=array(
+
+	"git"=>array(
+		"checkout"=>"git pull [url]",
+		"getrevision"=>"",
+		"built"=>"[workdir]/buildpackage.sh",
+		"deleteDirs"=>array(
+			".git",
+			"buildpackage.sh"
+		),
+	),
+	/*
+	"svn"=>array(
+		"checkout"=>"svn co [url]",
+		"getrevision"=>"svn info | grep '^Revision' | cut -f 2 -d ':' ",
+		"built"=>false,
+		"deleteDirs"=>".svn",
+	),
+	*/
+	
+);
+
+// ----------------------------------------------------------------------
+// override for local development
+// ----------------------------------------------------------------------
+
+if ($_SERVER && array_key_exists("SERVER_NAME", $_SERVER)){
+    switch ($_SERVER["SERVER_NAME"]) {
+        case "localhost":
+        case "dev.ci.iml.unibe.ch":
+            $aConfig['workDir']="D:\imldeployment";
+            $aProjects["scrudu"]["phases"]["preview"]["url"]="http://localhost/deployment/";
+            break;
+
+        default:
+            break;
+    }
+}
+
+$aConfig=array_merge($aConfig, array(
+    'buildDir'=>$aConfig['workDir'].'/build',
+    'packageDir'=>$aConfig['workDir'].'/packages',
+    'archiveDir'=>$aConfig['workDir'].'/packages/_files',
+));
diff --git a/public_html/deployment/gen__htusers.bat b/public_html/deployment/gen__htusers.bat
new file mode 100644
index 00000000..282452ca
--- /dev/null
+++ b/public_html/deployment/gen__htusers.bat
@@ -0,0 +1,9 @@
+@echo off
+
+set outfile=%~dp0.htusers
+
+if not exist %outfile% echo.>%outfile%
+C:\xampp\apache\bin\htpasswd.exe -b %outfile% iml deployment
+echo %outfile%
+type %outfile%
+timeout 20
\ No newline at end of file
diff --git a/public_html/deployment/inc_functions.php b/public_html/deployment/inc_functions.php
index c687339f..5ad8f098 100644
--- a/public_html/deployment/inc_functions.php
+++ b/public_html/deployment/inc_functions.php
@@ -1,50 +1,115 @@
-<?php
-
-global $aParams;
-$aParams=array();
-if (count($_GET))  foreach($_GET as $key=>$value)  $aParams[$key]=$value;
-if (count($_POST)) foreach($_POST as $key=>$value) $aParams[$key]=$value;
-
-/*
-print_r($_GET);
-print_r($_POST);
-print_r($aParams);
-*/
-
-/**
- * get link as home button
- * @return string
- */
-function aHome(){
-    return '<a href="/deployment/?" class="btn"><i class="icon-home"></i> zur &Uuml;bersicht</a>';
-}
-
-
-function enterDeployinfos(){
-    global $aParams;
-    $sIdUser="inputUser";
-    $sIdComment="inputComment";
-    $sUser=(array_key_exists($sIdUser, $aParams))?$aParams[$sIdUser]:"";
-    $sComment=(array_key_exists($sIdComment, $aParams))?$aParams[$sIdComment]:"";
-    
-    $sOut='
-        <div class="control-group">
-            <label class="control-label" for="inputUser">Benutzername</label>
-            <div class="controls">
-                <input type="text" id="inputUser" name="inputUser" placeholder="Benutzername" value="'.$sUser.'">
-            </div>
-        </div>
-        <div class="control-group">
-            <label class="control-label" for="inputComment">Kommentar zum Deployment</label>
-            <div class="controls">
-                <textarea id= rows="3" id="inputComment" name="inputComment" placeholder="Kommentar">'.$sComment.'</textarea>
-            </div>
-        </div>
-        ';
-    
-    return $sOut;
-}
-
-
-
-?>
+<?php
+
+global $aParams;
+$aParams=array();
+if (count($_GET))  foreach($_GET as $key=>$value)  $aParams[$key]=$value;
+if (count($_POST)) foreach($_POST as $key=>$value) $aParams[$key]=$value;
+
+/*
+print_r($_GET);
+print_r($_POST);
+print_r($aParams);
+*/
+
+/**
+ * get home link as button
+ * @return string
+ */
+function aHome(){
+    global $aParams;
+    if (!array_key_exists("prj", $aParams)) return false;
+    return '<a href="/deployment/?" class="btn"><i class="icon-home"></i> zur &Uuml;bersicht</a>';
+}
+/**
+ * get project Home link as button
+ * @return string
+ */
+function aPrjHome(){
+    global $aParams;
+    if (!array_key_exists("prj", $aParams)) return false;
+    if (!array_key_exists("action", $aParams)) return false;
+    
+    require_once("./config/inc_projects_config.php");
+    require_once("./classes/project.class.php");
+    $oPrj=new project($aParams["prj"]);
+    return '<a href="/deployment/'.$aParams["prj"].'/" class="btn"><i class="icon-book"></i> Projekt-Home '.$oPrj->getLabel().' </a>';
+}
+
+/**
+ * auto generate upper part of the page with header and navigation 
+ * @global type $aParams
+ * @return type
+ */
+function getTopArea(){
+    global $aParams;
+    $sReturn='';
+    if (!array_key_exists("prj", $aParams)){
+        $sReturn='<h1>&Uuml;bersicht</h1><span>Alle Projekte und Versionen in den einzelnen Phasen</span>';
+    } else {
+        require_once("./config/inc_projects_config.php");
+        require_once("./classes/project.class.php");
+        $oPrj=new project($aParams["prj"]);
+        $sReturn=' '. aHome() . ' ' . aPrjHome().'
+            <h1>'.$oPrj->getLabel().'</h1><span>'.$oPrj->getDescription().'</div>';
+            if (array_key_exists("action", $aParams)){
+                $sReturn.='<h2>Aktion: '.$aParams["action"].'</h2>';
+            }
+    }
+    return '<div id="header2">'.$sReturn.'</div>';
+}
+
+/**
+ * get html code of a div around a message
+ * @param string $sWarnlevel one of error|success|info|warning to get a colored box
+ * @param string $sMessage   message txt
+ * @return string
+ */
+function getBox($sWarnlevel, $sMessage){
+    $aCfg=array(
+        "error"=>array("class"=>"alert alert-error", "prefix"=>"ERROR :-("),
+        "success"=>array("class"=>"alert alert-success", "prefix"=>"SUCCESS :-)"),
+        "info"=>array("class"=>"alert alert-info", "prefix"=>"INFO"),
+        "warning"=>array("class"=>"alert alert-block", "prefix"=>"WARNING"),
+    );
+    $sClass="";
+    $sPrefix="";
+    if (array_key_exists($sWarnlevel, $aCfg)){
+        $sClass=$aCfg[$sWarnlevel]["class"];
+        $sPrefix=$aCfg[$sWarnlevel]["prefix"];
+        $sMessage='<strong>'.$aCfg[$sWarnlevel]["prefix"].'</strong> ' . $sMessage;
+    }
+    return '
+        <div class="'.$sClass.'">
+            '.$sMessage.'
+        </div>';
+    
+}
+
+function enterDeployinfos(){
+    global $aParams;
+    $sIdUser="inputUser";
+    $sIdComment="inputComment";
+    $sUser=(array_key_exists($sIdUser, $aParams))?$aParams[$sIdUser]:"";
+    $sComment=(array_key_exists($sIdComment, $aParams))?$aParams[$sIdComment]:"";
+    
+    $sOut='
+        <div class="control-group">
+            <label class="control-label" for="inputUser">Benutzername</label>
+            <div class="controls">
+                <input type="text" id="inputUser" name="inputUser" placeholder="Benutzername" value="'.$sUser.'">
+            </div>
+        </div>
+        <div class="control-group">
+            <label class="control-label" for="inputComment">Kommentar zum Deployment</label>
+            <div class="controls">
+                <textarea id= rows="3" id="inputComment" name="inputComment" placeholder="Kommentar">'.$sComment.'</textarea>
+            </div>
+        </div>
+        ';
+    
+    return $sOut;
+}
+
+
+
+?>
diff --git a/public_html/deployment/index.php b/public_html/deployment/index.php
index 6179e9e3..9c5be12a 100644
--- a/public_html/deployment/index.php
+++ b/public_html/deployment/index.php
@@ -1,47 +1,55 @@
-<?php
-
-require_once("./inc_functions.php");
-require_once("./classes/page.class.php");
-
-
-$sPrj="";
-$sAction="overview";
-
-if (array_key_exists("prj", $aParams)) {
-    $sPrj=$aParams["prj"];
-}
-if (array_key_exists("action", $aParams)) {
-    if (file_exists(__DIR__ . '/act_' . $aParams["action"] . ".php")){
-        $sAction=$aParams["action"];
-    }
-}
-
-// ------ action 
-
-$sActionFile=__DIR__ . '/act_' . $sAction . ".php";
-include($sActionFile);
-
-
-        
-// ------ Ausgabe
-
-$sPhpOut='
-    <div id="header">
-        IML DEPLOYMENT GUI
-    </div>
-    '.$sPhpOut;
-
-$oPage = new Page();
-$oPage->setOutputtype('html');
-$oPage->addResponseHeader("Pragma: no-cache");
-$oPage->addJsOnReady('
-    $(\'.tblOverview\').dataTable({
-            "bPaginate": false,
-            "bLengthChange": false
-    }); 
-');
-
-$oPage->setContent($sPhpOut);
-echo $oPage->render();
-
-?>
+<?php
+
+require_once("./inc_functions.php");
+require_once("./classes/page.class.php");
+
+$sPrj="";
+$sAction="overview";
+
+// ------ check parameters
+
+if (array_key_exists("prj", $aParams)) {
+    $sPrj=$aParams["prj"];
+}
+if (array_key_exists("action", $aParams)) {
+    if (file_exists(__DIR__ . '/act_' . $aParams["action"] . ".php")){
+        $sAction=$aParams["action"];
+    }
+}
+
+// ------ action 
+
+$sActionFile=__DIR__ . '/act_' . $sAction . ".php";
+include($sActionFile);
+
+        
+// ------ Ausgabe
+
+$sPhpOut='
+    <div id="header">
+        IML DEPLOYMENT GUI
+    </div>
+    <br>
+    '. getTopArea() .'
+    <div id="content">
+        '.$sPhpOut.'
+    </div>
+    <div id="footer">
+        IML Deployment &copy '.date("Y").' <a href="http://www.iml.unibe.ch/">Institut f&uuml;r medizinische Lehre; Universit&auml;t Bern</a>
+    </div>
+    ';
+
+$oPage = new Page();
+$oPage->setOutputtype('html');
+$oPage->addResponseHeader("Pragma: no-cache");
+$oPage->addJsOnReady('
+    $(\'.tblOverview\').dataTable({
+            "bPaginate": false,
+            "bLengthChange": false
+    }); 
+');
+
+$oPage->setContent($sPhpOut);
+echo $oPage->render();
+
+?>
diff --git a/shellscripts/sync_packagedir_to_puppet.php b/shellscripts/sync_packagedir_to_puppet.php
new file mode 100644
index 00000000..ab799f9c
--- /dev/null
+++ b/shellscripts/sync_packagedir_to_puppet.php
@@ -0,0 +1,25 @@
+<?php
+    /**
+     * 
+     * FOR CRONJOB
+     * sync archive files to pupet master
+     * 
+     */
+
+    require_once("../public_html/deployment/config/inc_projects_config.php");
+    
+    echo "========= SYNC archive files ==========\n\n";
+    
+    if (array_key_exists('mirrorPackages', $aConfig) && count($aConfig['mirrorPackages']) ){
+        foreach (array_keys($aConfig["phases"]) as $sPhase){
+            echo "===== $sPhase \n";
+            foreach($aConfig['mirrorPackages'] as $sLabel=>$sTarget){
+                
+                $sCmd="/usr/bin/rsync --delete -rv  ".$aConfig["packageDir"]."/$sPhase/* $sTarget/$sPhase ; \n";
+                echo "--- $sLabel\n";
+                echo "\$ $sCmd\n";
+                $sOut=shell_exec($sCmd);
+            }
+        }
+    }
+?>
\ No newline at end of file
-- 
GitLab