diff --git a/00_demo/build.htm b/00_demo/build.htm new file mode 100644 index 0000000000000000000000000000000000000000..1bbf32851c5dff4c3f5c24f2cc2c5e1ce33fb423 --- /dev/null +++ b/00_demo/build.htm @@ -0,0 +1 @@ +[[PHPOUT]] \ No newline at end of file diff --git a/00_demo/build.htm.php b/00_demo/build.htm.php new file mode 100644 index 0000000000000000000000000000000000000000..789dd6a73ec6be9aa340d44f1b85f68286476ca8 --- /dev/null +++ b/00_demo/build.htm.php @@ -0,0 +1,49 @@ +<?php + +require_once("../config/inc_projects_config.php"); +require_once("../classes/project.class.php"); +require_once("../classes/formgen.class.php"); + +require_once("inc_functions.php"); + +$sOut='<h1>Build</h1>'; +$sError=''; + +// --- Checks +if (!array_key_exists("prj", $_GET)){ + $sError.='<li>Es wurde kein Projekt angegeben.</li>'; +} + + +if ($sError){ + $sOut.='<i class="icon-exclamation-sign"></i> FEHLER:<ul>'.$sError.'</ul>'; + +} else { + $oPrj=new project($_GET["prj"]); + $sNext=$oPrj->getNextPhase(); + $sOut=' + <h1>Build '.$oPrj->getLabel().'</h1> + <p>' . $oPrj->getDescription() . '</p> + <hr> + <p> + Es wird ein neues Paket erstellt und auf die Phase <em class="'.$sNext.'">'.$sNext.'</em> installiert.<br> + Du kannst einen Kommentar zu diesem Deployment angeben und darin beschreiben, worin das Update + besteht (Fix, neue Funktion, ...). + </p> + '; + + // Eingabe Kommentare zum Deployment + //$oForm=new formgen(); + $sOut.=' + <hr> + ' . enterDeployinfos() . ' + <hr> + Aktionen + '; +} + +$sOut.=aHome(); +// -- Ausgabe +$sPhpOut=$sOut; +?> + diff --git a/00_demo/deploy.htm b/00_demo/deploy.htm new file mode 100644 index 0000000000000000000000000000000000000000..1bbf32851c5dff4c3f5c24f2cc2c5e1ce33fb423 --- /dev/null +++ b/00_demo/deploy.htm @@ -0,0 +1 @@ +[[PHPOUT]] \ No newline at end of file diff --git a/00_demo/deploy.htm.php b/00_demo/deploy.htm.php new file mode 100644 index 0000000000000000000000000000000000000000..b99642c836006923d6b523ded656393cc7569498 --- /dev/null +++ b/00_demo/deploy.htm.php @@ -0,0 +1,62 @@ +<?php + +require_once("../config/inc_projects_config.php"); +require_once("../classes/project.class.php"); +require_once("../classes/formgen.class.php"); + +require_once("inc_functions.php"); + + +$sOut='<h1>Deploy</h1>'; +$sError=''; + +// --- Checks +if (!array_key_exists("prj", $_GET)){ + $sError.='<li>Es wurde kein Projekt angegeben.</li>'; +} else { + $oPrj=new project($_GET["prj"]); + $sOut='<h1>Deploy to next phase :: '.$oPrj->getLabel().'</h1><p>' . $oPrj->getDescription() . '</p><hr>'; + if (!array_key_exists("phase", $_GET)){ + $sError.='<li>Die aktuelle Phase wurde nicht angegeben.</li>'; + } else { + $sPhase=$_GET["phase"]; + if (!$oPrj->isActivePhase($sPhase)){ + $sError.='<li>Die Phase '.$sPhase.' ist in diesem Projekt nicht aktiv.</li>'; + } else { + if (!$oPrj->canDeploy($sPhase)){ + $sError.='<li>Die Phase '.$sPhase.' kann nicht deployed werden.</li>'; + } + } + } +} + +if ($sError){ + $sOut.='<i class="icon-exclamation-sign"></i> FEHLER:<ul>'.$sError.'</ul>'; + +} else { + $sNext=$oPrj->getNextPhase($sPhase); + $sOut.=' + <p> + Das aktuelle Paket von Phase <em class="'.$sPhase.'">'.$sPhase.'</em> wird auf die Phase <em class="'.$sNext.'">'.$sNext.'</em> installiert.<br> + Du kannst einen Kommentar zu diesem Deployment angeben. + </p> + '; + // Eingabe Kommentare zum Deployment + //$oForm=new formgen(); + // $sOut.=showPhases($sPhase, $sNext); + $aInfos=$oPrj->getPhaseInfos($sPhase); + $sOut.='<pre>'.print_r($aInfos["deployed"], true).'</pre>'; + + $sOut.=' + <hr> + ' . enterDeployinfos() . ' + <hr> + Aktionen [Buld] | + '; +} +$sOut.=aHome(); + +// -- Ausgabe +$sPhpOut=$sOut; +?> + diff --git a/00_demo/doc.htm b/00_demo/doc.htm new file mode 100644 index 0000000000000000000000000000000000000000..1bbf32851c5dff4c3f5c24f2cc2c5e1ce33fb423 --- /dev/null +++ b/00_demo/doc.htm @@ -0,0 +1 @@ +[[PHPOUT]] \ No newline at end of file diff --git a/00_demo/doc.htm.php b/00_demo/doc.htm.php new file mode 100644 index 0000000000000000000000000000000000000000..a2b970c6a96d02a2db48aedcd0cc273f838bc2f4 --- /dev/null +++ b/00_demo/doc.htm.php @@ -0,0 +1,12 @@ +<?php + +require_once("../classes/classinfos.class.php"); +require_once("../classes/project.class.php"); + +$o=new classinfos("project"); +$sOut=$o->render(); + +// -- Ausgabe +$sPhpOut=$sOut; +?> + diff --git a/00_demo/inc_config.php b/00_demo/inc_config.php index 46b916ce7182f4caee9b0f43951f92ea4a9d41b2..21920b0e4b0a426a2f6fd21d33a9a7fc39012692 100644 --- a/00_demo/inc_config.php +++ b/00_demo/inc_config.php @@ -8,8 +8,20 @@ $aCfg=array( ', "pages"=>array( "index.htm"=>array( - "title"=>"Startseite", + "title"=>"Übersicht", "class"=>"icon-home", ), + "build.htm"=>array( + "title"=>"Build", + "class"=>"icon-play-circle", + ), + "deploy.htm"=>array( + "title"=>"Deploy", + "class"=>"icon-forward", + ), + "doc.htm"=>array( + "title"=>"Doc", + "class"=>"icon-forward", + ), ), ); \ No newline at end of file diff --git a/00_demo/inc_functions.php b/00_demo/inc_functions.php new file mode 100644 index 0000000000000000000000000000000000000000..88e775400e60181abfd553f3ab2b47cc052cdaa8 --- /dev/null +++ b/00_demo/inc_functions.php @@ -0,0 +1,67 @@ +<?php + + +/** + * get link as home button + * @return string + */ +function aHome(){ + return '<a href="index.htm" class="btn"><i class="icon-home"></i> zur Übersicht</a>'; +} + + +function enterDeployinfos(){ + $sOut=' + <div class="control-group"> + <label class="control-label" for="inputUser">Benutzername</label> + <div class="controls"> + <input type="text" id="inputUser" placeholder="Benutzername"> + </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" placeholder="Kommentar"></textarea> + </div> + </div> + '; + + return $sOut; +} + +function showPhases($sCurrentPhase=false, $sNext=false){ + include("../config/inc_projects_config.php"); + $sOut=''; + foreach (array_keys($aConfig["phases"]) as $sPhase){ + $sOut.='<div class="'.$sPhase.'" style="float: left; width: 200px; height: 3em;"> + '.$sPhase.'<br>'; + if ($sCurrentPhase==$sPhase) $sOut.='[ aktuell ]'; + if ($sNext==$sPhase) $sOut.='[ next ]'; + $sOut.='</div>'; + + } + $sOut.='<div style="clear: both;"></div>'; + return $sOut; +} + +function execAndSend($sCommand){ + 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()); + echo "<pre>"; + if (is_resource($process)) { + while ($s = fgets($pipes[1])) { + print $s; + flush(); + } + } + echo "</pre>"; +} + +?> diff --git a/00_demo/index.htm b/00_demo/index.htm index 3a15e1ac01520f252dc33817d00ae908b0714122..1422b02e6a81e37ac02cbc631f252c1291d931c8 100644 --- a/00_demo/index.htm +++ b/00_demo/index.htm @@ -2,9 +2,6 @@ <p> Ansicht aller Projekte und Versionen auf den einzelnen Deployment-Phasen </p> -<style> -</style> - [[PHPOUT]] diff --git a/classes/classinfos.class.php b/classes/classinfos.class.php new file mode 100644 index 0000000000000000000000000000000000000000..a699872698791dc433141aa84ef1502fc2d4d011 --- /dev/null +++ b/classes/classinfos.class.php @@ -0,0 +1,316 @@ +<?php +/** + * Documentation of a class + * + * @author axel + */ +class classinfos { + + + var $sClassname=''; + var $oRefClass=false; + var $sGoTop='<a href="#">top</a><br><br>'; + var $sHtmlFooter='<div id="footer">Simple class documentation - © <a href="http://www.axel-hahn.de/">www.axel-hahn.de</a></div>'; + + /** + * init function; it sets defaults. + * @return bool (dummy) + */ + public function __construct($sClassname){ + if($sClassname) $this->setClassname($sClassname); + return true; + } + + public function setClassname($sClassname){ + $this->oRefClass=false; + $this->sClassname=$sClassname; + $this->oRefClass=new ReflectionClass($this->sClassname); + } + + /** + * get comment of an object + * @param object $o + * @return string (html code) + */ + private function getComment($o){ + $sReturn=$o->getDocComment(); + if (!$sReturn) return ''; + + // all @-Tags + $aTags=array( + "abstract","access", "author", "category", "copyright", "deprecated", "example", + "final", "filesource", "global", "ignore", "internal", "license", + "link", "method", "name", "package", "param", "property", "return", + "see", "since", "static", "staticvar", "subpackage", "todo", "tutorial", + "uses", "var", "version" + ); + /* + $aTagsFollowedByKeyword=array( + "access", "global", + "method", "param", "property", "return", + "see", "since", "static", "staticvar", "subpackage", "todo", "tutorial", + "uses", "var" + ); + * + */ + + $sReturn=preg_replace('@[\ \t][\ \t]*@', ' ', $sReturn); // remove multiple spaces + + $sReturn=preg_replace('@^\/\*\*@', '', $sReturn); // remove first comment line + $sReturn=preg_replace('@.*\*\/@', '', $sReturn); // remove last comment line + $sReturn=preg_replace('@ \* @', '', $sReturn); // remove " * " + $sReturn=preg_replace('@\n@', '<br>', $sReturn); + $sReturn=preg_replace('@^\<br\>@', '', $sReturn); + + + /* + foreach(array("param", "return", "var") as $sKey){ + $sReturn=preg_replace('/\@'.$sKey.' ([a-zA-Z\|]*)\ (.*)\<br\>/U', '@'.$sKey.' <span style="font-weight: bold; color:#000;">$1</span> <span style="font-weight: bold;">$2</span><br>', $sReturn); + } + */ + $sReturn=preg_replace('/(\@.*)$/s', '<span class="phpdoc">$1</span>', $sReturn); + foreach($aTags as $sKey){ + $sReturn=preg_replace('/\@('.$sKey.')/U', '<span class="doctag">@'.$sKey.'</span>', $sReturn); + } + return $sReturn='<div class="comment">'. $sReturn . '</div>'; + + } + + public function renderProperties($sMode){ + if(!$this->oRefClass) return false; + $sHtml=''; + + $sHtml.='<div class="properties">'; + if ($sMode=="html"){ + $sHtml.='<h2 id="properties">Properties</h2>'; + } else { + $sHtml.=' + <a href="#" class="propertyswitch" onclick="$(\'.propertyswitch\').toggle(); $(\'.property\').hide(); return false;">Properties verstecken</a> + <a href="#" class="propertyswitch" onclick="$(\'.propertyswitch\').toggle(); $(\'.property\').show(); return false;" style="display: none">Properties anzeigen</a> + | + <a href="#" class="publicpropertyswitch" onclick="$(\'.publicpropertyswitch\').toggle(); $(\'.property.private\').hide(); return false;" style="display: none">private verstecken</a> + <a href="#" class="publicpropertyswitch" onclick="$(\'.publicpropertyswitch\').toggle(); $(\'.property.private\').show(); return false;">private anzeigen</a> + <br><br>'; + } + $oDefaulProps=$this->oRefClass->getDefaultProperties(); + + // foreach($this->oRefClass->getProperties(ReflectionProperty::IS_PROTECTED) as $o){ + foreach($this->oRefClass->getProperties() as $o){ + $sType=''; + if($o->isPublic()) $sType.='public'; + if($o->isPrivate()) $sType.='private'; + if($o->isProtected()) $sType.='protected'; + if($o->isStatic()) $sType.='static'; + + $sHtml.='<div class="property '.$sType.'">'; + $sHtml.='<span class="propertytype">'.$sType.'</span><span class="propertyname"> $'.$o->getName().'</span>'; + $sHtml.='<span class="propertyvalue"> = '; + if ($oDefaulProps[$o->getName()]) $sHtml.=$oDefaulProps[$o->getName()]; + else $sHtml.='false'; + $sHtml.='</span>'; + + $sHtml.=$this->getComment($o); + $sHtml.='</div>'; + } + $sHtml.='</div>'; + return $sHtml; + } + public function renderMethods($sMode){ + if(!$this->oRefClass) return false; + $sHtml=''; + $sHtml.='<div class="methods">'; + if ($sMode=="html"){ + $sHtml.='<h2 id="methods">Methods</h2>'; + } else { + $sHtml.=' + <a href="#" class="methodswitch" onclick="$(\'.methodswitch\').toggle(); $(\'.method\').hide(); return false;">Methoden verstecken</a> + <a href="#" class="methodswitch" onclick="$(\'.methodswitch\').toggle(); $(\'.method\').show(); return false;" style="display: none">Methoden anzeigen</a> + | + <a href="#" class="publicmethodswitch" onclick="$(\'.publicmethodswitch\').toggle(); $(\'.method.private\').hide(); return false;" style="display: none">private verstecken</a> + <a href="#" class="publicmethodswitch" onclick="$(\'.publicmethodswitch\').toggle(); $(\'.method.private\').show(); return false;">private anzeigen</a> + <br><br>'; + } + foreach($this->oRefClass->getMethods() as $o){ + $sMethodname=$o->name; + $sType=''; + if($o->isPublic()) $sType.='public'; + if($o->isPrivate()) $sType.='private'; + if($o->isProtected()) $sType.='protected'; + if($o->isStatic()) $sType.='static'; + if($o->isAbstract()) $sType.='abstract'; + if($o->isFinal()) $sType.='final'; + + $sHtml.='<div class="method '.$sType.'">'; + $sHtml.=$sType . ' <span class="methodname">' .$sMethodname . "</span>("; + $oMethod = $this->oRefClass->getMethod($sMethodname); + + + $sHtml.='<span class="parameters">' ; + $iCount=0; + $iRequired=$oMethod->getNumberOfRequiredParameters(); + foreach($oMethod->getParameters() as $oParam){ + $iCount++; + $sHtml.='<span class="'; + $sHtml.=$iCount<=$iRequired ? "required":"optional"; + $sHtml.='">'. preg_replace('@Parameter\ \#.*\[\ (.*)\ \]@', '$1', $oParam->__toString()).'</span>,'; + } + $sHtml=preg_replace('@,$@', "", $sHtml); + $sHtml.="</span>)"; + if ($iRequired){ + if ($iCount==$iRequired) $sHtml.=' ... all param(s) required'; + else $sHtml.=' ... '.$iRequired . ' of '.$iCount.' param(s) required'; + + } + + $sHtml.=$this->getComment($oMethod); + $sHtml.='</div>'; + } + $sHtml.='</div>'; + return $sHtml; + } + + function renderSource($sMode){ + + $source_code=file_get_contents($this->oRefClass->getFileName()); + + $sHtml.='<div class="sourcecode">'; + if ($sMode=="html"){ + $sHtml.='<h2 id="source">Source</h2>'; + } + else{ + $sHtml.=' + <a href="#" class="sourceswitch" onclick="$(\'.sourceswitch\').toggle(); $(\'.source\').hide(); return false;">Sourcecode verstecken</a> + <a href="#" class="sourceswitch" onclick="$(\'.sourceswitch\').toggle(); $(\'.source\').show(); return false;" style="display: none">Sourcecode anzeigen</a><br> + <br>'; + } + + $sHtml.='<pre class="source">' . highlight_string($source_code, true) .'</pre></div>'; + + return $sHtml; + + /* + $source_code = explode("\n", str_replace(array("\r\n", "\r"), "\n", $source_code)); + $line_count = 1; + + foreach ($source_code as $code_line) + { + $formatted_code .= '<tr><td>'.$line_count.'</td>'; + $line_count++; + + if (ereg('<\?(php)?[^[:graph:]]', $code_line)) + $formatted_code .= '<td>'. str_replace(array('<code>', '</code>'), '', highlight_string($code_line, true)).'</td></tr>'; + else + $formatted_code .= '<td>'.ereg_replace('(<\?php )+', '', str_replace(array('<code>', '</code>'), '', highlight_string('<?php '.$code_line, true))).'</td></tr>'; + } + + return '<table style="font: 1em Consolas, \'andale mono\', \'monotype.com\', \'lucida console\', monospace;">'.$formatted_code.'</table>'; + */ + } + + public function render($sMode=false){ + $sHtml=''; + + if(!$this->oRefClass) return false; + if ($sMode=="html") $sHtml.=' + <html> + <head> + <title>PHP class '.$this->sClassname.'</title> + </head> + <style> + body{font-family: Arial,Helvetica,Verdana,sans-serif; background: #fafafa; color: #444;} + a{color:#348; text-decoration: none;} + a:hover{text-decoration: underline;} + h1{color:#88c; border-bottom: 3px solid #ddf;} + h2{color:#44a; border-bottom: 3px solid #ddf;} + #footer{border-top: 3px solid #eee; background:#f4f4f4; margin-top:20px; padding: 5px; text-align: center;} + </style> + <body> + <h1>Simple class documentation - '.$this->sClassname.'</h1> + + local links: + <a href="#methods">Methods</a> :: + <a href="#properties">Properties</a> :: + <a href="#source">Source</a><br> + <br> + + '; + + + $sHtml.=' + <style> + .protected, .private, .public, .static, .abstract, .final{padding-left: 34px; } + .classinfo .protected {background: url(/axel/images/nuvola/16x16/actions/kgpg.png) no-repeat 10px 10px; } + .classinfo .private {background: rgba(255,220,220,0.3) url(/axel/images/nuvola/16x16/actions/kgpg_key1.png) no-repeat 10px 10px; display:none; } + .classinfo .public {background: rgba(220,255,220,0.3) url(/axel/images/nuvola/16x16/actions/ledlightgreen.png) no-repeat 10px 10px; } + .classinfo .static {background: url(/axel/images/nuvola/16x16/actions/ledyellow.png) no-repeat 10px 10px;} + .classinfo .abstract {background: url(/axel/images/nuvola/16x16/actions/ledpurple.png) no-repeat 10px 10px;} + .classinfo .final {background: url(/axel/images/nuvola/16x16/actions/ledred.png) no-repeat 10px 10px;} + + .classinfo{border: 0px solid #aac; padding: 5px;} + .classinfo .classname{font-weight: bold; font-size: 130%;} + .classinfo .methods, .classinfo .properties, .classinfo .sourcecode{margin-left: 20px; border: 0px solid #ccc; margin-top: 20px; } + .classinfo .property, .classinfo .method, .classinfo .source{margin-left: 30px; margin-top: 10px; border-top: 0px dotted #ccc; padding-top: 10px;} + .classinfo .methodname{font-weight: bold;} + .classinfo .propertyname{font-weight: bold;} + .classinfo .propertyvalue{font-style: italic; color:#aaa;} + .classinfo .parameters{font-style: italic; color:#aaa;} + .classinfo .parameters .required {background:rgba(255,210,210,1);color:#666;} + .classinfo .comment{margin: 10px 0 0 0px; padding: 10px; background-color:rgba(255,255,255,0.4);} + .classinfo .comment .phpdoc{font-style: italic; color:#8aa; font-weight: normal;} + .classinfo .comment .phpdoc .doctag{color:#a8a; font-weight: normal;} + + .classinfo .sourcecode .source{border: 1px solid #eee; padding: 10px;} + + </style> + '; + $sHtml.='<div class="classinfo">'; + $sHtml.='<div class="classname">Class: '.$this->sClassname.'</div>'; + + + /* + foreach (array("public","private","protected", "static", "abstract", "final") as $sType){ + print '<input type="checkbox" onclick="$(\'.'.$sType.'\').toggle();"'; + if ($sType=="public") print " checked "; + print '> '. $sType . ' ... '; + } + */ + if (!$sMode) $sHtml.=' + <style> + .classinfo .comment{display:none;} + </style> + Ansicht: + <ul class="naviH" > + <li class="active"><a href="#" class="viewswitch" onclick="$(\'.naviH >li\').removeClass(\'active\'); $(this.parentNode).addClass(\'active\'); $(\'.comment\').hide(); return false;">einfach</a> + <li><a href="#" class="viewswitch" onclick="$(\'.naviH >li\').removeClass(\'active\'); $(this.parentNode).addClass(\'active\'); $(\'.comment\').show(); return false;">erweitert</a> + </ul> + </span> + <br>'; + + $sHtml.=$this->renderMethods($sMode); + if ($sMode=="html") $sHtml.=$this->sGoTop; + $sHtml.=$this->renderProperties($sMode); + if ($sMode=="html") $sHtml.=$this->sGoTop; + + if ($sMode=="html") $sHtml.=$this->renderSource($sMode); + if ($sMode=="html") $sHtml.=$this->sGoTop; + + if ($sMode=="html") $sHtml.=$this->sHtmlFooter; + + $sHtml.='</div>'; + // print "DocComment:<br>".$this->$oRefClass->getDocComment(); + // $this->renderMethods(); + + if ($sMode=="html") $sHtml.=' + </body> + </html>'; + + // echo $sHtml; + return $sHtml; + } + + +} + +?> + + \ No newline at end of file diff --git a/classes/lang.class.php b/classes/lang.class.php new file mode 100644 index 0000000000000000000000000000000000000000..01a95ee9ad9c038a1c8f9ce8abcdddae34aa5286 --- /dev/null +++ b/classes/lang.class.php @@ -0,0 +1,27 @@ +<?php +/** + * Description of lang + * + * @author hahn + */ +class lang { + + private $_aLangData=array(); + + public function __construct($s){ + if ($s) return $this->l($s); + } + + public function _readConfig(){ + $this->_aLangData=array(); + } + + public function l($s){ + if (array_key_exists($s, $this->_aLangData)){ + return $this->_aLangData[$s]; + } + return $s; + } +} + +?> diff --git a/classes/project.class.php b/classes/project.class.php index 5004729cadefb81688f675a0b5063bcde766d440..d34d3ea45364af4220772df99a9967cb043a1ae8 100644 --- a/classes/project.class.php +++ b/classes/project.class.php @@ -12,6 +12,7 @@ class project { protected $_aData = array(); protected $_sPackageDir = false; protected $_aPhases=array(); + protected $_sCfgfile="../config/inc_projects_config.php"; // ---------------------------------------------------------------------- // constructor @@ -22,10 +23,10 @@ class project { * use setConfig method if you initialize without config * @param array $aConfig config of the project */ - public function __construct($aConfig = false) { + public function __construct($sId = false) { $this->_readConfig(); - if ($aConfig) - $this->setConfig($aConfig); + if ($sId) + $this->setProjectById($sId); } // ---------------------------------------------------------------------- @@ -37,9 +38,9 @@ class project { * @return boolean */ private function _readConfig() { - require("../config/inc_projects_config.php"); - $this->_sPackageDir = $sPackageDir; - $this->_aPhases=$aPhases; + require($this->_sCfgfile); + $this->_sPackageDir = $aConfig['packageDir']; + $this->_aPhases=$aConfig["phases"]; return true; } @@ -53,7 +54,7 @@ class project { if (!$this->_sPackageDir) die("ERROR::CONFIG: packagedir is not set."); if (!file_exists($this->_sPackageDir)) - die("ERROR::CONFIG: packagedir does not exist: \$sPackageDir="" . $this->_sPackageDir . ""."); + die("ERROR::CONFIG: packagedir does not exist: "" . $this->_sPackageDir . ""."); if (!array_key_exists("phases", $this->_aConfig)) die("ERROR::CONFIG: key "phases" was not found in config.<br><pre>" . print_r($this->_aConfig, true) . "</pre>"); // TODO: verify ausbauen @@ -95,7 +96,7 @@ class project { public function getAllPhaseInfos(){ if (!array_key_exists("phases", $this->_aData)) $this->_aData["phases"]=array(); - foreach($this->_aPhases as $sPhase){ + foreach(array_keys($this->_aPhases) as $sPhase){ if (!array_key_exists($sPhase, $this->_aData["phases"])){ $this->getPhaseInfos($sPhase); } @@ -117,8 +118,26 @@ class project { $this->_aData["phases"][$sPhase]=array(); $aTmp=array(); + // a blocked package is waiting for deployment timeslot? + $sKey="onhold"; + $sBaseFilename=$this->_sPackageDir."/".$sPhase."/".$this->_aConfig["fileprefix"]; + $sJsonfile=$sBaseFilename."_onhold.json"; + $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; + } else { + $aTmp[$sKey]["error"]="info file $sJsonfile exists but is corrupt (no timestamp)." . print_r($aJson, true); + } + } else { + $aTmp[$sKey]["info"]="No package is wainting in the queue."; + $aTmp[$sKey]["ok"]=1; + } + // package for puppet - $sKey="queued"; + $sKey="ready4deployment"; $sBaseFilename=$this->_sPackageDir."/".$sPhase."/".$this->_aConfig["fileprefix"]; $sJsonfile=$sBaseFilename.".json"; $aTmp[$sKey]=array(); @@ -126,13 +145,13 @@ class project { $sPkgfile=$sBaseFilename.".tgz"; if (file_exists($sPkgfile)){ $aJson=json_decode(file_get_contents($sJsonfile), true); - if (array_key_exists("date", $aJson)){ + 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."; + $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"; @@ -149,12 +168,12 @@ class project { $sJsonData=@file_get_contents($sJsonUrl); if ($sJsonData){ $aJson=json_decode($sJsonData, true); - if (array_key_exists("date", $aJson)){ + 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"; + $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>"; @@ -182,6 +201,74 @@ class project { ); } + /** + * 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->_aPhases)){ + die("ERROR: this phase does not exist: $sPhase."); + } + } + + $sNextPhase=false; + $bUseNextPhase=$sPhase?false:true; + foreach(array_keys($this->_aPhases) 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 canDeploy($sPhase=false){ + + if (!$sPhase){ + $sNext=$this->getNextPhase($sPhase); + return $sNext>''; + } + + + if (!array_key_exists($sPhase, $this->_aPhases)){ + 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 @@ -191,12 +278,29 @@ class project { * 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->_aConfig = array(); + require($this->_sCfgfile); + if (!array_key_exists("$sId", $aProjects)){ + die("ERROR: a project with ID $sId does not exist."); + } + $this->_aConfig=$aProjects[$sId]; + $_aConfig["id"]=$sId; + $this->_verifyConfig(); + return true; + } // ---------------------------------------------------------------------- // ACTIONS diff --git a/classes/projectlist.class.php b/classes/projectlist.class.php index f5e4a0b911c1e5d1e112e0525ab37616ffcd4587..9fbe2210b7b7ae0f985eacb4cbea94b3e8a0e335 100644 --- a/classes/projectlist.class.php +++ b/classes/projectlist.class.php @@ -12,6 +12,7 @@ class projectlist { // ---------------------------------------------------------------------- protected $_aProjects = array(); protected $_aPhases = array("preview", "stage", "live"); + protected $_sCfgfile="../config/inc_projects_config.php"; // ---------------------------------------------------------------------- // constructor @@ -38,9 +39,9 @@ class projectlist { */ private function _readConfig() { $this->_aProjects = array(); - require("../config/inc_projects_config.php"); + require($this->_sCfgfile); $this->_aProjects = $aProjects; - $this->_aPhases = $aPhases; + $this->_aPhases = $aConfig["phases"]; $this->_verifyConfig(); return true; } @@ -95,32 +96,53 @@ class projectlist { public function renderOverview() { $sOut = ''; $oPrj = false; + + $sDebug = ''; // loop over projects foreach ($this->_aProjects as $sPrj => $aData) { - $oPrj = new project($aData); + $oPrj = new project($sPrj); $aPrjData = $oPrj->getAllPhaseInfos(); - echo "<pre>" . print_r($aPrjData, true) . "</pre>"; + $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 ($this->_aPhases as $sPhase) { + foreach (array_keys($this->_aPhases) as $sPhase) { $aTmp = $aPrjData[$sPhase]; - $q = "queued"; + $h = "onhold"; + $q = "ready4deployment"; $d = "deployed"; if (!$oPrj->isActivePhase($sPhase)) { $sOutPhases.=' - <td class="' . $sPhase . '" colspan="2"> - <div class="versioninfo">inactive</div> + <td class="' . $sPhase . '" colspan="3"> + <div class="versioninfo center inactive"><i class="icon-ban-circle"></i> inactive</div> </td>'; } else { - // (1) queue and deployment have the same version? + // --- on hold + $sInfoH = ''; + if (!array_key_exists("ok", $aTmp[$h])) { + if (array_key_exists("deploytimes", $this->_aPhases[$sPhase])){ + $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> + <br> + Deployment: '. implode(",", array_values($this->_aPhases[$sPhase]["deploytimes"])).' + </div>'; + } else { + $sInfoH = '<div class="error"><i class="icon-exclamation-sign"></i> FEHLER:<br>Es ist keine Queue definiert, aber es ist eine Info-Datei für eine Queue vorhanden: '.$aTmp[$h]["infofile"].'<br>Confused :-/</div>'; + } + } else { + $sInfoH = ''; + } + + // --- queue and deployment have the same version? $sInfoQ = ''; - if (array_key_exists("ok", $aTmp[$q]) && array_key_exists("ok", $aTmp[$d]) && $aTmp[$q]["date"] > " " && $aTmp[$q]["date"] == $aTmp[$d]["date"]) { + 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])) { @@ -131,12 +153,12 @@ class projectlist { </div>'; } else { if (array_key_exists("error", $aTmp[$q])) { - $sInfoQ = '<div class="error">FEHLER:<br>' . $aTmp[$q]["error"] . '</div>'; + $sInfoQ = '<div class="error"><i class="icon-exclamation-sign"></i> FEHLER:<br>' . $aTmp[$q]["error"] . '</div>'; } } } - // (2) deployment infos + // -- deployment infos $sInfoD = ''; if (array_key_exists("ok", $aTmp[$d])){ $sInfoD = ' @@ -146,15 +168,24 @@ class projectlist { '; } if (array_key_exists("warning", $aTmp[$d])) { - $sInfoD = '<div class="warning">WARNUNG:<br>' . $aTmp[$d]["warning"] . '</div>'; + $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">FEHLER:<br>' . $aTmp[$d]["error"] . '</div>'; + $sInfoD = '<div class="error"><i class="icon-exclamation-sign"></i> FEHLER:<br>' . $aTmp[$d]["error"] . '</div>'; } + if ($oPrj->canDeploy($sPhase)){ + $sNext=$oPrj->getNextPhase($sPhase); + $sInfoD.='<br><a href="deploy.htm?prj='.$sPrj.'&phase='.$sPhase.'" class="btn '.$sNext.'"><i class=" icon-forward"></i> Deploy auf ['.$sNext.']</a>'; + } // output $sOutPhases.=' + <td class="' . $sPhase . '"> + <div class="versioninfo"> + ' . $sInfoH . ' + </div> + </td> <td class="' . $sPhase . '"> <div class="versioninfo"> ' . $sInfoQ . ' @@ -175,9 +206,12 @@ class projectlist { <tr> <td class="prj"> <strong>' . $oPrj->getLabel() . '</strong><br> - ' . $oPrj->getDescription() . '<br> - <a href="#" class="btn"><i class=" icon-forward"></i> build PREVIEW</a> - </td> + ' . $oPrj->getDescription() . '<br>'; + if ($oPrj->canDeploy()){ + $sNext=$oPrj->getNextPhase(); + $sOut.='<a href="build.htm?prj='.$sPrj.'" class="btn '.$sNext.'"><i class=" icon-forward"></i> Build für ['.$sNext.']</a>'; + } + $sOut.='</td> ' . $sOutPhases . ' </tr>'; } @@ -187,23 +221,27 @@ class projectlist { <thead> <tr> <th class="prj">Projekt</th> - <th class="preview" colspan="2" >Preview</th> - <th class="stage" colspan="2" >Stage</th> - <th class="live" colspan="2" >Live</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">deployed</td> - <td class="stage">queue</td> - <td class="stage">deployed</td> - <td class="live">queue</td> - <td class="live">deployed</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; } diff --git a/config/inc_projects_config.php b/config/inc_projects_config.php index 6285e2789511caecac077e4b0fdeb9ef75e4b848..1e40ae22028b035371ed183824b9d1e30829022f 100644 --- a/config/inc_projects_config.php +++ b/config/inc_projects_config.php @@ -1,66 +1,69 @@ <?php -// ---------------------------------------------------------------------- -// Pfade -// ---------------------------------------------------------------------- -$sWorkDir="/var/imldeployment"; -$sWorkDir="D:/imldeployment"; -$sBuiltDir=$sWorkDir."/built"; // darunter ist je Projekt 1 Verzeichnis -$sPackageDir=$sWorkDir."/packages"; // darunter ist je Projekt 1 Verzeichnis - darunter Phasen? - // ---------------------------------------------------------------------- // fetch status infos von den einzelnen Phasen // ---------------------------------------------------------------------- -$aPhases = array("preview", "stage", "live"); -$sUrlStatus="http://[projecturl]/[project].json"; +$aConfig=array( + + // Basispfad: + 'workDir'=>"/var/imldeployment", + + + 'phases'=>array( + "preview"=>array( + ), + "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( - "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(), - "hasPreview"=>1, - "hasStage"=>1, - "hasLive"=>0, - - ), "scrudu"=>array( "label"=>"SCRUDU", "fileprefix"=>"scrudu", "description" => 'Das sagenumwogene Scrudu Dings', "contact" => 'Entwickler, Verantwortliche etc.', "build"=>array( - "type"=>"script", // one of git, svn, ... - "script"=>"[path]/built", - ), + "type"=>"git", // one of git, svn, ... + "url"=>"ssh gituser@gitserver:/path/to/project", + ), "phases"=>array( "preview"=>array( // "url"=>"http://preview.scrudu.iml.unibe.ch/", - "url"=>"http://localhost/deployment/", + "url"=>"http://ci.iml.unibe.ch/deployment/", ), "stage"=>array( "url"=>"http://stage.scrudu.iml.unibe.ch/" ), - "live"=>array(), + "live"=>array( + // "url"=>"http://www.scrudu.iml.unibe.ch/" + ), ), - "hasPreview"=>1, - "hasStage"=>1, - "hasLive"=>0, - ) + ), + "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(), + ), ); // ---------------------------------------------------------------------- @@ -88,3 +91,22 @@ $aTypeAbstraction=array( ); +// ---------------------------------------------------------------------- +// override for local development +// ---------------------------------------------------------------------- + +switch ($_SERVER["SERVER_NAME"]) { + case "localhost": + $aConfig['workDir']="D:/imldeployment"; + $aProjects["scrudu"]["phases"]["preview"]["url"]="http://localhost/deployment/"; + break; + + default: + break; +} + +$aConfig=array_merge($aConfig, array( + 'builtDir'=>$aConfig['workDir'].'/built', + 'packageDir'=>$aConfig['workDir'].'/packages', +)); + diff --git a/scrudu.json b/scrudu.json index 1942a3dd401f097a45a874ed449dac41a02584c0..0cd7a325b179f0bff39642f646bcd9e41d14837c 100644 --- a/scrudu.json +++ b/scrudu.json @@ -1,5 +1,6 @@ { "date": "2013-10-09 09:34:12", + "timestamp": "20131009_093412", "revision": "optional: ausgecheckte Version aus dem Repo/ den Repos", "remark": "Hinweise in diesem Release: wer hat was aktualisiert/ gefixt o.ä." } \ No newline at end of file diff --git a/shared/default.tpl.html b/shared/default.tpl.html index 212c765a48b33e6d22f68f73b7ef3f71572a0082..8689f0bcc5d65f38f31a367f55f6e9203a488737 100644 --- a/shared/default.tpl.html +++ b/shared/default.tpl.html @@ -43,6 +43,20 @@ margin: 0px 0px 20px 0px; } } + + body{} + body, label, input, button, select, textarea, p, .btn { + font-size: 12px; + } + + + 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%;} @@ -50,16 +64,19 @@ #tbloverview td{border-left: 1px solid #ccc;} th.preview{background:#358; color:#eee;} th.stage{background:#388; color:#eee;} - th.live{background:#383; color: #eee; } - .preview{background:#f4f8ff; color:#333; background: rgba(230,240,255, 0.7);} - .stage{background:#f4ffff; color:#333; background: rgba(230,255,255, 0.7);} - .live{background:#f0fff0; color:#333; background: rgba(240,255,240, 0.7);} + 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);} - .tdstage{background:#f4f8ff; color:#333; background: rgba(230,240,255, 0.7);} - .tdlive{background:#f0fff0; color:#333; background: rgba(240,255,240, 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; @@ -68,58 +85,6 @@ margin-bottom: 10px; } - body, label, input, button, select, textarea, p, .btn { - font-size: 12px; - } - - - h2{font-size: 16px; margin: 0;} - h3{font-size: 14px; margin: 0;} - - /* ----- FORM ----- */ - input[type="radio"]+label, input[type="checkbox"]+label{ - color:#000; background: #eee; - } - input[type="radio"]+label:hover, input[type="checkbox"]+label:hover{ - background: #f4f4f4; - } - - .highlight, - .pagination .active a, .pagination .active span, - ul li.customcriteria, - input[type="radio"]:checked+label, input[type="checkbox"]:checked+label{ - color:#000;background: #f8f8d0 !important; - } - input[type="radio"]:checked+label:hover, input[type="checkbox"]:checked+label:hover{ - background: #faf8e4 !important; - } - - input[type="radio"], input[type="checkbox"]{float: left; margin-top:5px;} - input[type="radio"]+label, input[type="checkbox"]+label{ - display: block; margin-left: 2em; - } - - input, textarea { - width: 515px; /* passt so in modal Box */ - height: none !important; - font-size: 12px !important; - padding: 1px 2px !important; - } - td input, td textarea, select { - width: 350px; /* für Tabellen-Darstellung */ - } - #divBgModal{ - background: #000; opacity: 0.4; - position: fixed; top: 0; left: 0; - width: 100%; height: 100%; - display: none; - } - - ul {} - ul li { - margin-bottom: 3px; - } - .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%;}