rollout_base.class.php 19.21 KiB
<?php
require_once 'rollout.interface.php';
/**
* rollout_base class that will beextended in a rollout plugin
*
*
* @author axel
*/
class rollout_base implements iRolloutplugin{
// ---------------------------------------------------------------
// VARIABLES
// ---------------------------------------------------------------
/**
* identifier for current plugin; it us used to find the current plugin
* settings in the config structore for global and project based config
* @var string
*/
protected $_sPluginId='UNSET';
/**
* data with plugin infos (read from info.json)
* @var array
*/
protected $_aPlugininfos=false;
/**
* array with translation texts
* @var type
*/
protected $_aLang=false;
/**
* set language; 2 letter code, i.e. "de"; default language is "en" ; a
* file "lang_en.json" is required in the plugin dir
* @var string
*/
protected $_sLang = 'en';
/**
* string with phase of project; one of preview|stage|live
* @var type
*/
protected $_sPhase = false;
/**
* global configuration of the rollout plugin
* @var array
*/
protected $_aCfgGlobal = false;
/**
* configuration of the project
* @var array
*/
protected $_aCfgProject = false;
protected $_sNamePrefix4Project=false; // set in constructor
protected $_sNamePrefix4Phase=false; // set in constructor
// ---------------------------------------------------------------
// CONSTRUCTOR
// ---------------------------------------------------------------
/**
* initialize rollout plugin
* @param array $aParams hash with those possible keys
* lang string language, i.e. 'de'
* phase string name of phase in a project
* globalcfg array given global config $aConfig
* projectcfg array project config to generate config
* for project and all phases
* @return boolean
*/
public function __construct($aParams) {
// set current plugin id - taken from plugin directory name above
$oReflection=new ReflectionClass($this);
$this->_sPluginId=basename(dirname($oReflection->getFileName()));
// ----- init language
if (isset($aParams['lang'])){
$this->setLang($aParams['lang']);
} else {
$this->setLang();
}
// ----- init phase
if (isset($aParams['phase'])){
$this->setPhase($aParams['phase']);
}
// ----- init global config
if (isset($aParams['globalcfg'])){
$this->setGlobalConfig($aParams['globalcfg']);
}
// ----- init project config
if (isset($aParams['projectcfg'])){
$this->setProjectConfig($aParams['projectcfg']);
}
return true;
}
// ---------------------------------------------------------------
// FORM HELPER
// ---------------------------------------------------------------
/**
* get a string for a prefix for name attribute in form vars.
* It is important to store the value in the wanted structure.
*
* @param type $sPhase
* @return type
*/
protected function _getNamePrefix($sPhase=false){
return ($sPhase
? 'phases['.$sPhase.'][plugins][rollout]['.$this->getId().']'
: 'plugins[rollout]['.$this->getId().']'
);
}
/**
* render a form by given form elementes
* @param array $aFormdata array of form elements
* @param string $sKey part of the identifier used in id of the input field
* @return string
*/
protected function _renderForm($aFormdata, $sKey){
static $i;
if (!isset($i)){
$i=0;
}
$sReturn='';
$sKeyPrefix=$this->getId().'_'.$sKey;
$oForm = new formgen($aForms);
foreach ($aFormdata as $elementData) {
$elementKey=$sKeyPrefix.'_'.$i++;
$sReturn.=$oForm->renderHtmlElement($elementKey, $elementData);
}
return $sReturn;
}
/**
* render form fields for global plugin variables
* @param string $sScope scope of vars ... one of global|project|phase
* @param string $sPhase optional: render global vars in a phase; if no phase was set it renders form fields for project based settings
* @return string
*/
protected function _renderForm4Vars($sScope, $sPhase=false){
$sReturn='';
// test vars from info.json file
$aInfos=$this->getPluginInfos();
if(!isset($aInfos['vars'][$sScope]) || !count($aInfos['vars'][$sScope])){
return '';
}
$sKey=($sPhase ? 'phase_'.$sPhase : 'project');
$sPrefixName=$this->_getNamePrefix($sPhase);
// set defaults - to be used in placeholder attribute
// no phase = project level - take global defaults of ci config
// on a phase - fetch merged cofig data of project
$aDefaultValues=($sPhase ? $this->getConfig() : $this->_aCfgGlobal);
$aDefaultSource=($sPhase ? 'project' : 'global');
// set defaults - to be used in value attribute
$aValues=($sPhase
? $this->_aCfgProject['phases'][$sPhase]['plugins']['rollout'][$this->getId()]
: $this->_aCfgProject['plugins']['rollout'][$this->getId()]
);
// create form fields
// $aFormdata[]=array('type' => 'markup','value' => '<br>'.$this->_t('section-override-'.$sScope.'-vars').':');
$aFormdata[]=array('type' => 'markup','value' => '<div style="style: clear: left;"></div><h4>'.$this->getId() .' :: '. $sScope.'</h4>');
$sMiss='';
foreach ($aInfos['vars'][$sScope] as $sVarname=>$aVarinfos){
if ($sScope==='global' && !isset($this->_aCfgGlobal[$sVarname])){
$sMiss.='- plugin var was not set in global CI config: "'.$sVarname.'".<br>';
}
$sMyPlaceholder=(isset($aDefaultValues[$sVarname])
? htmlentities($aDefaultValues[$sVarname])
: (isset($aVarinfos['default']) ? $aVarinfos['default'] : 'N.A.')
);
switch ($aVarinfos['type']) {
case "password":
$sMyPlaceholder=(isset($aDefaultValues[$sVarname])
? '******************************'
: $sMyPlaceholder
);
$aFormdata[]=array(
'type' => $aVarinfos['type'],
'name' => $sPrefixName.'['.$sVarname.']',
'label' => $this->_t($sVarname.'-label'),
'title' => $this->_t($sVarname.'-hint'),
'value' => (isset($aValues[$sVarname]) ? htmlentities($aValues[$sVarname]) : ''),
// 'required' => 'required',
'validate' => 'isastring',
// 'size' => 25,
// 'placeholder' => (isset($this->_aCfgGlobal[$sVarname]) ? $this->_aCfgGlobal[$sVarname] : '') . ' | '.$aDefaultValues[$sVarname],
'placeholder' => $sMyPlaceholder
);
break;
case "text":
$aFormdata[]=array(
'type' => $aVarinfos['type'],
'name' => $sPrefixName.'['.$sVarname.']',
'label' => $this->_t($sVarname.'-label'),
'ondblclick' => ($aDefaultValues[$sVarname] ? 'if (this.value==\'\') { this.value=\''.$aDefaultValues[$sVarname].'\' }' : ''),
'onfocusout' => ($aDefaultValues[$sVarname] ? 'if (this.value==\''.$aDefaultValues[$sVarname].'\') { this.value=\'\' }' : ''),
'title' => htmlentities($this->_t($sVarname.'-hint')."\n"
. ($this->_aCfgGlobal[$sVarname] ? '- global: '.$this->_aCfgGlobal[$sVarname]."\n" : '')
. ($this->_aCfgProject['plugins']['rollout'][$this->getId()][$sVarname] ? '- project: '.$this->_aCfgProject['plugins']['rollout'][$this->getId()][$sVarname]."\n" : '')
)
,
'value' => (isset($aValues[$sVarname]) ? htmlentities($aValues[$sVarname]) : ''),
// 'required' => 'required',
'validate' => 'isastring',
// 'size' => 25,
// 'placeholder' => (isset($this->_aCfgGlobal[$sVarname]) ? $this->_aCfgGlobal[$sVarname] : '') . ' | '.$aDefaultValues[$sVarname],
'placeholder' => $sMyPlaceholder
);
break;
default:
$sMiss.='- plugin var "'.$sVarname.'" was not rendered - its type "'.$aVarinfos['type'].'" is not supported in the general form renderer.<br>';
break;
}
}
// $aFormdata[]=array('type' => 'markup','value' => '<div style="style: clear: left;"></div><br><br>');
return $this->_renderForm($aFormdata, $sKey)
. ($sMiss
? '<pre>WARNINGS:<br>'.$sMiss.'</pre>' . ($sScope==='global' ? $this -> renderCfgExample() : '' )
: ''
)
;
}
/**
* get a translated text from lang_XX.json in plugin dir;
* If the key is missed it returns "[KEY :: LANG]"
*
* @see setLang()
* @param string $sKey key to find in lang file
* @return string
*/
protected function _t($sKey){
return (isset($this->_aLang[$sKey]) && $this->_aLang[$sKey])
? $this->_aLang[$sKey]
: "[ $sKey :: $this->_sLang ]"
;
}
// ---------------------------------------------------------------
// PUBLIC METHODS
// ---------------------------------------------------------------
/**
* set language for output of formdata and other texts.
* This method loads the language file into a hash. The output of
* translated texts can be done with $this->_t("your_key")
*
* @see _t()
* @param string $sLang language code, i.e. "de"
* @return boolean
*/
public function setLang($sLang=false){
$this->_sLang=$sLang ? $sLang : $this->_sLang;
$oReflection=new ReflectionClass($this);
$sFile=dirname($oReflection->getFileName()) . '/lang_'.$this->_sLang.'.json';
$this->_aLang=(file_exists($sFile)) ? json_decode(file_get_contents($sFile), 1) : $this->_aLang;
return true;
}
/**
* set a phase for automatic use GETTER methods
*/
public function setPhase($sPhase){
$this->_sPhase=$sPhase;
return true;
}
// ----------------------------------------------------------------------
// INTERFACE :: CHECKS
// ----------------------------------------------------------------------
/**
* check requirements if the plugin could work
*/
public function checkRequirements(){
// no specific checks needed ... always true
return true;
}
/**
* check access to a deploy target
*/
public function checkConnectionToTarget(){
// do nothing ... always true
return true;
}
// ----------------------------------------------------------------------
// INTERFACE :: SETTER
// ----------------------------------------------------------------------
/**
* set Config ... by given global config of the current plugin
* @param array $aConfigArray
*/
public function setGlobalConfig($aConfigArray){
return $this->_aCfgGlobal=$aConfigArray;
}
/**
* set Config ... by given project config
*/
public function setProjectConfig($aProjectConfigArray){
$this->_aCfgProject=$aProjectConfigArray;
// echo '<pre>'.print_r($aProjectConfigArray, 1).'</pre>';
// ----- ensure that the config structure exists
// (it is easier fo handling in getConfig())
if (!isset($this->_aCfgProject['plugins']['rollout'][$this->_sPluginId])){
/*
if (!isset($this->_aCfgProject['plugins']['rollout'])){
if (!isset($this->_aCfgProject['plugins'])){
$this->_aCfgProject['plugins']=array();
}
$this->_aCfgProject['plugins']['rollout']=array();
}
*
*/
$this->_aCfgProject['plugins']['rollout'][$this->_sPluginId]=array('INFO'=>'created');
}
// unset empty project values to get global values
foreach ($this->_aCfgProject['plugins']['rollout'][$this->_sPluginId] as $sVarname=>$value){
if ($value===''){
unset($this->_aCfgProject['plugins']['rollout'][$this->_sPluginId][$sVarname]);
}
}
foreach (array_keys($this->_aCfgProject['phases']) as $sMyPhase){
if (isset($this->_aCfgProject['phases'][$sMyPhase]['plugins']['rollout'][$this->getId()])){
foreach($this->_aCfgProject['phases'][$sMyPhase]['plugins']['rollout'][$this->getId()] as $sVarname=>$value){
if ($value===''){
unset($this->_aCfgProject['phases'][$sMyPhase]['plugins']['rollout'][$this->getId()][$sVarname]);
}
}
}
}
// TODO:
return $this->_aCfgProject;
}
// ----------------------------------------------------------------------
// INTERFACE :: GETTER
// ----------------------------------------------------------------------
/**
* get a hash with the merged config for project or mo specific: of a given
* phase
* @param string $sPhase
* @return array
*/
public function getConfig($sPhase=false){
return ($sPhase && isset($this->_aCfgProject['phases'][$sPhase]['plugins']['rollout'][$this->getId()]))
? array_merge($this->_aCfgGlobal, $this->_aCfgProject['plugins']['rollout'][$this->getId()], $this->_aCfgProject['phases'][$sPhase]['plugins']['rollout'][$this->getId()])
: array_merge($this->_aCfgGlobal, $this->_aCfgProject['plugins']['rollout'][$this->getId()])
;
}
/**
* get an array with shell commands to execute
* @param string $sPhase
* @return array
*/
public function getDeployCommands($sPhase){
return [
'echo "ERROR: The method getDeployCommamds($sPhase) was not implemented in the rollout plugin ['.$this->getId().']"',
'exit 1'
];
}
/**
* get string with current ID
* @return string
*/
public function getId(){
return $this->_sPluginId;
}
/**
* get string with plugin name (taken from plugin language file)
* @return string
*/
public function getName(){
return $this->_t('plugin_name');
}
/**
* get string with plugin description (taken from plugin language file)
* @return string
*/
public function getDescription(){
return $this->_t('description');
}
/**
* get array read from info.json
* @return type
*/
public function getPluginInfos(){
if ($this->_aPlugininfos){
return $this->_aPlugininfos;
}
$oReflection=new ReflectionClass($this);
$sFile=dirname($oReflection->getFileName()) . '/info.json';
$this->_aPlugininfos= (file_exists($sFile))
? json_decode(file_get_contents($sFile), 1)
: array('error'=> 'unable to read info file ['.$sFile.'].')
;
return $this->_aPlugininfos;
}
// ----------------------------------------------------------------------
// INTERFACE :: RENDERER
// ----------------------------------------------------------------------
public function renderCfgExample(){
$sReturn='';
$sPre=' ';
$aInfos=$this->getPluginInfos();
$sReturn.='<pre>$aConfig = array(
...
\'plugins\'=>array(
...
// enabled rollout plugins
\'rollout\'=>array(
...
<strong>
\''.$this->getId().'\'=>array(
// '.$this->getName().'
// '.$this->getDescription().'
'.PHP_EOL;
// add global vars
if(!isset($aInfos['vars']['global']) || !count($aInfos['vars']['global'])){
$sReturn.=$sPre.'// this plugin has no global config vars'.PHP_EOL;
} else {
foreach ($aInfos['vars']['global'] as $sVar=>$aItem){
$sReturn.=$sPre.'// '.$this->_t($sVar.'-hint').PHP_EOL;
$sReturn.=$sPre.'\''.$sVar.'\'=>\''.(isset($this->_aCfgGlobal[$sVar]) ? $this->_aCfgGlobal[$sVar] : $aItem['default']).'\','.PHP_EOL;
$sReturn.=PHP_EOL;
}
}
$sReturn.='
),
</strong>
...
),
...
),
);</pre>';
return $sReturn;
}
protected function _renderMoreToggler($sContent){
$sDivId='rollout-more-toggler-'.$this->getId().'-'.md5($sContent);
return ''
. '<button onclick="$(\'#'.$sDivId.'\').slideToggle(); return false;"> ... </button>'
. '<div id="'.$sDivId.'" style="display: none;">'
. $sContent
. '</div>'
;
}
public function renderFormdata4Project() {
return ''
. $this->_renderForm4Vars('project', false)
. $this->_renderForm4Vars('global', false)
// . $this->_renderFormProjectVars($this->_sNamePrefix4Project, false)
// . '<pre>DEBUG: GLOBAL settings - $this->_aCfgGlobal = ' . print_r($this->_aCfgGlobal, 1) . '</pre>'
// . '<pre>DEBUG: PROJECT settings - $this->getConfig() = ' . print_r($this->getConfig(), 1) . '</pre>'
// .'<pre>DEBUG: $this->_aCfgProject ... plugin = '.print_r($this->_aCfgProject, 1).'</pre>'
;
}
public function renderFormdata4Phase($sPhase){
static $iCounter;
if(!isset($iCounter)){
$iCounter=0;
}
$sDivId='rollout-override-div-'.$this->getId().'-'.$sPhase.'-'.$iCounter;
return ''
. $this->_renderForm4Vars('phase', $sPhase)
. $this->_renderMoreToggler(
$this->_renderForm4Vars('project', $sPhase)
. $this->_renderForm4Vars('global', $sPhase)
)
// . $this->_renderForm($aFormdata, 'project')
// .$sReturn
// . '<pre>DEBUG: GLOBAL settings - $this->_aCfgGlobal = ' . print_r($this->_aCfgGlobal, 1) . '</pre>'
// . '<pre>DEBUG: PROJECT settings - $this->getConfig() = ' . print_r($this->getConfig(), 1) . '</pre>'
// . '<pre>DEBUG: PHASE settings - $this->getConfig("'.$sMyPhase.'") = ' . print_r($this->getConfig($sMyPhase), 1) . '</pre>'
;
}
}