<?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) . "»"; } $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="hello world"', ), '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>' . ' ' . $sInfos . ' ' . '</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;">»»</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 . ' </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) . ' </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; } }