Skip to content
Snippets Groups Projects
Select Git revision
  • 0344f13448a739d80a98572f91b0819e5e43e146
  • master default protected
  • simple-task/7248-eol-check-add-node-22
  • 6877_check_iml_deployment
4 results

check_couchdb

Blame
  • project_gui.class.php 78.71 KiB
    <?php
    
    require_once 'project.class.php';
    require_once 'htmlguielements.class.php';
    
    
    /* ######################################################################
    
      IML DEPLOYMENT
    
      class project for all actions for single project
      Rendering of web ui
    
      ---------------------------------------------------------------------
      2013-11-08  Axel <axel.hahn@iml.unibe.ch>
      ###################################################################### */
    
    /**
     * class for single project
     */
    // class project {
    class projectgui extends project {
    
        /**
         * constructor
         * @param string $sId  id of the project
        public function __construct($sId = false) {
            parent::__construct($sId);
            $this->_oHtml = new htmlguielements();
        }
        */
    
        // ----------------------------------------------------------------------
        // private functions
        // ----------------------------------------------------------------------
    
        /**
         * return html code for a div with background color based on a checksum of the given text
         * @param string $sText      text that is used for checksum; if false ist returns a gray
         * @param string $sContent   optional: text to show
         * @return string
         */
        private function _getChecksumDiv($sText, $sContent='', $sBarHeight='3px') {
            if ($sText){
                
                // color ranges in decimal values for RGB from ... to
                $iFgStart=60;  $iFgEnd=160;
                $iBgStart=200; $iBgEnd=250;
    
                $iFgStart=60;  $iFgEnd=160;
                $iBgStart=190; $iBgEnd=250;
    
                // deivider: 3 digits of md5 will be extracted
                $iFgDivider=16*16*16/($iFgEnd-$iFgStart);
                $iBgDivider=16*16*16/($iBgEnd-$iBgStart);
                
                $sHash=md5($sText);
                $sColor=''
                    . 'color: rgba(' 
                    . ($iFgStart + round(hexdec(substr($sHash,0,3))/$iFgDivider)) . ','
                    . ($iFgStart + round(hexdec(substr($sHash,3,3))/$iFgDivider)) . ','
                    . ($iFgStart + round(hexdec(substr($sHash,6,3))/$iFgDivider)) . ','
                    . '1'
                    . ');'
                    . 'background: rgba(' 
                    . ($iBgStart + round(hexdec(substr($sHash,0,3))/$iBgDivider)) . ','
                    . ($iBgStart + round(hexdec(substr($sHash,3,3))/$iBgDivider)) . ','
                    . ($iBgStart + round(hexdec(substr($sHash,6,3))/$iBgDivider)) . ','
                    . '1'
                    . ');'
                    ;
            } else {
                $sColor = "color: #888; background: #ccc;";
            }
            return '<div style="' . $sColor . ' border-top: '.$sBarHeight.' solid;">'.($sContent ? $sContent : ' ').'</div>';
        }
    
    
        /**
         * get html code for the colored bar on top of each phase detail items
         * @param string $sPhase  phase of a project
         * @param string $sPlace  place in the given phase
         * @return string
         */
        private function _renderBar($sPhase, $sPlace, $sBarHeight='3px') {
            $aDataPhase = $this->getPhaseInfos($sPhase);
            $aData = $aDataPhase[$sPlace];
            if (!array_key_exists("revision", $aData)) {
                return false;
            }
            return $this->_getChecksumDiv($aData["revision"], '', $sBarHeight);
        }
    
        private function _renderHostsData($aData) {
            $sReturn = '';
            if (array_key_exists('_hosts', $aData)) {
                
                // $sReturn.= print_r($aData['_hosts'], 1);
                $sReturn.= '<div class="hosts">'
                        . '<br><strong>' . t('hosts') . ':</strong><br>'
                ;
                foreach ($aData['_hosts'] as $sHostname => $aHostinfos) {
                    $oUpdateDate = date("U", strtotime($aHostinfos['time']));
                    $iAgeUpdate = round((date("U") - $oUpdateDate) / 60);
                    $sAge = $iAgeUpdate < 60 * 60 * 13 ? $iAgeUpdate . " min" : "??";
    
                    $sReturn.= '<div class="host">'
                            . $this->_getChecksumDiv(
                                $aHostinfos['_data']['revision'],
                                $this->_oHtml->getIcon('host').'<br>' . $sHostname
                            )
                            . "($sAge)"
                            . '</div>'
                    ;
                }
                $sReturn.= '</div><div style="clear: both;"></div>';
            }
            return $sReturn;
        }
    
        /**
         * get html code for list of hosts in a phase
         * @param string $sPhase  phase of a project
         * @return string
         */
        private function _renderHosts($sPhase) {
            $aDataPhase = $this->getPhaseInfos($sPhase);
            if (is_array($aDataPhase) && array_key_exists('deployed', $aDataPhase)) {
                return $this->_renderHostsData($aDataPhase['deployed']);
            }
            return '';
        }
    
        /**
         * get html code for list of files in a phase
         * @param string $sPhase  phase of a project
         * @return string
         */
        private function _renderFiles($sPhase) {
            $sReturn = '';
            $aFiles = $this->getBuildfilesByPlace($sPhase, 'ready2install');
            if (!$aFiles || !$aFiles['filecount']) {
                return '';
            }
            $sReturn.='<strong>' . t("filelist") . '</strong> (' . $aFiles['filecount'] . '):<br>';
            foreach ($aFiles['files'] as $sFilename => $aData) {
                $sReturn.='<div class="file file-' . $aData['type'] . ' fileext-' . $aData['extension'] . '" title="' . $sFilename . ' (' . $aData['type'] . ')">'
                        . $aData['icon'] . $sFilename
                        // . ' ('.$aData['type'].')'
                        . '</div>'
                ;
            }
            $sReturn.='(' . $aFiles['totalsize-hr'] . ')';
            return $sReturn;
        }
    
        // ----------------------------------------------------------------------
        // RENDERING
        // ----------------------------------------------------------------------
    
    
        /**
         * render html for a row with td for all places of a phase
         * @param string $sPhase   phase
         * @param bool   $bActions draw action links (deploy, accept) on/ off
         * @param bool   $bLong    use long variant to display infos? 
         * @return string|boolean
         */
        public function renderAllPhaseDetails($sPhase, $bActions = true, $bLong = true) {
            if (!$sPhase) {
                return false;
            }
            if (!$this->isActivePhase($sPhase)) {
                return '
                            <td class="td-phase-' . $sPhase . ' td-phase-inactive ' . $this->_aConfig["id"] . '" colspan="' . count($this->_aPlaces) . '">
                                <div class="versioninfo center inactive">' . $this->_oHtml->getIcon('sign-info').t('inactive') . '</div>
                            </td>';
            }
            $sRow2 = false;
    
            $aRows = array();
            $sLastPlace = '';
            
            foreach (array_keys($this->_aPlaces) as $sPlace) {
                $aRows[$sPlace] = $this->renderPhaseDetail($sPhase, $sPlace, $bActions, $bLong);
                
                // generate ">>" sign for lastly generated td
                if ($sLastPlace && array_key_exists("version", $this->_aData["phases"][$sPhase][$sLastPlace]) 
                        && array_key_exists("version", $this->_aData["phases"][$sPhase][$sPlace]) 
                        && $this->_aData["phases"][$sPhase][$sLastPlace]["version"] == $this->_aData["phases"][$sPhase][$sPlace]["version"] 
                        && !$bLong
                ) {
                    $aRows[$sLastPlace] = $this->_renderBar($sPhase, $sPlace) . "&raquo;";
                }
                $sLastPlace = $sPlace;
            }
            
            foreach (array_keys($this->_aPlaces) as $sPlace) {
                $sRow2.='<td class=" td-phase-'.$sPhase.' td-place-'.$sPlace.' td' . $this->_aConfig["id"] . '">' . $aRows[$sPlace] . '</td>';
            }
            return $sRow2;
        }
    
        /**
         * return html code for current project errors by rendering a box per error in $this->_errors
         * @return string
         */
        public function renderErrorBoxes(){
            $sReturn='';
            if(count($this->_errors)){
                foreach($this->_errors as $sError){
                    $sReturn.=$this->_oHtml->getBox("error", $sError);
                }
            }
            return $sReturn;
        }
    
    
        /**
         * fix output of commit message as html
         * This is a compatibility function for older builds
         * 
         * @param  string  $sMessage  git commit message
         * @return string
         */
        public function transformCommitMessage($sMessage){
            if(strstr($sMessage, '<br>Date:')){
                $_aReplace=[
                    '<br>Author:' => "\nAuthor:",
                    '<br>Date:' => "\nDate:",
                    '<br><br>' => "\n\n",
                ];
                $sMessage=str_replace(array_keys($_aReplace), array_values($_aReplace), $sMessage)." *";
            }
            return htmlentities($sMessage);
        }
        /**
         * render html code for info link that shows popup with metadata on mouseover
         * @param array $aInfos   metainfos of the package (from json file)
         *                   one of ok=1|error=message - status key
         *                   date      - timestamp of build
         *                   revision  - revision
         *                   branch    - branch
         *                   message   - commit message
         * @param array $aOptions options
         *                   title - tile in popover; default: empty
         *                   label - link label; default: empty (results in Infos|ERROR)
         *                   more  - additional infos in popover; default: empty
         *                   hpos  - horizontal position; one of left|right; default: right
         * @return string
         */
        public function renderInfoLink($aInfos, $aOptions = array()) {
            $sReturn = '';
            $bIsError = false;
            $this->_oHtml = new htmlguielements();
    
            $sInfos='';
            $sTitle='';
            if (array_key_exists("title", $aOptions) && $aOptions["title"]) {
                $sTitle.=$aOptions["title"];
            }
            if (array_key_exists("ok", $aInfos)) {
                $sLinktitle = t('infos');
                if (array_key_exists("message", $aInfos)) {
                    $sInfos.=$this->_getChecksumDiv($aInfos["revision"],
                            $this->_oHtml->getIconByType('calendar') . t('build-from') . ' ' . date("d.m.Y H:i:s", strtotime($aInfos["date"])) . '<br>'
                            . $this->_oHtml->getIconByType('branch') . t('branch') . ': ' . $aInfos["branch"] . '<br>'
                            . $this->_oHtml->getIconByType('revision') . t('revision') . ': ' . $this->_renderRevision($aInfos["revision"]) . '<br>'
                            . $this->_oHtml->getIconByType('comment') . t('commitmessage') . ': '
                            )
                            . '<pre>' . $this->transformCommitMessage($aInfos["message"]) . '</pre>';
                    if (array_key_exists("more", $aOptions)) {
                        $sInfos.=$aOptions["more"];
                    }
                }
            } else {
                $bIsError = true;
                if (!$sTitle) {
                    $sTitle.=' ' . t('error');
                }
                $sLinktitle = t('error');
                $sInfos = $this->_oHtml->getBox('error', '') . '<p>'.$aInfos["error"].'</p>';
            }
            $sInfos.=$this->_renderHostsData($aInfos, '');
    
            if (array_key_exists("label", $aOptions) && $aOptions["label"]) {
                $sLinktitle.=$aOptions["label"];
            }
    
            // render html
            $sId = 'info' . md5($sInfos);
            $sReturn = '<a href="#" class="btn ' . ($bIsError ? 'btn-danger' : 'btn-default') . '" title="" 
                    onclick="showIdAsModalMessage(\'' . $sId . '\', \''.$sTitle.'\'); return false;"
                    >'
                    . $this->_oHtml->getIcon($bIsError ? 'sign-error' : 'sign-info') // ... '<i class="fa fa-info"></i> '
                    . $sLinktitle
                    . '</a><div id="' . $sId . '" style="display: none;" '
                    ;
            if (array_key_exists("hpos", $aOptions)) {
                $sReturn.=' class="' . $aOptions["hpos"] . '"';
            }
            $sReturn.='>';
    
            if ($sTitle) {
                // $sReturn.='<span class="title">' . $sTitle . '</span><br><br>';
            }
    
            $sReturn.=$sInfos . '</div>';
    
            if ($bIsError) {
                // $sReturn = '<div class="error">' . $sReturn . '</div>';
            }
    
            return $sReturn;
        }
    
        /**
         * render html for a colored link to any project action
         * @param string $sFunction name of the action; one of accept|build|cleanup|deploy|new|overview|phase|rollback|setup
         * @param string $sPhase    current phase where to place the link
         * @return string
         */
        public function renderLink($sFunction, $sPhase = false, $sVersion = false) {
            $sFirst = $this->getNextPhase();
            $sNext = $this->getNextPhase($sPhase);
            $aLinkdata = array(
                'default' => array('class' => ''),
                'accept' => array('class' => $sNext,
                    'hint' => sprintf(t("accept-hint"), $sPhase, $sNext),
                    'label' => t('accept'),
                ),
                'build' => array('class' => $sFirst,
                    'hint' => sprintf(t("build-hint"), $sFirst),
                    'label' => t('build'),
                    'role' => 'buildProject'
                ),
                'cleanup' => array('class' => ''),
                'deploy' => array('class' => $sPhase,
                    'hint' => sprintf(t("deploy-hint"), $sPhase),
                    'label' => t('deploy'),
                ),
                'new' => array(
                    'hint' => t("new-project-hint"),
                    'label' => t('new-project'),
                ),
                'overview' => array('class' => '',
                    'hint' => t('menu-project-home') . ' [' . $this->getLabel() . ']',
                    'label' => $this->getLabel()
                ),
                'phase' => array('icon' => $this->_oHtml->getIcon('phase'), 'class' => $sPhase,
                    'hint' => sprintf(t('phase-details-hint'), $sPhase),
                    'label' => sprintf(t('phase-details'), $sPhase),
                ),
                'rollback' => array('class' => $sPhase,
                    'hint' => sprintf(t('rollback-hint'), $sPhase, $sVersion),
                    'label' => t('rollback')
                ),
                'setup' => array('class' => $sPhase,
                    'hint' => sprintf(t('setup-hint'), $sPhase, $sVersion),
                    'label' => t('setup'),
                    'class' => 'btn'
                ),
            );
            /*
              if (!$this->oUser->hasRole("project-action-$sFunction")){
              // $sClass .= ' disabled';
              // return '<span title="no permission [project-action-'.$sFunction.']">[ ]</span>';
              }
             * 
             */
            // fuer wen ist der Link
            $sRole = '';
            $sOnMouseover = '';
            $sOnMouseout = '';
            switch($sFunction){
                case 'accept';
                    $sRole = 'developer';
                    if ($sNext == "live") {
                        $sRole = 'pl';
                        // $aLinkdata[$sFunction]['icon']='glyphicon glyphicon-star';
                    }
                    $sOnMouseover = '$(\'.td-phase-' . $sNext . '.td' . $this->_aConfig["id"] . '\').addClass(\'highlight\');';
                    $sOnMouseout = '$(\'.td-phase-' . $sNext . '.td' . $this->_aConfig["id"] . '\').removeClass(\'highlight\');';
                    break;
                case 'build';
                    $sRole = 'developer';
                    $sOnMouseover = '$(\'.td-phase-' . $sNext . '.td' . $this->_aConfig["id"] . '\').addClass(\'highlight\');';
                    $sOnMouseout = '$(\'.td-phase-' . $sNext . '.td' . $this->_aConfig["id"] . '\').removeClass(\'highlight\');';
                    break;
                case 'deploy';
                    $sRole = 'developer';
                    $sOnMouseover = '$(\'.td-phase-' . $sPhase . '.td-place-ready2install.td' . $this->_aConfig["id"] . '\').addClass(\'highlight\');'
                            .'$(\'.td-phase-' . $sPhase . '.td-place-deployed.td' . $this->_aConfig["id"] . '\').addClass(\'highlight\');'
                            ;
                    $sOnMouseout = '$(\'.td-phase-' . $sPhase . '.td-place-ready2install.td' . $this->_aConfig["id"] . '\').removeClass(\'highlight\');'
                            .'$(\'.td-phase-' . $sPhase . '.td-place-deployed.td' . $this->_aConfig["id"] . '\').removeClass(\'highlight\');'
                            ;
                    break;
            }
    
            // $sClass = $sPhase;
            $sIconClass = (array_key_exists($sFunction, $aLinkdata)) ? $aLinkdata[$sFunction]['icon'] : $aLinkdata['default']['icon'];
            $sHint = isset($aLinkdata[$sFunction]['hint']) ? $aLinkdata[$sFunction]['hint'] : "";
            $sLabel = isset($aLinkdata[$sFunction]['label']) ? $aLinkdata[$sFunction]['label'] : $sFunction;
            $sClass = isset($aLinkdata[$sFunction]['class']) ? $aLinkdata[$sFunction]['class'] : '';
            if ($sRole) {
                $sClass .= " role role" . $sRole;
            }
    
            $sLink = "/deployment/" . ($this->_aConfig["id"] ? $this->_aConfig["id"] : 'all/setup') . "/";
            if ($sFunction != "overview") {
                $sLink.="$sFunction/";
            }
            if ($sPhase) {
                $sLink.="$sPhase/";
            }
            if ($sVersion) {
                $sLink.="$sVersion/";
            }
            if (!$this->oUser->hasPermission("project-action-$sFunction")) {
                // $sClass .= ' disabled';
                return '<span class="btn disabled btn-default" title="no permission [project-action-' . $sFunction . '] for user [' . $this->oUser->getUsername() . ']"><i class="' . $sIconClass . '"></i> ' . $sLabel . '</span>';
            }
    
            // $sClass='btn ' . (strstr('btn-', $sClass) ? '': 'btn-default ') .$sClass;
            return $this->_oHtml->getLinkButton(array(
                        'href' => $sLink,
                        'title' => $sHint,
                        'class' => $sClass,
                        'type' => $sFunction,
                        'onmouseover' => $sOnMouseover,
                        'onmouseout' => $sOnMouseout,
                        'label' => $sLabel,
            ));
            // return '<a href="' . $sLink . '" ' . $sOnMouseover . ' title="' . $sHint . '" class="btn  btn-default ' . $sClass . '"><i class="' . $sIconClass . '"></i> ' . $sLabel . '</a>';
        }
    
        /**
         * return html code for the setup form for a new project
         * @return string
         */
        public function renderNewProject() {
            global $aParams;
            if (!$this->oUser->hasPermission("project-action-create")) {
                return $this->oUser->showDenied();
            }
    
            require_once ("formgen.class.php");
            $i = 0;
            $sID = array_key_exists("id", $aParams) ? $aParams["id"] : "";
    
            $aForms = array(
                'setup' => array(
                    'meta' => array(
                        'method' => 'POST',
                        'action' => '?',
                    ),
                    'validate' => array(),
                    'form' => array(
                        'input' . $i++ => array(
                            'type' => 'hidden',
                            'name' => 'setupaction',
                            'value' => 'create',
                        ),
                        'input' . $i++ => array(
                            'type' => 'text',
                            'name' => 'id',
                            'label' => t("class-project-info-setup-projectId"),
                            'value' => $sID,
                            'required' => 'required',
                            'validate' => 'isastring',
                            'size' => 100,
                            'pattern' => '[a-z0-9\-_]*',
                            'placeholder' => t("class-project-info-setup-projectId-placeholder"),
                        ),
                    ),
                ),
            );
            $aForms["setup"]["form"]['input' . $i++] = array(
                'type' => 'submit',
                'name' => 'btnsave',
                'label' => t("save"),
                'value' => $this->_oHtml->getIcon('sign-ok') . t("save"),
            );
    
            $oForm = new formgen($aForms);
            return $oForm->renderHtml("setup");
        }
        /**
         * render html for a place of a phase
         * @param string  $sPhase    phase
         * @param string  $sPlace    name of the place; one of onhold|ready2install|deployed
         * @param bool    $bActions  draw action links (deploy, accept) on/ off
         * @param bool    $bLong     use long variant to display infos? 
         * @return string|boolean
         */
        public function renderPhaseDetail($sPhase, $sPlace, $bActions = true, $bLong = true) {
    
            if (!$sPhase) {
                return false;
            }
            if (!$sPlace) {
                return false;
            }
            if (!$this->isActivePhase($sPhase)) {
                return false;
            }
            if (!array_key_exists($sPlace, $this->_aPlaces)) {
                return false;
            }
      
            $sReturn = '';
            $aDataPhase = $this->getPhaseInfos($sPhase);
            $aData = $aDataPhase[$sPlace];
            // foreach($aDataPhase[$sPlace] as $aData) {
            if (array_key_exists("ok", $aData) && array_key_exists("version", $aData)) {
                // TODO: getChecksumDiv anhand der Repo-Versionsnummer - dann kann man beim build auch die Farbe mit dem Repo HEAD vergleichen
                // time
                $sDateFormat = "d.m.Y H:i";
                $oPkgDate = date("U", strtotime($aData["date"]));
                /*
                  $iAge=date("U")-$oPkgDate;
                  $sAgeClass="";
                  if ($iAge< 60*60*24*3){
                  $sAgeClass="last1d";
                  }
                  if ($iAge< 60*60){
                  $sAgeClass="last1h";
                  }
                 */
    
                if ($bLong) {
                    // long display of the revision
                    // $sJsonUrl = $this->_getInfofile($sPhase, $sPlace);
                        $sReturn .=$this->_getChecksumDiv(
                            $aData["revision"], 
                            $this->_oHtml->getIconByType('calendar') .' ' . date($sDateFormat, $oPkgDate) . '<br>'
                            . $this->_oHtml->getIconByType('branch') . t('branch') . ': ' . $aData["branch"] . '<br>'
                            . $this->_oHtml->getIconByType('revision') . t('revision') . ': ' . $this->_renderRevision($aData["revision"]) . '<br>'
                            . $this->_oHtml->getIconByType('comment') . t('commitmessage') . ':<br>'
                        )
                        . '<pre>' . $this->transformCommitMessage($aData["message"]) . '</pre>'
                    // . '<i class="glyphicon glyphicon-globe"></i> ' . t('url') . ': <a href="' . $sJsonUrl . '">' . $sJsonUrl . '</a><br>'
                    ;
                    if ($sPlace == "deployed" && array_key_exists("url", $this->_aPrjConfig["phases"][$sPhase])) {
                        $sUrl = $this->_aPrjConfig["phases"][$sPhase]["url"];
                        $sReturn.=$this->_oHtml->getIconByType('link-extern') . ' '. t('url') . ': <a href="' . $sUrl . '">' . $sUrl . '</a><br>';
                    }
                } else {
                    $sReturn .= $this->_getChecksumDiv(
                                $aData["revision"], 
                                $this->_oHtml->getIconByType('calendar') .' ' . date($sDateFormat, $oPkgDate)
                        );
                    if ($sPlace == "deployed" && array_key_exists("url", $this->_aPrjConfig["phases"][$sPhase])) {
                        $sMore = $this->_oHtml->getIconByType('link-extern').' '
                                . t('url')
                                . ': <a href="' . $this->_aPrjConfig["phases"][$sPhase]["url"] . '">' . $this->_aPrjConfig["phases"][$sPhase]["url"] . '</a><br>';
                    }
    
                    $sReturn.=' ' . $this->renderInfoLink(
                                    $aData, 
                                    [
                                        'title' => $this->getLabel() . " :: $sPhase :: $sPlace",
                                        'more' => $sMore,
                                    ]
                    );
                }
    
                switch ($sPlace) {
                    case "onhold":
                        if (array_key_exists("phases", $this->_aConfig) && array_key_exists($sPhase, $this->_aConfig["phases"])) {
                            // $sReturn .= print_r($this->_aConfig["phases"][$sPhase], true);
                            if (count($this->_getDeploytimes($sPhase))) {
                                $sReturn .= '<br>'.$this->_oHtml->getIcon('time').t('deploytimes') . ':<br>'
                                        . implode("<br>", array_values($this->_getDeploytimes($sPhase)))
                                        . '<br>';
                            }
                            if ($bActions) {
                                $sReturn .= ' ' . $this->renderLink("deploy", $sPhase);
                            }
                        }
                        break;
    
                    case "ready2install":
                        /*
                        // IDEA: try to install the same phase again. Needs update of method project->deploy() 
                        if ($bActions) {
                            $sReturn .= ' ' . $this->renderLink("deploy", $sPhase);
                        }
                        */
                        break;
    
                    case "deployed":
                        if ($bActions && $this->canAcceptPhase($sPhase)) {
                            $sReturn .= ' ' . $this->renderLink("accept", $sPhase);
                        }
                        break;
                    default:
                        break;
                }
                // $this->_getChecksumDiv($aData["revision"])
            } else {
                if (array_key_exists("error", $aData)) {
                    $sReturn.=''
                            . $this->renderInfoLink(array('error' => $aData["error"]), array())
                    ;
                } else if (array_key_exists("warning", $aData)) {
                    $sReturn.= '<div class="warning">'.$this->_oHtml->getIcon('sign-info'). t('warning') . ':<br>' . $aData["warning"] . '</div>';
                } else {
                    
                    // OK = 1 ... for the queue we show no hint
                    return '';
                    /*
                    return $sPlace=='onhold' 
                        ? t('class-project-queue-empty')
                        : ''
                        ;
                    */
                }
            } // if
            // } // for
            return $sReturn;
        }
    
        /**
         * render html for the project overview; it shows the defined phases for 
         * the project as a table
         * @return type
         */
        public function renderPhaseInfo() {
            $sRow1 = false;
            $sRow2 = false;
    
            $renderAdminLTE=new renderadminlte();                    
    
            $iWidth=min(12 / count($this->getActivePhases()), 4);
            foreach ($this->getActivePhases() as $sPhase) {
                $sRow1.=$renderAdminLTE->addCol(
                    '<table class="nomargin"><tr><th class="' . $sPhase . ' tdphase">' . $sPhase . '</th></tr></table>'
                    ,
                    $iWidth);
                  
                $sDetails=t('url') . ': <a href="' . $this->_aPrjConfig["phases"][$sPhase]["url"] . '">' . $this->_aPrjConfig["phases"][$sPhase]["url"] . '</a><br>'
                . '<br>' . t('deploytimes') . ':<br>';
                if (count($this->_getDeploytimes($sPhase))) {
                    $sDetails.=implode("<br>", $this->_getDeploytimes($sPhase));
                } else {
                    $sDetails.=t('deploytimes-immediately');
                }
                $sDetails.='<br>' . $this->renderLink("phase", $sPhase)
                        . $this->_renderHosts($sPhase)
                        . '<br>'
                        . $this->_renderFiles($sPhase)
                        ;
    
                $sRow2.=$renderAdminLTE->addCol(
                    $renderAdminLTE->getCard(array (
                        'class' => $sPhase,
                        'variant' => '',
                        'tb-remove' => 1,
                        'tb-collapse' => 1,
                        'title' => '',
                        'tools' => '',
                        'text' => $sDetails,
                        'footer' => '',
                    )), 
                    $iWidth
                );
            }
            return ''
                .$renderAdminLTE->addRow($sRow1)
                .$renderAdminLTE->addRow($sRow2)
                ;
    
        }
    
        /**
         * render html for a row with td for all places (first row)
         * @param string $sPhase  phase (just needed for coloring)
         * @return string
         */
        public function renderPlacesAsTd($sPhase) {
            $sRow1 = '';
            foreach (array_keys($this->_aPlaces) as $sPlace) {
                $sRow1.='<td class="' . $sPhase . ' ' . $this->_aConfig["id"] . ' tdphase">' . t($sPlace) . '</td>';
            }
            return $sRow1;
        }
    
        /**
         * return html code for the setup form of an exsiting project
         * @return string
         */
        public function renderProjectSetup() {
            if (!$this->oUser->hasPermission("project-action-setup")) {
                return $this->oUser->showDenied();
            }
            $sMessages = '';
            require_once ("formgen.class.php");
    
    
                $aSelectProjectGroup = array(
                    'type' => 'select',
                    'name' => 'projectgroup',
                    'label' => t("projectgroup"),
                    'options' => array(
                        OPTION_NONE => array(
                            'label' => t('none'),
                        ),
                        '' => array(
                            'label' => '- - - - - - - - - - - - - - - - - - - - ',
                        ),
                    ),
                );
                foreach($this->_aConfig['projectgroups'] as $sGroupid=>$sGroupLabel){
                    $bActive=$this->getProjectGroup() === $sGroupid;
                    $aSelectProjectGroup['options'][$sGroupid] = array(
                        'label' => $sGroupLabel,
                        'selected' => $bActive ? 'selected' : false,
                    );
                }
                
            $aSelectSlack = array(
                    'type' => 'hidden',
                    'name' => 'messenger[slack]',
                    'value' => false,
            );
            if (
                    isset($this->_aConfig['messenger']['slack']['presets'])
                    && count($this->_aConfig['messenger']['slack']['presets'])
            ) {
                $aSelectSlack = array(
                    'type' => 'select',
                    'name' => 'messenger[slack]',
                    'label' => t("messenger-slack"),
                    'options' => array(
                        OPTION_NONE => array(
                            'label' => t('none'),
                        ),
                        '' => array(
                            'label' => '- - - - - - - - - - - - - - - - - - - - ',
                        ),
                    ),
                );
                foreach($this->_aConfig['messenger']['slack']['presets'] as $sSlackUrl=>$aSlackCfg){
                    $bActive=$this->_aPrjConfig['messenger']['slack'] === $sSlackUrl;
                    $aSelectSlack['options'][$sSlackUrl] = array(
                        'label' => array_key_exists('label', $aSlackCfg) ? $aSlackCfg['label'] : $sSlackUrl,
                        'selected' => $bActive ? 'selected' : false,
                    );
                }
                
            }
            // ---------- Build plugins
            /*
            
            $aPluginsBuild = array(
                'select' => array(
                    'type' => 'checkbox',
                    'name' => 'build[enabled_build_plugins]',
                    'label' => t("build-plugins"),
                    'options' => [],
                ),
                // 'project-config' => '',
            );
            foreach (array_keys($this->getConfiguredPlugins('build')) as $sPluginName){
    
                $sPluginFile=$this->_getPluginFilename('build', $sPluginName);
                $TmpRolloutPlugin = false;
                $sMyClassname='build_'. $sPluginName;
                if(file_exists($sPluginFile)){
                try{
                    include_once $this->_getPluginFilename('build', $sPluginName);
                    $TmpRolloutPlugin = new $sMyClassname([]);
                    echo "FOUND $sMyClassname<br>";
                    $aPluginsBuild['select']['options'][$sPluginName]=array(
                            'label' => $TmpRolloutPlugin->getName(),
                            'checked' => $bActive,
                            // 'onclick' => '$(\'.'.$sMyDivClass.'\').hide(); $(\'.' . $sMyDivClassActive . '\').show();',
                        );
                    } catch (Exception $ex) {
    
                    }
                } else {
                    $aRollout['project-select']['options'][$sPluginName]=array(
                            'label' => 'not found: <span class="error">' . $sMyClassname . '</span>',
                            'checked' => false,
                            'disabled' => "disabled",
                        );
    
                    
                }
            }
            echo '<pre>'; print_r($aPluginsBuild); die(__METHOD__);
            */
    
            // ---------- /Build plugins
            
            // ---------- Rollout plugins
            $aRollout = array(
                'project-select' => array(
                    'type' => 'radio',
                    'name' => 'deploy[enabled_rollout_plugin]',
                    'label' => t("deploy-rollout-plugin"),
                ),
                'project-config' => '',
            );
            foreach (array_keys($this->getConfiguredPlugins('rollout')) as $sPluginName){
    
                $sPluginFile=$this->_getPluginFilename('rollout', $sPluginName);
                $TmpRolloutPlugin = false;
                $sMyClassname='rollout_'. $sPluginName;
                $sMyDivId='rollout-'. $sPluginName.'-config';
                $sMyDivClass='rolloutconfigdiv';
                $sMyDivClassActive='rolloutconfigdiv-'. $sPluginName;
                $bActive=$sPluginName === $this->oRolloutPlugin->getId();
    
                if(file_exists($sPluginFile)){
                    try{
                        include_once $this->_getPluginFilename('rollout', $sPluginName);
                        $TmpRolloutPlugin = new $sMyClassname(array(
                            'lang'=>$this->_aConfig['lang'],
                            'phase'=>false,
                            'globalcfg'=>$this->_aConfig['plugins']['rollout'][$sPluginName],
                            'projectcfg'=>$this->_aPrjConfig,
                        ));
                        $aRollout['project-select']['options'][$sPluginName]=array(
                                'label' => $TmpRolloutPlugin->getName(),
                                'checked' => $bActive,
                                'onclick' => '$(\'.'.$sMyDivClass.'\').hide(); $(\'.' . $sMyDivClassActive . '\').show();',
                            );
                        
                        $aRollout['project-config'].=''
                                . '<div id="'.$sMyDivId.'" class="'.$sMyDivClass.' '.$sMyDivClassActive.'"'
                                . ($bActive ? '' : ' style="display: none;"' )
                                . '>'
                                    . $TmpRolloutPlugin->renderFormdata4Project()
                                . '</div>'
                                ;
                        
                        // generate form firlds for each phase
                        foreach(array_keys($this->getPhases()) as $sMyPhase){
                            $aRollout[$sMyPhase].=''
                                . '<div id="'.$sMyDivId.'-'.$sMyPhase.'" class="'.$sMyDivClass.' '.$sMyDivClassActive.'"'
                                . ($bActive ? '' : ' style="display: none;"' )
                                . '>'
                                    . $TmpRolloutPlugin->renderFormdata4Phase($sMyPhase)
                                . '</div>'
                                ;
                        }
                    } catch (Exception $ex) {
    
                    }
                } else {
                    $aRollout['project-select']['options'][$sPluginName]=array(
                            'label' => 'not found: <span class="error">' . $sMyClassname . '</span>',
                            'checked' => false,
                            'disabled' => "disabled",
                        );
    
                    
                }
            }
            // ---------- /Rollout plugins
            
            $aForemanHostgroups = false;
            $iForemanHostgroupDefault = false;
            $sForemanHostgroupDefault = false;
            if (array_key_exists('foreman', $this->_aConfig)) {
                // echo '<pre>' . print_r($this->_aPrjConfig, 1) . '</pre>';
                $iForemanHostgroupDefault = (int) $this->_aPrjConfig['deploy']['foreman']['hostgroup'];
                require_once('foremanapi.class.php');
                $oForeman = new ForemanApi($this->_aConfig['foreman']);
                // $oForeman->setDebug(1);
                // $oForeman->selfcheck(); die(__FUNCTION__);
    
                $aForemanHostgroups = $oForeman->read(array(
                    'request' => array(
                        array('hostgroups'),
                    // array('operatingsystems',4),
                    ),
                    'response' => array(
                        'id', 'title'
                    ),
                ));
                $aSelectForemanGroups = array(
                    'type' => 'select',
                    'name' => 'deploy[foreman][hostgroup]',
                    'label' => $this->_oHtml->getIcon('foreman') . t("foreman-hostgroup"),
                    'options' => array(
                        OPTION_NONE => array(
                            'label' => t('none'),
                        ),
                        '' => array(
                            'label' => '- - - - - - - - - - - - - - - - - - - - ',
                        ),
                    ),
                );
                if ($aForemanHostgroups && count($aForemanHostgroups)) {
                    foreach ($aForemanHostgroups as $aItem) {
                        $bActive=$iForemanHostgroupDefault === (int) $aItem['id'];
                        $aSelectForemanGroups['options'][$aItem['id']] = array(
                            'label' => $aItem['title'],
                            'selected' => $bActive ? 'selected' : false,
                        );
                        $sForemanHostgroupDefault = $bActive ? $aItem['title'] : $sForemanHostgroupDefault;
                    }
                }
            }
    
    
            $i = 0;
    
            $aPrefixItem = count($this->getVersions()) ?
                    array(
                'type' => 'markup',
                'value' => '<div class="form-group">
                            <label class="col-sm-2">' . t('fileprefix') . '</label>
                            <div class="col-sm-10">
                                <input id="inputprefix" type="hidden" name="fileprefix" value="' . $this->_aPrjConfig["fileprefix"] . '">
                                ' . $this->_aPrjConfig["fileprefix"] . '
                            </div></div>
                                ',
                    ) : array(
                'type' => 'text',
                'name' => 'fileprefix',
                // 'disabled' => 'disabled',
                'label' => t('fileprefix-label'),
                'value' => $this->_aPrjConfig["fileprefix"],
                'required' => 'required',
                'validate' => 'isastring',
                'pattern' => '[a-z0-9\-_]*',
                'size' => 100,
                'placeholder' => '',
            );
    
            // detect access to repo url
            $aBranches=$this->getRemoteBranches(true);
            // $aRepodata = $this->getRepoRevision();
    
            // if (is_array($aRepodata) && array_key_exists("message", $aRepodata)) {
            if (is_array($aBranches) && count($aBranches)) {
                $sRepoCheck = '<span class="ok">' . sprintf(t('class-project-info-repoaccess'), count($aBranches)) . '</span>';
            } else {
                $sRepoError=sprintf(t('class-project-error-no-repoaccess'), $aRepodata["error"]);
                $sRepoCheck = '<span class="error">' . $sRepoError . '</span>';
                $sMessages.=$this->_oHtml->getBox("error", $sRepoError);
            }
    
            // generate datalist with exisating ssh keys for auth field
            $sAuthListitems = '';
            foreach ($this->_getSshKeys() as $sKey) {
                $sAuthListitems.='<option value="' . $sKey . '">';
            }
            $aForms = array(
                'setup' => array(
                    'meta' => array(
                        'method' => 'POST',
                        'action' => '?',
                    ),
                    'validate' => array(),
                    'form' => array(
                        'input' . $i++ => array(
                            'type' => 'hidden',
                            'name' => 'setupaction',
                            'value' => 'save',
                        ),
                        'input' . $i++ => array(
                            'type' => 'hidden',
                            'name' => 'id',
                            'value' => $this->_aConfig["id"],
                        ),
                        'input' . $i++ => array(
                            'type' => 'markup',
                            'value' => '<div class="tabbable">
                                <ul class="nav nav-tabs">
                                    <li class="active"><a href="#tab1" class="nav-link active" data-toggle="tab" aria-selected="1">' . $this->_oHtml->getIcon('list').t('setup-metadata') . '</a></li>
                                    <li><a href="#tab2" class="nav-link" data-toggle="tab">' . $this->_oHtml->getIcon('repository').t('repositoryinfos') . '</a></li>
    
                                    <li><a href="#tab3" class="nav-link" data-toggle="tab">' . $this->_oHtml->getIcon('deploy-configfile').t('deploy-configfile') . '</a></li>
                                    <li><a href="#tab4" class="nav-link" data-toggle="tab">' . $this->_oHtml->getIcon('deploy-rollout-plugin').t('deploy-rollout-plugin') . '</a></li>
                                    <li><a href="#tab5" class="nav-link" data-toggle="tab">' . $this->_oHtml->getIcon('phase').t('phases') . '</a></li>
                                    <li><a href="#tab6" class="nav-link" data-toggle="tab">' . $this->_oHtml->getIcon('raw-data').t('raw-data') . '</a></li>
                                </ul>
                                <div class="tab-content">
                                <div class="tab-pane fade active show" id="tab1">
                                <br>
                                
                                ',
                        ),
    
                        // --------------------------------------------------
                        // Tab for metadata
                        // -------------------------------------------------
                        'input' . $i++ => array(
                            'type' => 'text',
                            'name' => 'label',
                            'label' => t('projectname'),
                            'value' => $this->_aPrjConfig["label"],
                            'required' => 'required',
                            'validate' => 'isastring',
                            'size' => 100,
                            'placeholder' => 'Projekt',
                        ),
                        'input' . $i++ => array(
                            'type' => 'text',
                            'name' => 'description',
                            'label' => t('projectdescription'),
                            'value' => $this->_aPrjConfig["description"],
                            'required' => 'required',
                            'validate' => 'isastring',
                            'size' => 100,
                            'placeholder' => '',
                        ),
                        'input' . $i++ => array(
                            'type' => 'text',
                            'name' => 'contact',
                            'label' => t('contact'),
                            'value' => $this->_aPrjConfig["contact"],
                            'required' => 'required',
                            'validate' => 'isastring',
                            'size' => 100,
                            'placeholder' => '',
                        ),
    
                        'input' . $i++ => $aSelectProjectGroup,
    
                        'input' . $i++ => array(
                            'type' => 'markup',
                            'value' => '<p>' . t('messenger') . '</p>',
                        ),
                        'input' . $i++ => array(
                            'type' => 'text',
                            'name' => 'messenger[email]',
                            'label' => t("messenger-email"),
                            'value' => $this->_aPrjConfig["messenger"]["email"],
                            'validate' => 'isastring',
                            'size' => 100,
                            'placeholder' => '',
                            'autocomplete' => 'off',
                        ),
                        
                        'input' . $i++ => $aSelectSlack,
                        
                        // --------------------------------------------------
                        // Tab soources repository & build
                        // --------------------------------------------------
                        'input' . $i++ => array(
                            'type' => 'markup',
                            'value' => ' </div><div class="tab-pane fade" id="tab2">
                                <p>' . t('setup-hint-build') . '</p>',
                        ),
                        'input' . $i++ => array(
                            'type' => 'text',
                            'name' => 'build[type]',
                            'label' => t("build-type"),
                            'value' => $this->_aPrjConfig["build"]["type"],
                            'required' => 'required',
                            'validate' => 'isastring',
                            'size' => 100,
                            'placeholder' => '',
                        ),
                        'input' . $i++ => array(
                            'type' => 'text',
                            'name' => 'build[url]',
                            'label' => t("repository-url"),
                            'value' => $this->_aPrjConfig["build"]["url"],
                            // 'required' => 'required',
                            'validate' => 'isastring',
                            'size' => 100,
                            'placeholder' => '',
                        ),
                        'input' . $i++ => array(
                            'type' => 'text',
                            'name' => 'build[auth]',
                            'label' => t("repository-auth"),
                            'value' => $this->_aPrjConfig["build"]["auth"],
                            // 'required' => 'required',
                            'list' => 'listauth', // listauth is the next form id below
                            'validate' => 'isastring',
                            'size' => 100,
                            'placeholder' => '',
                        ),
                        'input' . $i++ => array(
                            'type' => 'markup',
                            'value' => '<datalist id="listauth">' . $sAuthListitems . '</datalist>',
                        ),
                        'input' . $i++ => array(
                            'type' => 'markup',
                            'value' => '<div class="form-group">'
                            . '<label class="col-sm-2"> </label><div class="col-sm-10">'
                            . $sRepoCheck
                            . '</div></div>',
                        ),
                        'input' . $i++ => array(
                            'type' => 'text',
                            'name' => 'build[webaccess]',
                            'label' => t("repository-urlwebgui"),
                            'value' => $this->_aPrjConfig["build"]["webaccess"],
                            'validate' => 'isastring',
                            'size' => 100,
                            'placeholder' => '',
                        ),
                        'input' . $i++ => $aPrefixItem,
                        'input' . $i++ => array(
                            'type' => 'markup',
                            'value' => '<div style="clear: both"></div>',
                        ),
                        // task#1498 - handle project without "public" directory
                        'input' . $i++ => array(
                            'type' => 'checkbox',
                            'name' => 'build[haspublic]',
                            'label' => t("repository-has-public-dir"),
                            'required' => false,
                            'validate' => 'isastring',
                            'options' => array(
                                '1' => array(
                                    'label' => t("yes"),
                                    'checked' => (array_key_exists('haspublic', $this->_aPrjConfig["build"]) ? $this->_aPrjConfig["build"]["haspublic"] : 0),
                                ),
                            ),
                        ),
    
                        // --------------------------------------------------
                        // Tab for config and API key
                        // --------------------------------------------------
                        'input' . $i++ => array(
                            'type' => 'markup',
                            'value' => ' </div><div class="tab-pane fade" id="tab3">
                                <p>' . t('deploy-configfile-hint') . '</p>',
                        ),
                        'textarea' . $i++ => array(
                            'type' => 'textarea',
                            'name' => 'deploy[configfile]',
                            'label' => t("deploy-configfile"),
                            'value' => $this->_aPrjConfig['deploy']["configfile"],
                            // 'required' => 'required',
                            'validate' => 'isastring',  
                            'cols' => 100,
                            'rows' => 10,
                            'placeholder' => 'export myvariable=&quot;hello world&quot;',
                        ),
       
                        'input' . $i++ => array(
                            'type' => 'text',
                            'name' => 'api[secret]',
                            'label' => t("api-secret"),
                            'value' => $this->_aPrjConfig["api"]["secret"],
                            'validate' => 'isastring',
                            'size' => 100,
                            'placeholder' => '',
                        ),                    
                        'input' . $i++ => array(
                            'type' => 'markup',
                            'value' => '<div class="col-sm-12">'
                            . '<p>' . t('api-secret-hint') . '<br>'
                                . '<a href="#" class="btn btn-default" onclick="$(\'#input'.($i-2).'\').val(generateSecret(64)); return false">'.t("api-secret-generate").'</a>'
                            . '</p></div>',
                        ),
                        
                        // --------------------------------------------------
                        // Tab rollout plugin
                        // --------------------------------------------------
                        'input' . $i++ => array(
                            'type' => 'markup',
                            'value' => ' </div><div class="tab-pane fade" id="tab4">
                                <p>' . t('deploy-rollout-plugin-hint') . '</p>',
                        ),
                        // select box for active rollout plugin
                        $aRollout['project-select'],
                        
                        // project based config 
                        'input' . $i++ => array(
                            'type' => 'markup',
                            'value' => ''
                                . '<hr>'
                                    .'<label class="col-sm-2">'.t('deploy-rollout-plugin-config') .'</label>'
                                    .'<div class="col-sm-10">'. $aRollout['project-config'].'</div>'
                        ),
                        // --------------------------------------------------
                        'input' . $i++ => array(
                            'type' => 'markup',
                            'value' => ' </div><div class="tab-pane fade" id="tab5">
                                <p>' . sprintf(t("class-project-info-setup-phaseinfos"), $this->getNextPhase()) . '</p>',
                        ),
                    ),
                ),
            );
            // --------------------------------------------------
            // Tab for phases
            // --------------------------------------------------
            if ($aSelectForemanGroups) {
                $aForms["setup"]["form"]['input' . $i++] = array(
                    'type' => 'markup',
                    'value' => '<strong>'.t("defaults-all-phases").'</strong><br><br>',
                );
                $aForms["setup"]["form"]['input' . $i++] = $aSelectForemanGroups;
                $aForms["setup"]["form"]['input' . $i++] = array(
                    'type' => 'markup',
                    'value' => '<br><br>',
                );
            }
            foreach (array_keys($this->getPhases()) as $sPhase) {
    
                $bActivePhase = $this->isActivePhase($sPhase);
                $sUrl = array_key_exists("url", $this->_aPrjConfig["phases"][$sPhase]) ? $this->_aPrjConfig["phases"][$sPhase]["url"] : "";
                $sDeploymethod = array_key_exists("deploymethod", $this->_aPrjConfig["phases"][$sPhase]) ? $this->_aPrjConfig["phases"][$sPhase]["deploymethod"] : "";
                $sDeployhosts = array_key_exists("hosts", $this->_aPrjConfig["phases"][$sPhase]) ? $this->_aPrjConfig["phases"][$sPhase]["hosts"] : "";
    
                /*
                 * task-1847 - reove adding ssh key
                if($sDeployhosts){
                    echo "$sDeployhosts<br>";
                    if(!strpos($sDeployhosts, ",")){
                        $sCmd=sprintf($this->_aConfig["installPackages"]["addkeycommand"], $sDeployhosts, $sDeployhosts);
                        exec($sCmd . " 2>&1", $aOut);
                        echo "<pre>\$ $sCmd<br>"
                            . implode('<br>', $aOut)
                            ."</pre>"
                            ;
                    }
                }
                 */
                $sDeploytimes = array_key_exists("deploytimes", $this->_aPrjConfig["phases"][$sPhase]) ? $this->_aPrjConfig["phases"][$sPhase]["deploytimes"] : "";
                $sDivId4PhaseSettings = 'divSettings' . $sPhase;
                $sDivId4TargetHosts = 'divSettings' . $sPhase . 'hosts';
    
                if ($aSelectForemanGroups) {
                    $iForemanHostgroup = (int) $this->_aPrjConfig['phases'][$sPhase]['foreman-hostgroup'];
                    $aSelectForemanHostGroup = array(
                        'type' => 'select',
                        'name' => 'phases[' . $sPhase . '][foreman-hostgroup]',
                        'label' => $this->_oHtml->getIcon('foreman') . t("foreman-hostgroup"),
                        'options' => array(
                            OPTION_DEFAULT => array(
                                'label' => t('default') . ' (' . $sForemanHostgroupDefault . ')',
                                'selected' => $iForemanHostgroup === OPTION_DEFAULT ? 'selected' : false,
                            ),
                            OPTION_NONE => array(
                                'label' => t('none'),
                                'selected' => $iForemanHostgroup === OPTION_NONE ? 'selected' : false,
                            ),
                            '' => array(
                                'label' => '- - - - - - - - - - - - - - - - - - - - ',
                            ),
                        ),
                    );
                    if (is_array($aForemanHostgroups) && count($aForemanHostgroups)) {
                        foreach ($aForemanHostgroups as $aItem) {
                            $aSelectForemanHostGroup['options'][$aItem['id']] = array(
                                'label' => $aItem['title'],
                                'selected' => ($iForemanHostgroup === $aItem['id']) ? 'selected' : false,
                            );
                        }
                    }
                }
                $aForms["setup"]["form"]['input' . $i++] = array(
                    'type' => 'markup',
                    'value' => ''
                    // .'<pre>'.print_r($this->_aPrjConfig["phases"][$sPhase], 1).'</pre>'
                    /*
                      . '<a class="'.$sPhase.'">'
                      . t("phase") . ' ' . $sPhase
                      . '</a>'
                     */
                    . '<table class="table">'
                    . '<tbody>'
                    . '<tr><th class="' . $sPhase . '">' . $this->_oHtml->getIcon('phase') . t("phase") . ' ' . $sPhase . '</th></tr>'
                    . '<tr><td class="' . ($bActivePhase ? $sPhase : '') . '">'
                    . ''
                );
    
                $aForms["setup"]["form"]['input' . $i++] = array(
                    'type' => 'checkbox',
                    'name' => 'phases[' . $sPhase . '][active]',
                    'label' => t("phase-is-active"),
                    // 'value' => $bUsePuppet,
                    'required' => false,
                    'validate' => 'isastring',
                    // 'size' => 100,
                    // 'placeholder' => '...',
                    'options' => array(
                        '1' => array(
                            'label' => t("yes"),
                            'checked' => $bActivePhase,
                            'onclick' => '$(\'#' . $sDivId4PhaseSettings . '\').css(\'display\', (this.checked ? \'block\' : \'none\') )',
                        ),
                    ),
                );
                $aForms["setup"]["form"]['input' . $i++] = array(
                    'type' => 'markup',
                    'value' => ''
                    . '<div id="' . $sDivId4PhaseSettings . '" ' . ($bActivePhase ? '' : ' style="display: none;"') . '>'
                );
                $aForms["setup"]["form"]['input' . $i++] = array(
                    'type' => 'text',
                    'name' => 'phases[' . $sPhase . '][url]',
                    'label' => $this->_oHtml->getIcon('url') . t("url-project-website"),
                    'value' => $sUrl,
                    // 'required' => 'required',
                    'validate' => 'isastring',
                    'size' => 100,
                    'placeholder' => 'https://' . $sPhase . '.[' . t("project") . '].[...]/',
                );
                $aForms["setup"]["form"]['input' . $i++] = array(
                    'type' => 'radio',
                    'name' => 'phases[' . $sPhase . '][deploymethod]',
                    'label' => $this->_oHtml->getIcon('method') . t("deploymethod"),
                    // 'value' => $bUsePuppet,
                    // 'required' => 'required',
                    'validate' => 'isastring',
                    // 'size' => 100,
                    // 'placeholder' => '...',
                    'options' => array(
                        'none' => array(
                            'label' => t("deploymethod-none"),
                            'checked' => $sDeploymethod === "none",
                            'onclick' => '$(\'#' . $sDivId4TargetHosts . '\').css(\'display\', (this.checked ? \'none\' : \'block\') )',
                        ),
                        'rolloutplugin' => array(
                            // 'label' => t("deploymethod-puppet").' - '.  $this->oRolloutPlugin->getName(),
                            'label' => t("deploymethod-rolloutplugin"),
                            'checked' => $sDeploymethod === "rolloutplugin",
                            'onclick' => '$(\'#' . $sDivId4TargetHosts . '\').css(\'display\', (this.checked ? \'block\' : \'none\') )',
                        ),
                    /*
                     * see deploy method to handle an action
                      'sshproxy' => array(
                      'label' => t("deploymethod-sshproxy"),
                      'checked' => $sDeploymethod==="sshproxy",
                      'onclick' => '$(\'#'.$sDivId4TargetHosts.'\').css(\'display\', (this.checked ? \'block\' : \'none\') )',
                      ),
                     */
                    ),
                );
                
    
                $aForms["setup"]["form"]['input' . $i++] = array(
                    'type' => 'markup',
                    'value' => ''
                    . '<div id="' . $sDivId4TargetHosts . '" ' . ($sDeploymethod !== "none" ? '' : ' style="display: none;"') . '>'
                );
                
                // rollout plugin: phase specific overrides
                $aForms["setup"]["form"]['input' . $i++] = array(
                    'type' => 'markup',
                    'value' => ''
                        // . '<hr>'
                        .'<label class="col-sm-2">'.t('deploy-rollout-plugin-config') .'</label>'
                        .'<div class="col-sm-10">'.$aRollout[$sPhase].'</div>'
                ); 
                
                $aForms["setup"]["form"]['input' . $i++] = array(
                    'type' => 'text',
                    'name' => 'phases[' . $sPhase . '][hosts]',
                    'label' => $this->_oHtml->getIcon('host') . t("phase-targethosts"),
                    'value' => $sDeployhosts,
                    // 'required' => 'required',
                    'validate' => 'isastring',
                    'size' => 100,
                    'placeholder' => 'FQDN1,FQDN2',
                );
    
                /*
                  if ($sPuppethost) {
    
                  // add ssh host key
                  $sOut0 = shell_exec(sprintf($this->_aConfig["installPackages"]["addkeycommand"], $sPuppethost, $sPuppethost));
    
                  $sCmd2 = 'ssh ' . $this->_aConfig["installPackages"]["user"]
                  . '@' . $sPuppethost
                  . ' ' . $this->_aConfig["installPackages"]["testcommand"];
                  $sOut = 'skip';
                  // $sOut = shell_exec($sCmd2);
                  // Check auf Versionsnummer - mehr als n Zeichen ist mutmasslich eine Fehlermeldung
                  if (strlen($sOut) > 7) {
                  $sMessages.=$this->getBox("error", sprintf(t("class-project-error-setup-sudo-pupet-agent-failed"), $sPhase, $sCmd, $sOut));
                  $sOut = '<span class="error" title="' . $sCmd . '">' . $sOut . '</span>';
                  } else {
                  $sOut = '<span class="ok">' . sprintf(t("class-project-info-setup-ssh-and-puppet-ok"), $sPuppethost) . '</span>';
                  }
                  $aForms["setup"]["form"]['input' . $i++] = array(
                  'type' => 'markup',
                  'value' => '<div class="form-group">'
                  . '<label class="col-sm-2"> </label><div class="col-sm-10">'
                  . $sOut
                  . '</div></div>',
                  );
                  }
                 */
    
                // when to deploy
                $aForms["setup"]["form"]['input' . $i++] = array(
                    'type' => 'text',
                    'name' => 'phases[' . $sPhase . '][deploytimes]',
                    'label' => $this->_oHtml->getIcon('time') . t("deploytimes"),
                    'value' => $sDeploytimes,
                    // 'required' => 'required',
                    'validate' => 'isastring',
                    'size' => 100,
                    'placeholder' => isset($this->_aConfig["phases"][$sPhase]["deploytimes"]) ? implode(", ", $this->_aConfig["phases"][$sPhase]["deploytimes"]) : '',
                );
                $aForms["setup"]["form"]['input' . $i++] = array(
                    'type' => 'markup',
                    'value' => ''
                    . '</div>'
                );
    
                if ($aSelectForemanGroups) {
                    $aForms["setup"]["form"]['input' . $i++] = $aSelectForemanHostGroup;
                }
    
                $aForms["setup"]["form"]['input' . $i++] = array(
                    'type' => 'markup',
                    'value' => ''
                    . '</div>'
                ); // close div for active phase
    
    
                $aForms["setup"]["form"]['input' . $i++] = array(
                    'type' => 'markup',
                    'value' => '</td></tr></tbody></table>',
                );
            } // END: loop over phases
    
            // --------------------------------------------------
            // Tab for raw data
            // --------------------------------------------------
            
            $sRolloutDebug='<hr>DEBUG:<br>';
            foreach (array_keys($this->getPhases()) as $sPhase) {
                if ($this->isActivePhase($sPhase)){
                    $sRolloutDebug.='<strong>'.$sPhase.'</strong>'
                    . '<pre>Config = '.print_r($this->oRolloutPlugin->getConfig($sPhase, 1), 1).'</pre>'
                    . '<pre>Commands = '.print_r($this->oRolloutPlugin->getDeployCommands($sPhase, 1), 1).'</pre>'
                    ;
                }
            }
    
            $aForms["setup"]["form"]['input' . $i++] = array(
                'type' => 'markup',
                'value' => '</div>'
                
                    . '<div class="tab-pane fade" id="tab6">'
                    . '<br><pre>'.print_r($this->_aPrjConfig, 1).'</pre>'
                    . $sRolloutDebug
                    . '</div>'
                
                . '</div>'
                . '</div>'
                . '<div style="clear: both; margin-bottom: 1em;"></div>'
                
                
                . '<hr>',
            );
            $aForms["setup"]["form"]['input' . $i++] = array(
                'type' => 'submit',
                'name' => 'btnsave',
                'label' => t("save"),
                'value' => $this->_oHtml->getIcon('sign-ok').t("save"),
            );
    
            $oForm = new formgen($aForms);
            return $sMessages . $oForm->renderHtml("setup");
        }
    
        /**
         * return html code for the installed version in the repository
         * @param boolean  $bRefresh  optional: refresh flag; default: use cached information
         * @return string
         */
        public function renderRepoInfo($bRefresh=false) {
            $sReturn = "";
            switch ($this->_aPrjConfig["build"]["type"]) {
                case "git":
    
                    $aRepodata = $this->getRepoRevision($bRefresh);
                    if (array_key_exists("revision", $aRepodata)) {
                        $sReturn.=$this->_getChecksumDiv($aRepodata["revision"],
                            $this->_oHtml->getIconByType('branch') . t('branch') . ': ' . (array_key_exists("branch", $aRepodata) ? $aRepodata["branch"] : '-') . '<br>'
                            . $this->_oHtml->getIconByType('revision') . t('revision') . ': ' . $this->_renderRevision($aRepodata["revision"]) . '<br>'
                            . $this->_oHtml->getIconByType('comment') . t('commitmessage') . ':<br>'
                            )
                            ."<pre>" . htmlentities($aRepodata["message"]). "</pre>";
                    } else {
                        $sReturn .= $this->_oHtml->getBox("error", sprintf(t('class-project-error-no-repoinfo'), $aRepodata["error"]))
                                . $this->renderLink("setup") . '<br>';
                    }
    
                    break;
    
                default:
                    $sReturn .= $this->_oHtml->getBox("error", sprintf(t('class-project-error-wrong-buildtype'), $this->_aPrjConfig["build"]["type"]));
            }
            if (array_key_exists("url", $this->_aPrjConfig["build"])) {
                $sReturn.=t('repository-url') . ': ' . $this->_aPrjConfig["build"]["url"] . '<br>';
            }
            if (array_key_exists("webaccess", $this->_aPrjConfig["build"])) {
                $sReturn.=t('repository-access-browser') . ':<br><a href="' . $this->_aPrjConfig["build"]["webaccess"] . '">' . $this->_aPrjConfig["build"]["webaccess"] . '</a><br>';
            }
            return $sReturn;
        }
    
        /**
         * get html code for a link to the commit
         * (works for guithub and gitlab instances)
         * 
         * @param string $sRevision
         * @return string
         */
        public function _renderRevision($sRevision) {
            $sUrl = str_replace('/tree/master', '', $this->_aPrjConfig["build"]["webaccess"]) . '/commit/' . $sRevision;
            return '<a href="' . $sUrl . '">' . $sRevision . '</a>';
            return $sUrl;
        }
        /**
         * get html form with selectr for remote branches
         * @param string $sActiveBranchname  force active branch name
         * @param bool $bIgnoreCache  flag to ignore exiting cached data
         * @return string
         */
        public function renderSelectRemoteBranches($sActiveBranchname = false, $bIgnoreCache=false) {
            $this->log(__FUNCTION__."(sActiveBranchname = $sActiveBranchname, bIgnoreCache = ".($bIgnoreCache ? 'true' : 'false').") start");
            $aReturn = array();
            $aRadios = array();
            $bFoundActive = false;
            $i = 0;
            if (!$this->_oVcs) {
                $this->_initVcs();
            }
            require_once("formgen.class.php");
            if (!$sActiveBranchname) {
                $sActiveBranchname = $this->_sBranchname;
            }
            if ($this->_oVcs) {
                if (!method_exists($this->_oVcs, "getRemoteBranches")) {
                    // the version control class does not have this method
                    return '';
                }
                foreach ($this->_oVcs->getRemoteBranches($bIgnoreCache) as $aBranch) {
                    $sBranch = $aBranch['name'];
                    $aRadios[$sBranch] = array(
                        'value' => $sBranch,
                        'label' => $aBranch['label'],
                    );
                    // if no param was given the first branch will be marked
                    if (!$sActiveBranchname) {
                        $sActiveBranchname = $sBranch;
                    }
                    if ($sBranch == $sActiveBranchname) {
                        $bFoundActive = true;
                        // $aRadios[$sBranch]['checked'] = 'checked';
                        $aRadios[$sBranch]['selected'] = 'selected';
                    } else {
                        // for SELECT we need the onclick even on select element
                        // not on the option (Chrome)
                        // $aRadios[$sBranch]['onclick'] = 'document.getElementById(\'submitBranch\').click()';
                    }
                };
            }
            // no branches were found
            if (count($aRadios) == 0) {
                return '';
            }
    
            $aForms = array(
                'frmSelectBranch' => array(
                    'meta' => array(
                        'method' => 'POST',
                        'action' => '?',
                        'id' => 'frmSelectBranch',
                    ),
                    'validate' => array(),
                    'form' => array(
                        'branchname' => array(
                            'inline' => true,
                            'type' => 'select',
                            'onchange' => 'document.getElementById(\'submitBranch\').click()',
                            'name' => 'branchname',
                            'label' => '<strong>' . t('branch-select') . '</strong>',
                            'validate' => 'isastring',
                            'options' => $aRadios,
                        ),
                    ),
                ),
            );
    
            // submit to switch branches - only if a selection is available
            if (count($aRadios) > 1 || !$bFoundActive) {
                $aForms['frmSelectBranch']['form']['submitBranch'] = array(
                    'type' => 'submit',
                    'name' => 'btnsave',
                    'onclick' => 'showModalMessage(\'' . t('branch-switch') . '\'); ',
                    'label' => t("change"),
                    'value' => $this->_oHtml->getIcon('sign-ok').t("change"),
                );
            }
    
            $oFrm = new formgen($aForms);
            return $oFrm->renderHtml('frmSelectBranch')
                    . '<script>$("#submitBranch").hide();</script>';
            // return $oFrm->renderHtmlElement('dummy',$aFormData);
        }
    
        /**
         * return html code for a list of all built packages and their usage
         * @return string
         */
        public function renderVersionUsage() {
            $sReturn = false;
            $sRowHead1 = false;
            $sRowHead2 = '<td></td>';
    
            $aAllVersions = $this->_getVersionUsage();
            if (!count($aAllVersions)) {
                return $this->_oHtml->getBox("info", t('class-project-info-no-package'));
            }
    
            foreach ($this->getActivePhases() as $sPhase) {
                $sRowHead1.='<th class="' . $sPhase . ' tdphase" colspan="' . (count($this->_aPlaces) + 1) . '">' . $sPhase . '</th>';
                $sRowHead2.='<td></td>' . $this->renderPlacesAsTd($sPhase);
            }
            
            krsort($aAllVersions);
            foreach ($aAllVersions as $sVersion => $aData) {
                $sReturn.='<tr>';
    
                $sInfos = $this->renderInfoLink($aData["info"], array('hpos' => 'left'));
                $sReturn.='<td>'
                            . $this->_getChecksumDiv(
                                $aData['info']['revision'],
                                $this->_oHtml->getIconByType('calendar') . t('build-from') . ': ' . $sVersion .'<br>'
                                    . $this->_oHtml->getIconByType('branch') . t('branch') . ': ' . $aData['info']["branch"] . '<br>'
                                    . $this->_oHtml->getIconByType('revision') . t('revision') . ': ' . $this->_renderRevision($aData['info']["revision"]) . '<br>'
                              )
                        . '</td><td>'
                        . '&nbsp;&nbsp;' . $sInfos . '&nbsp;&nbsp;'
                        . '</td>'
                        ;
    
                foreach ($this->getActivePhases() as $sPhase) {
                    $sTLine = '';
                    $bCanRollback = $aData["rollback"][$sPhase];
    
                    // $sReturn.=$aData["rollback"][$sPhase] ? '<td>'.$this->renderLink("rollback", $sPhase, $sVersion).'</td>' : '<td>Rollback NOT possible</td>';
                    // $sReturn.=$aData["rollback"][$sPhase] ? '<td> Y </td>' : '<td> N </td>';
                    $sReturn.='<td>  </td>';
    
                    foreach (array_keys($this->_aPlaces) as $sPlace) {
                        $bFound = false;
                        $sReturn.=$aData["usage"][$sPhase][$sPlace] 
                                ? '<td class="' . $sPhase . '" style="text-align: center;">'
                                . $this->_getChecksumDiv($aData['info']['revision'], 'X')
                                . '</td>' 
                                : '<td> </td>'
                            ;
                    }
                }
                $sReturn.='</tr>';
            }
    
            $sReturn = t('class-project-info-table-packages') . '<br><br>'
                    . '<table>'
                    . '<thead><tr><td>Version</td><td></td>'
                    . $sRowHead1
                    . '</tr><tr><td>'
                    . $sRowHead2
                    . '</tr></thead>'
                    . '<tbody>'
                    . $sReturn
                    . '</tbody>'
                    . '</table>';
            return $sReturn;
        }
    
        /**
         * render graphical overview of process (in project overview)
         * @return string
         */
        public function renderVisual() {
            $sReturn = '';
    
            $renderAdminLTE=new renderadminlte();
    
            $sBarHeightBg = '1.0em';
            $sBarHeight = '0.8em';
            $sContinue = '<span style="font-size: 300%; color:#ace;">&raquo;&raquo;</span><br>';
    
            $aBranches=$this->getRemoteBranches();
            if(!is_array($aBranches)){
                return '<br>'.$this->_oHtml->getBox("error", t("project-setup-incomplete"));
            }
    
            $sRepoBar = '';
            /*
                Speedup:
                
            $aRepodata = $this->getRepoRevision();
            if (array_key_exists("revision", $aRepodata)) {
                $sRepoBar = $this->_getChecksumDiv($aRepodata["revision"]);
            } else {
                $sRepoBar = '<span class="error">' . t("error") . '</span>';
            }
            */
    
            $sPackagebar = '';
            $aVersions = $this->_getVersionUsage();
            foreach ($aVersions as $sVersion => $aData) {
                $sBar = $aData["info"]["revision"] ? $this->_getChecksumDiv($aData["info"]["revision"], '' , $sBarHeight) : '';
                $sPackagebar.='<span title="' . $sVersion . '" style="float: left; background:#eee; height: '.$sBarHeightBg.'; width:' . (100 / count($aVersions)) . '%">' . $sBar . '&nbsp;</span>';
            }
    
            $sPhaseImg = '';
            $sLastPhase = '';
            foreach ($this->getActivePhases() as $sPhase) {
                if ($sPhaseImg) {
                    $sAction = $sContinue;
                    if ($this->canAcceptPhase($sLastPhase)) {
                        $sAction .= $this->renderLink("accept", $sLastPhase);
                    }
                    $sPhaseImg.='<div class="action">' . $sAction . '</div>';
                }
                $sLastPhase = $sPhase;
    
                $sFullbar = '';
                foreach (array_keys($this->_aPlaces) as $sPlace) {
                    $sFullbar.='<span title="' . $this->_aPlaces[$sPlace] . '" style="float: left; background:#eee; height: '.$sBarHeightBg.'; width:' . (100 / count($this->_aPlaces)) . '%">' . $this->_renderBar($sPhase, $sPlace, $sBarHeight) . '&nbsp;</span>';
                }
                // $sDetail = $sFullbar . '<br><a href="#h3phases" class="scroll-link">' . $sPhase . '</a>';
                $sDetail = $sFullbar . '<br>' . $sPhase;
    
                $sPhaseImg.='
                <div class="process">
                    <div class="details">' . $sDetail . ' </div>
                </div>';
            }
    
            $sReturn .= ''
                . '<div class="visualprocess">'
                . $renderAdminLTE->addRow(
                    ''
                    . $renderAdminLTE->addCol(
                        $renderAdminLTE->getCard([
                            'type'=>'',
                            'variant'=>'outline',
                            'title'=>'',
                            'text'=> '<h3>'.t("versioncontrol"). '</h3>' . $sRepoBar . '<br>
                                ' . t("repositoryinfos") . '<br>
                                ' // . $this->_aPrjConfig["build"]["type"] 
                                . preg_replace('/.*\@(.*):.*/', '$1', $this->_aPrjConfig["build"]["url"])
                                . '<br>(<strong title="' . t('branch-select') . '">' . count($aBranches) . '</strong>)',
                            ]
                        ), 2
                        )
                    . $renderAdminLTE->addCol(
                        '<div class="action">' . $sContinue . t("build-hint-overview") . '<br><br>' . ($this->canAcceptPhase() ? $this->renderLink("build") : '') . '</div>',
                        1
                    )
                    . $renderAdminLTE->addCol(
                        $renderAdminLTE->getCard([
                            'type'=>'',
                            'variant'=>'outline',
                            'title'=>'',
                            'text'=> '<h3>'.$this->_oHtml->getIcon('package') . t("archive"). '</h3>' 
                                . '<div class="archive">'
                                    . '<div class="details">'
                                    . $sPackagebar . '<br>' 
                                    . '</div>'
                                    . t("packages") . '<br>'
                                    .'(<strong>' . count($this->_getVersionUsage()) . '</strong>)'
                                . '</div>'
                            ]
                        ), 2
                        )
                    . $renderAdminLTE->addCol(
                        '<div class="action">'.$sContinue . sprintf(t("queue-hint-overview"), $this->getNextPhase()).'</div>',
                        1
                    )
                    . $renderAdminLTE->addCol(
                        $renderAdminLTE->getCard([
                            'type'=>'',
                            'variant'=>'outline',
                            'title'=>'',
                            'text'=> '<h3>'.$this->_oHtml->getIcon('phase') . t("phases"). '</h3>' 
                                . ($sPhaseImg ? $sPhaseImg : '<div class="process">' . t("none") . '</div>')
                            ]
                        ), 6
                        )
                    )
                    .'</div>'
                ;
    
            return $sReturn;
        }
    
    }