New concept in the XML: HighlightScale to avoid overloading GetIcon and GetHilightClass...

SVN:trunk[3291]
This commit is contained in:
Denis Flaven
2014-07-28 15:16:16 +00:00
parent 357ae4abb1
commit fd9008a163
11 changed files with 604 additions and 581 deletions

View File

@@ -3717,7 +3717,7 @@ class AttributeStopWatch extends AttributeDefinition
);
$aThresholds = array();
foreach ($this->ListThresholds() as $iThreshold => $aFoo)
foreach ($this->ListThresholds() as $iThreshold => $aDefinition)
{
$sThPrefix = '_'.$iThreshold;
$value->DefineThreshold(
@@ -3725,7 +3725,8 @@ class AttributeStopWatch extends AttributeDefinition
self::DateToSeconds($aCols[$sPrefix.$sThPrefix.'_deadline']),
(bool)($aCols[$sPrefix.$sThPrefix.'_passed'] == 1),
(bool)($aCols[$sPrefix.$sThPrefix.'_triggered'] == 1),
$aCols[$sPrefix.$sThPrefix.'_overrun']
$aCols[$sPrefix.$sThPrefix.'_overrun'],
array_key_exists('highlight', $aDefinition) ? $aDefinition['highlight'] : null
);
}

View File

@@ -95,6 +95,7 @@ abstract class DBObject implements iDisplay
private $m_aLoadedAtt = array(); // Compound objects can be partially loaded, array of sAttCode
protected $m_aModifiedAtt = array(); // list of (potentially) modified sAttCodes
protected $m_oMasterReplicaSet = null; // Set of SynchroReplica related to this object
protected $m_sHighlightCode = null;
// Use the MetaModel::NewObject to build an object (do we have to force it?)
public function __construct($aRow = null, $sClassAlias = '', $aAttToLoad = null, $aExtendedDataSpec = null)
@@ -557,6 +558,64 @@ abstract class DBObject implements iDisplay
{
return $this->m_aExtendedData;
}
/**
* Set the HighlightCode if the given code has a greater rank than the current HilightCode
* @param string $sCode
* @return void
*/
protected function SetHighlightCode($sCode)
{
$aHighlightScale = MetaModel::GetHighlightScale(get_class($this));
$fCurrentRank = 0.0;
if (($this->m_sHighlightCode !== null) && array_key_exists($this->m_sHighlightCode, $aHighlightScale))
{
$fCurrentRank = $aHighlightScale[$this->m_sHighlightCode]['rank'];
}
if (array_key_exists($sCode, $aHighlightScale))
{
$fRank = $aHighlightScale[$sCode]['rank'];
if ($fRank > $fCurrentRank)
{
$this->m_sHighlightCode = $sCode;
}
}
}
/**
* Get the current HighlightCode
* @return string The Hightlight code (null if none set, meaning rank = 0)
*/
protected function GetHighlightCode()
{
return $this->m_sHighlightCode;
}
protected function ComputeHighlightCode()
{
// First if the state defines a HiglightCode, apply it
$sState = $this->GetState();
if ($sState != '')
{
$sCode = MetaModel::GetHighlightCode(get_class($this), $sState);
$this->SetHighlightCode($sCode);
}
// The check for each StopWatch if a HighlightCode is effective
foreach(MetaModel::ListAttributeDefs(get_class($this)) as $sAttCode => $oAttDef)
{
if ($oAttDef instanceof AttributeStopWatch)
{
$oStopWatch = $this->Get($sAttCode);
$sCode = $oStopWatch->GetHighlightCode();
if ($sCode !== '')
{
$this->SetHighlightCode($sCode);
}
}
}
return $this->GetHighlightCode();
}
/**
* Updates the value of an external field by (re)loading the object
@@ -785,10 +844,27 @@ abstract class DBObject implements iDisplay
/**
* Get the icon representing this object
* @param boolean $bImgTag If true the result is a full IMG tag (or an emtpy string if no icon is defined)
* @return string Either the full IMG tag ($bImgTag == true) or just the path to the icon file
* @return string Either the full IMG tag ($bImgTag == true) or just the URL to the icon file
*/
public function GetIcon($bImgTag = true)
{
$sCode = $this->ComputeHighlightCode();
if($sCode != '')
{
$aHighlightScale = MetaModel::GetHighlightScale(get_class($this));
if (array_key_exists($sCode, $aHighlightScale))
{
$sIconUrl = $aHighlightScale[$sCode]['icon'];
if($bImgTag)
{
return "<img src=\"$sIconUrl\" style=\"vertical-align:middle\"/>";
}
else
{
return $sIconUrl;
}
}
}
return MetaModel::GetClassIcon(get_class($this), $bImgTag);
}
@@ -1998,17 +2074,55 @@ abstract class DBObject implements iDisplay
// array('target_state'=>..., 'actions'=>array of handlers procs, 'user_restriction'=>TBD
$bSuccess = true;
foreach ($aTransitionDef['actions'] as $sActionHandler)
foreach ($aTransitionDef['actions'] as $actionHandler)
{
// std PHP spec
$aActionCallSpec = array($this, $sActionHandler);
if (!is_callable($aActionCallSpec))
if (is_string($actionHandler))
{
throw new CoreException("Unable to call action: ".get_class($this)."::$sActionHandler");
return;
// Old (pre-2.0.4) action definition without any parameter
$aActionCallSpec = array($this, $sActionHandler);
if (!is_callable($aActionCallSpec))
{
throw new CoreException("Unable to call action: ".get_class($this)."::$sActionHandler");
return;
}
$bRet = call_user_func($aActionCallSpec, $sStimulusCode);
}
else // if (is_array($actionHandler))
{
// New syntax: 'verb' and typed parameters
$sAction = $actionHandler['verb'];
$aParams = array();
foreach($actionHandler['params'] as $aDefinition)
{
$sParamType = array_key_exists('type', $aDefinition) ? $aDefinition['type'] : 'string';
switch($sParamType)
{
case 'int':
$value = (int)$aDefinition['value'];
break;
case 'float':
$value = (float)$aDefinition['value'];
break;
case 'bool':
$value = (bool)$aDefinition['value'];
break;
case 'reference':
$value = ${$aDefinition['value']};
break;
case 'string':
default:
$value = (string)$aDefinition['value'];
}
$aParams[] = $value;
}
$aCallSpec = array($this, $sAction);
$bRet = call_user_func_array($aCallSpec, $aParams);
}
$bRet = call_user_func($aActionCallSpec, $sStimulusCode);
// if one call fails, the whole is considered as failed
if (!$bRet) $bSuccess = false;
}
@@ -2563,6 +2677,15 @@ abstract class DBObject implements iDisplay
public function GetHilightClass()
{
$sCode = $this->ComputeHighlightCode();
if($sCode != '')
{
$aHighlightScale = MetaModel::GetHighlightScale(get_class($this));
if (array_key_exists($sCode, $aHighlightScale))
{
return $aHighlightScale[$sCode]['color'];
}
}
return HILIGHT_CLASS_NONE;
}

View File

@@ -279,6 +279,7 @@ abstract class MetaModel
private static $m_aChildClasses = array(); // array of ("classname" => array of "childclass")
private static $m_aClassParams = array(); // array of ("classname" => array of class information)
private static $m_aHighlightScales = array(); // array of ("classname" => array of highlightscale information)
static public function GetParentPersistentClass($sRefClass)
{
@@ -1908,6 +1909,50 @@ abstract class MetaModel
self::$m_aTransitions[$sTargetClass][$sStateCode] = array();
}
public static function Init_DefineHighlightScale($aHighlightScale)
{
$sTargetClass = self::GetCallersPHPClass("Init");
self::$m_aHighlightScales[$sTargetClass] = $aHighlightScale;
}
public static function GetHighlightScale($sTargetClass)
{
$aScale = array();
$aParentScale = array();
$sParentClass = self::GetParentPersistentClass($sTargetClass);
if (!empty($sParentClass))
{
// inherit the scale from the parent class
$aParentScale = self::GetHighlightScale($sParentClass);
}
if (array_key_exists($sTargetClass, self::$m_aHighlightScales))
{
$aScale = self::$m_aHighlightScales[$sTargetClass];
}
return array_merge($aParentScale, $aScale); // Merge both arrays, the values from the last one have precedence
}
public static function GetHighlightCode($sTargetClass, $sStateCode)
{
$sCode = '';
if ( array_key_exists($sTargetClass, self::$m_aStates)
&& array_key_exists($sStateCode, self::$m_aStates[$sTargetClass])
&& array_key_exists('highlight', self::$m_aStates[$sTargetClass][$sStateCode]) )
{
$sCode = self::$m_aStates[$sTargetClass][$sStateCode]['highlight']['code'];
}
else
{
// Check the parent's definition
$sParentClass = self::GetParentPersistentClass($sTargetClass);
if (!empty($sParentClass))
{
$sCode = self::GetHighlightCode($sParentClass, $sStateCode);
}
}
return $sCode;
}
public static function Init_OverloadStateAttribute($sStateCode, $sAttCode, $iFlags)
{
// Warning: this is not sufficient: the flags have to be copied to the states that are inheriting from this state
@@ -3386,6 +3431,33 @@ abstract class MetaModel
{
// Do nothing...
}
else if ($oAttDef instanceof AttributeStopWatch)
{
$aThresholds = $oAttDef->ListThresholds();
if (is_array($aThresholds))
{
foreach($aThresholds as $iPercent => $aDef)
{
if (array_key_exists('highlight', $aDef))
{
if(!array_key_exists('code', $aDef['highlight']))
{
$aErrors[$sClass][] = "The 'code' element is missing for the 'highlight' property of the $iPercent% threshold in the attribute: '$sAttCode'.";
$aSugFix[$sClass][] = "Add a 'code' entry specifying the value of the highlight code for this threshold.";
}
else
{
$aScale = self::GetHighlightScale($sClass);
if (!array_key_exists($aDef['highlight']['code'], $aScale))
{
$aErrors[$sClass][] = "'{$aDef['highlight']['code']}' is not a valid value for the 'code' element of the $iPercent% threshold in the attribute: '$sAttCode'.";
$aSugFix[$sClass][] = "The possible highlight codes for this class are: ".implode(', ', array_keys($aScale)).".";
}
}
}
}
}
}
else // standard attributes
{
// Check that the default values definition is a valid object!
@@ -3483,17 +3555,46 @@ abstract class MetaModel
}
}
// Lifcycle - check that the action handlers are defined
// Lifecycle - check that the action handlers are defined
foreach (self::EnumStates($sClass) as $sStateCode => $aStateDef)
{
foreach(self::EnumTransitions($sClass, $sStateCode) as $sStimulusCode => $aTransitionDef)
{
foreach ($aTransitionDef['actions'] as $sActionHandler)
foreach ($aTransitionDef['actions'] as $actionHandler)
{
if (!method_exists($sClass, $sActionHandler))
if (is_string($actionHandler))
{
$aErrors[$sClass][] = "Unknown function '$sActionHandler' in transition [$sStateCode/$sStimulusCode] for state attribute '$sStateAttCode'";
$aSugFix[$sClass][] = "Specify a function which prototype is in the form [public function $sActionHandler(\$sStimulusCode){return true;}]";
if (!method_exists($sClass, $actionHandler))
{
$aErrors[$sClass][] = "Unknown function '$sActionHandler' in transition [$sStateCode/$sStimulusCode] for state attribute '$sStateAttCode'";
$aSugFix[$sClass][] = "Specify a function which prototype is in the form [public function $sActionHandler(\$sStimulusCode){return true;}]";
}
}
else // if(is_array($actionHandler))
{
$sActionHandler = $actionHandler['verb'];
if (!method_exists($sClass, $sActionHandler))
{
$aErrors[$sClass][] = "Unknown function '$sActionHandler' in transition [$sStateCode/$sStimulusCode] for state attribute '$sStateAttCode'";
$aSugFix[$sClass][] = "Specify a function which prototype is in the form [public function $sActionHandler(...){return true;}]";
}
}
}
}
if (array_key_exists('highlight', $aStateDef))
{
if(!array_key_exists('code', $aStateDef['highlight']))
{
$aErrors[$sClass][] = "The 'code' element is missing for the 'highlight' property of state: '$sStateCode'.";
$aSugFix[$sClass][] = "Add a 'code' entry specifying the value of the highlight code for this state.";
}
else
{
$aScale = self::GetHighlightScale($sClass);
if (!array_key_exists($aStateDef['highlight']['code'], $aScale))
{
$aErrors[$sClass][] = "'{$aStateDef['highlight']['code']}' is not a valid value for the 'code' element in the 'highlight' property of state: '$sStateCode'.";
$aSugFix[$sClass][] = "The possible highlight codes for this class are: ".implode(', ', array_keys($aScale)).".";
}
}
}
@@ -4718,6 +4819,7 @@ abstract class MetaModel
self::$m_aStates = $result['m_aStates'];
self::$m_aStimuli = $result['m_aStimuli'];
self::$m_aTransitions = $result['m_aTransitions'];
self::$m_aHighlightScales = $result['m_aHighlightScales'];
}
$oKPI->ComputeAndReport('Metamodel APC (fetch + read)');
}
@@ -4754,6 +4856,7 @@ abstract class MetaModel
$aCache['m_aStates'] = self::$m_aStates; // array of ("classname" => array of "statecode"=>array('label'=>..., attribute_inherit=> attribute_list=>...))
$aCache['m_aStimuli'] = self::$m_aStimuli; // array of ("classname" => array of ("stimuluscode"=>array('label'=>...)))
$aCache['m_aTransitions'] = self::$m_aTransitions; // array of ("classname" => array of ("statcode_from"=>array of ("stimuluscode" => array('target_state'=>..., 'actions'=>array of handlers procs, 'user_restriction'=>TBD)))
$aCache['m_aHighlightScales'] = self::$m_aHighlightScales; // array of ("classname" => array of higlightcodes)))
apc_store($sOqlAPCCacheId, $aCache);
$oKPI->ComputeAndReport('Metamodel APC (store)');
}

View File

@@ -62,12 +62,13 @@ class ormStopWatch
return (string) $this->iTimeSpent;
}
public function DefineThreshold($iPercent, $tDeadline = null, $bPassed = false, $bTriggered = false, $iOverrun = null)
public function DefineThreshold($iPercent, $tDeadline = null, $bPassed = false, $bTriggered = false, $iOverrun = null, $aHighlightDef = null)
{
$this->aThresholds[$iPercent] = array(
'deadline' => $tDeadline, // unix time (seconds)
'triggered' => $bTriggered,
'overrun' => $iOverrun
'overrun' => $iOverrun,
'highlight' => $aHighlightDef, // array('code' => string, 'persistent' => boolean)
);
}
@@ -143,6 +144,30 @@ class ormStopWatch
return false;
}
}
public function GetHighlightCode()
{
$sCode = '';
// Process the thresholds in ascending order
$aPercents = array();
foreach($this->aThresholds as $iPercent => $aDefs)
{
$aPercents[] = $iPercent;
}
sort($aPercents, SORT_NUMERIC);
foreach($aPercents as $iPercent)
{
$aDefs = $this->aThresholds[$iPercent];
if (array_key_exists('highlight', $aDefs) && is_array($aDefs['highlight']) && $this->IsThresholdPassed($iPercent))
{
if (($aDefs['highlight']['persistent'] == true) || (($aDefs['highlight']['persistent'] == false) && !is_null($this->iLastStart)))
{
$sCode = $aDefs['highlight']['code'];
}
}
}
return $sCode;
}
public function GetAsHTML($oAttDef, $oHostObject = null)
{
@@ -424,9 +449,44 @@ class CheckStopWatchThresholds implements iBackgroundProcess
{
$sVerb = $aActionData['verb'];
$aParams = $aActionData['params'];
$sParams = implode(', ', $aParams);
$aValues = array();
foreach($aParams as $def)
{
if (is_string($def))
{
// Old method (pre-2.0.4) non typed parameters
$aValues[] = $def;
}
else // if(is_array($def))
{
$sParamType = array_key_exists('type', $def) ? $def['type'] : 'string';
switch($sParamType)
{
case 'int':
$value = (int)$def['value'];
break;
case 'float':
$value = (float)$def['value'];
break;
case 'bool':
$value = (bool)$def['value'];
break;
case 'reference':
$value = ${$def['value']};
break;
case 'string':
default:
$value = (string)$def['value'];
}
$aValues[] = $value;
}
}
$aCallSpec = array($oObj, $sVerb);
call_user_func_array($aCallSpec, $aParams);
call_user_func_array($aCallSpec, $aValues);
}
// Mark the threshold as "triggered"