link to a class
*/
function MakeClassHLink($sClass, $sContext)
{
return "".MetaModel::GetName($sClass)." ( ".$sClass." ) ";
}
/**
* Helper for this page -> link to a class
*/
function MakeRelationHLink($sRelCode, $sContext)
{
$sDesc = MetaModel::GetRelationDescription($sRelCode);
return "".$sRelCode." ";
}
/**
* Helper for the global list and the details of a given class
*/
function DisplaySubclasses($oPage, $sClass, $sContext)
{
$aChildClasses = MetaModel::EnumChildClasses($sClass);
if (count($aChildClasses) != 0)
{
$oPage->add("
\n");
$aOrderedClasses = array();
foreach ($aChildClasses as $sClassName)
{
// Skip indirect childs, they will be handled somewhere else
if (MetaModel::GetParentPersistentClass($sClassName) == $sClass)
{
$aOrderedClasses[$sClassName] = MetaModel::GetName($sClassName);
}
}
// Sort on the display name
asort($aOrderedClasses);
foreach ($aOrderedClasses as $sClassName => $sDisplayName)
{
// Skip indirect childs, they will be handled somewhere else
if (MetaModel::GetParentPersistentClass($sClassName) == $sClass)
{
$oPage->add("".MakeClassHLink($sClassName, $sContext)."\n");
DisplaySubclasses($oPage, $sClassName, $sContext);
$oPage->add(" \n");
}
}
$oPage->add(" \n");
}
}
/**
* Helper for the lifecycle details of a given class
*/
function DisplayLifecycle($oPage, $sClass)
{
$sStateAttCode = MetaModel::GetStateAttributeCode($sClass);
if (empty($sStateAttCode))
{
$oPage->p(Dict::S('UI:Schema:NoLifeCyle'));
}
else
{
$aStates = MetaModel::EnumStates($sClass);
$aStimuli = MetaModel::EnumStimuli($sClass);
$oPage->add(" \n");
$oPage->add_ready_script(
<<add("Open All Close All ");
$oPage->add_ready_script(
<<add("".Dict::S('UI:Schema:LifeCycleTransitions')." \n");
$oPage->add("\n");
foreach ($aStates as $sStateCode => $aStateDef)
{
$sStateLabel = MetaModel::GetStateLabel($sClass, $sStateCode);
$sStateDescription = MetaModel::GetStateDescription($sClass, $sStateCode);
$oPage->add("$sStateLabel ( $sStateCode ) $sStateDescription \n");
$oPage->add("\n");
foreach (MetaModel::EnumTransitions($sClass, $sStateCode) as $sStimulusCode => $aTransitionDef)
{
$sStimulusLabel = $aStimuli[$sStimulusCode]->GetLabel();
$sTargetState = $aTransitionDef['target_state'];
$sTargetStateLabel = MetaModel::GetStateLabel($sClass, $sTargetState);
if (count($aTransitionDef['actions']) > 0)
{
$aActionsDesc = array();
foreach ($aTransitionDef['actions'] as $actionHandler)
{
if (is_string($actionHandler))
{
$aActionsDesc[] = $actionHandler;
}
else
{
$aParamsDesc = array();
foreach ($actionHandler['params'] as $aParamData)
{
$aParamsDesc[] = $aParamData['type'].':'.$aParamData['value'];
}
$aActionsDesc[] = $actionHandler['verb'].'('.implode(', ', $aParamsDesc).')';
}
}
$sActions = " (".implode(', ', $aActionsDesc).") ";
}
else
{
$sActions = "";
}
$oPage->add("$sStimulusLabel
( $sStimulusCode )
=>
$sTargetStateLabel ( $sTargetState ) $sActions \n");
}
$oPage->add(" \n");
}
$oPage->add(" \n");
$oPage->add("".Dict::S('UI:Schema:LifeCyleAttributeOptions')." \n");
$oPage->add("\n");
foreach ($aStates as $sStateCode => $aStateDef)
{
$sStateLabel = MetaModel::GetStateLabel($sClass, $sStateCode);
$sStateDescription = MetaModel::GetStateDescription($sClass, $sStateCode);
$oPage->add("$sStateLabel ( $sStateCode ) $sStateDescription \n");
if (count($aStates[$sStateCode]['attribute_list']) > 0)
{
$oPage->add("\n");
foreach ($aStates[$sStateCode]['attribute_list'] as $sAttCode => $iOptions)
{
$oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode);
$sAttLabel = $oAttDef->GetLabel();
$aOptions = array();
if ($iOptions & OPT_ATT_HIDDEN)
{
$aOptions[] = Dict::S('UI:Schema:LifeCycleHiddenAttribute');
}
if ($iOptions & OPT_ATT_READONLY)
{
$aOptions[] = Dict::S('UI:Schema:LifeCycleReadOnlyAttribute');
}
if ($iOptions & OPT_ATT_MANDATORY)
{
$aOptions[] = Dict::S('UI:Schema:LifeCycleMandatoryAttribute');
}
if ($iOptions & OPT_ATT_MUSTCHANGE)
{
$aOptions[] = Dict::S('UI:Schema:LifeCycleAttributeMustChange');
}
if ($iOptions & OPT_ATT_MUSTPROMPT)
{
$aOptions[] = Dict::S('UI:Schema:LifeCycleAttributeMustPrompt');
}
if (count($aOptions))
{
$sOptions = implode(', ', $aOptions);
}
else
{
$sOptions = "";
}
$oPage->add("$sAttLabel $sOptions \n");
}
$oPage->add(" \n");
}
else
{
$oPage->p("".Dict::S('UI:Schema:LifeCycleEmptyList')." ");
}
}
$oPage->add(" \n");
$oPage->add_ready_script('$("#LifeCycleList").treeview({collapsed: true,});');
$oPage->add_ready_script('$("#LifeCycleAttrOptList").treeview({collapsed: true,});');
}
}
/**
* Helper for the trigger
*/
function DisplayTriggers($oPage, $sClass)
{
$sClassList = implode("', '", MetaModel::EnumParentClasses($sClass, ENUM_PARENT_CLASSES_ALL));
$oSet = new CMDBObjectSet(DBObjectSearch::FromOQL("SELECT TriggerOnObject WHERE target_class IN ('$sClassList')"));
cmdbAbstractObject::DisplaySet($oPage, $oSet, array('block_id' => 'triggers'));
}
/**
* Display the list of classes from the business model
*/
function DisplayClassesList($oPage, $sContext)
{
$oPage->add("".Dict::S('UI:Schema:Title')." \n");
$oPage->add("".Dict::S('UI:Schema:ClassFilter')." ");
$oPage->add("
");
$oPage->add("\n");
$oPage->add_ready_script(
<< li').show();
e.parents().show();
});
});
$("#delDataModelSearch").on ('click', function(){
$("#search-model").val("");
$("#search-model").trigger('input');
});
EOF
);
// Get all the "root" classes for display
$aRootClasses = array();
foreach (MetaModel::GetClasses() as $sClassName)
{
if (MetaModel::IsRootClass($sClassName))
{
$aRootClasses[$sClassName] = MetaModel::GetName($sClassName);
}
elseif (MetaModel::IsStandaloneClass($sClassName))
{
$aRootClasses[$sClassName] = MetaModel::GetName($sClassName);
}
$sLabelClassName = MetaModel::GetName($sClassName);
//Fetch classes names for autocomplete purpose
$oPage->add_script(
<< $sDisplayName)
{
if (MetaModel::IsRootClass($sClassName))
{
$oPage->add("".MakeClassHLink($sClassName, $sContext)."\n");
DisplaySubclasses($oPage, $sClassName, $sContext);
$oPage->add(" \n");
}
elseif (MetaModel::IsStandaloneClass($sClassName))
{
$oPage->add("".MakeClassHLink($sClassName, $sContext)." \n");
}
}
$oPage->add(" \n");
$oPage->add_ready_script('$("#ClassesList").treeview();');
}
/**
* Helper for the list of classes related to the given class in a graphical way
*/
function DisplayRelatedClassesGraph($oPage, $sClass)
{
try
{
$bOnTheLeft = true;
$bSkipLinkingClasses = false;
// 1) Fetching referencing classes data
//
$aData = array();
$aOrigins = array('_' => true);
$aRefs = MetaModel::EnumReferencingClasses($sClass, $bSkipLinkingClasses);
$sSelfReference = "false";
if (count($aRefs) != 0)
{
foreach ($aRefs as $sRemoteClass => $aRemoteKeys)
{
foreach ($aRemoteKeys as $sExtKeyAttCode => $oExtKeyAttDef)
{
if ($sRemoteClass != $sClass)
{
// ref_prefix to avoid collision between attributes labels that refer to this class and local attributes label that references other classes
$aAttribute = array('label' => 'ref_'.$sExtKeyAttCode);
// Test if a distant attribut exists and if it uses a link class
if (!($oExtKeyAttDef->GetMirrorLinkAttribute() == null ? false : $oExtKeyAttDef->GetMirrorLinkAttribute() instanceof AttributeLinkedSetIndirect))
{
$aAttribute['related'] = $sRemoteClass;
$aAttribute['related_icon'] = MetaModel::GetClassIcon($aAttribute['related'], false);
$aAttribute['related_position'] = $bOnTheLeft ? -1 : 1;
$aAttribute['relation_type'] = 0;
$bOnTheLeft = !$bOnTheLeft; // Toggle the side
$sOrigin = MetaModel::GetAttributeOrigin($sRemoteClass, $sExtKeyAttCode);
$aAttribute['origin'] = $sOrigin;
$aOrigins[$sOrigin] = true;
$aData[$sExtKeyAttCode.$sRemoteClass] = $aAttribute;
}
}
}
}
}
// 2) Fetching referenced classes data
//
$aDataRef = array(
array(
'label' => $sClass,
'icon' => MetaModel::GetClassIcon($sClass, false),
'origin_index' => 0,
'alphabetical_index' => 0,
'origin' => '_',
),
);
$bOnTheLeft = true;
$aOriginsRef = array('_' => true);
foreach (MetaModel::ListAttributeDefs($sClass) as $sAttCode => $oAttDef)
{
$aAttribute = array('label' => $sAttCode);
if ($oAttDef->IsLinkSet())
{
if ($oAttDef->IsIndirect())
{
$sRemoteAttDef = $oAttDef->GetExtKeyToRemote();
$aAttribute['related'] = MetaModel::GetAttributeDef($oAttDef->GetLinkedClass(), $sRemoteAttDef)->GetTargetClass();
$aAttribute['related_icon'] = MetaModel::GetClassIcon($aAttribute['related'], false);
$aAttribute['related_position'] = $bOnTheLeft ? 1 : -1;
$aAttribute['relation_type'] = 0; //
$aAttribute['tooltip_data']['class'] = $oAttDef->GetLinkedClass();
$aAttribute['tooltip_data']['to_remote'] = $sRemoteAttDef;
$aAttribute['tooltip_data']['to_me'] = $oAttDef->GetExtKeyToMe();
$bOnTheLeft = !$bOnTheLeft; // Toggle the side
}
else
{
$aAttribute['related'] = $oAttDef->GetLinkedClass();
$aAttribute['related_icon'] = MetaModel::GetClassIcon($aAttribute['related'], false);
$aAttribute['related_position'] = $bOnTheLeft ? 1 : -1;
$aAttribute['relation_type'] = 1;
$bOnTheLeft = !$bOnTheLeft; // Toggle the side
}
}
else
{
if ($oAttDef->IsHierarchicalKey())
{
$aAttribute['related'] = $sClass;
$aAttribute['related_icon'] = MetaModel::GetClassIcon($aAttribute['related'], false);
$aAttribute['related_position'] = $bOnTheLeft ? 1 : -1;
$aAttribute['relation_type'] = 2;
$bOnTheLeft = !$bOnTheLeft; // Toggle the side
$sSelfReference = "true";
}
else
{
if ($oAttDef->IsExternalKey())
{
$aAttribute['related'] = $oAttDef->GetTargetClass();
$aAttribute['related_icon'] = MetaModel::GetClassIcon($aAttribute['related'], false);
$aAttribute['related_position'] = $bOnTheLeft ? 1 : -1;
$aAttribute['relation_type'] = 3;
$bOnTheLeft = !$bOnTheLeft; // Toggle the side
}
}
}
if ($oAttDef->IsLinkSet() || $oAttDef->IsHierarchicalKey() || $oAttDef->IsExternalKey())
{
$sOrigin = MetaModel::GetAttributeOrigin($sClass, $sAttCode);
$aAttribute['origin'] = $sOrigin;
$aOriginsRef[$sOrigin] = true;
$aDataRef[$sAttCode] = $aAttribute;
}
}
//sort referencing data
$aOrigins = array_keys($aOrigins);
$idx = 0;
$bOnTheLeft = true;
foreach ($aData as $sAttCode => $aAttribute)
{
$is_also_referenced = false;
foreach ($aDataRef as $sAttCodeRef => $aAttributeRef)
{
if (!empty($aDataRef[$sAttCodeRef]['related']) && ($aData[$sAttCode]['related'] == $aDataRef[$sAttCodeRef]['related']))
{
$is_also_referenced = true;
}
}
if (!$is_also_referenced)
{
$aData[$sAttCode]['related_position'] = ($bOnTheLeft) ? -1 : 1;
$bOnTheLeft = !$bOnTheLeft;
$aData[$sAttCode]['origin_index'] = ($aData[$sAttCode]['related_position'] == -1) ? ++$idx : $idx;
}
else
{
unset($aData[$sAttCode]);
}
}
ksort($aData);
$idx = 0;
$aFinalDataReferencing = array();
foreach ($aData as $sAttCode => $aAttribute)
{
$aData[$sAttCode]['alphabetical_index'] = $aAttribute['related_position'] == 1 ? ++$idx : $idx;
$aFinalDataReferencing[] = $aData[$sAttCode];
}
$sDataReferencing = json_encode($aFinalDataReferencing);
$sOriginsReferencing = json_encode(array_keys($aOrigins));
//sort referenced data
$idx = 1;
foreach ($aDataRef as $sAttCode => $aAttribute)
{
$aDataRef[$sAttCode]['origin_index'] = $idx++;
}
$idx = 1;
$aFinalData = array();
foreach ($aDataRef as $sAttCode => $aAttribute)
{
$aDataRef[$sAttCode]['alphabetical_index'] = $idx++;
$aFinalData[] = $aDataRef[$sAttCode];
}
$sData = json_encode($aFinalData);
// 3) Processing data and building graph
//
$oPage->add(
<<
EOF
);
$oPage->add_ready_script(
<< 1)
{
schema.append("path")
.attr("d", "M"+(margins.left + relatedCellWidth + gap + cellWidth*0.75)+" "+ (margins.top + cellHeight + cellHeight*(datareflen+1.5)) +" l 0 "+ cellHeight*-datareflen+"")
.attr("fill", "transparent")
.attr("stroke", "black")
.attr("stroke-linecap", "round")
.attr("stroke-width", 2);
}
if(dataref.length > 0)
{
schema.append("path")
.attr("d", "M"+(margins.left + relatedCellWidth + gap + cellWidth*0.25)+" "+ (margins.top + cellHeight + cellHeight*(datareflen+1.5)) +" l 0 "+ cellHeight*-datareflen+"")
.attr("fill", "transparent")
.attr("stroke", "black")
.attr("stroke-linecap", "round")
.attr("stroke-width", 2);
}
//loop + arrow to show that a class has a hierarchical attribute
if(isSelfReferencing == true)
{
schema.append("path")
.attr("d", "M"+(margins.left + relatedCellWidth + gap + cellWidth/1.9)+" "+ (margins.top + cellHeight*(datareflen+1.5))+" a 20 20 0 1 0 20 0 m-10 0l-5 3 m5 -3 l-5 -3")
.attr("id", "selfreferencing")
.attr("fill", "transparent")
.attr("stroke", "black")
.attr("stroke-linecap", "round")
.attr("stroke-width", 2)
.attr("transform", "rotate(95, "+ (margins.left + relatedCellWidth + gap + cellWidth/1.9) + ", " + ((margins.top + cellHeight*(datareflen+1.5))+10)+")");
}
// 2) Classes linked to horns (classes referencing us)
//
var fieldref = schema.selectAll("g")
.data(dataref, function(d) { return d.label + d.related } )
.enter().append("g")
.attr("transform", function(d, i) { return "translate(" + (margins.left + relatedCellWidth + gap + cellWidth/2) + "," + (margins.top + d.origin_index*cellHeight*2) + ")"; });
fieldref.filter(function(d) {
return (d.related != null);
}).append("a")
.attr("xlink:href",function(d){ return refClassLinkpre + d.related + refClassLinksuf})
.append("rect")
.attr("x", -relatedCellWidth/2)
.attr("width", relatedCellWidth)
.attr("height", cellHeight)
.attr("fill", "#fff")
.attr("stroke", "#000")
.attr("stroke-width", 1)
.attr("transform", function(d, i) { return "translate("+ d.related_position*(relatedCellWidth/2+cellWidth/2+gap) +", 0)"; });
fieldref.filter(function(d) {
return (d.related != null);
}).append("a")
.attr("xlink:href",function(d){ return refClassLinkpre + d.related + refClassLinksuf })
.append("text")
.attr("x", 0)
.attr("y", cellHeight / 2)
.attr("dy", ".35em")
.text(function(d) { return d.related ? d.related : ''; })
.attr("transform", function(d, i) { return "translate("+ (d.related_position*(relatedCellWidth/2+cellWidth/2+gap)) +", 0)"; });
fieldref.filter(function(d) {
return (d.related != null);
}).append("path")
.attr("d", "M"+(cellWidth/2 - cellWidth*0.25)+" "+cellHeight/2+" h"+(gap-2 + cellWidth*0.25))
.attr("fill", "transparent")
.attr("stroke", "black")
.attr("stroke-linecap", "round")
.attr("stroke-width", 2)
.attr("transform", function(d, i) { return (d.related_position < 0) ? "rotate(180, 0, "+(cellHeight/2)+")" : ""});
fieldref.filter(function(d) {
return (d.related != null);
}).append("path")
.attr("d", "M"+cellWidth/1.9*-1+" "+cellHeight/2+" m-10 0l-5 3 m5 -3 l-5 -3")
.attr("fill", "transparent")
.attr("stroke", "black")
.attr("stroke-linecap", "round")
.attr("stroke-width", 2)
.attr("transform", function(d, i) { return (d.related_position < 0) ? "rotate(360, 0, "+(cellHeight/2)+")" : "rotate(180, 0, "+(cellHeight/2)+")"});
fieldref.filter(function(d) {
return (d.related != null);
}).append("svg:image")
.attr("x", -relatedCellWidth/2)
.attr("width", cellHeight)
.attr("height", cellHeight)
.attr("xlink:href", function(d, i) { return d.related_icon })
.attr("transform", function(d, i) { return "translate("+ (d.related_position*(relatedCellWidth/2+cellWidth/2+gap) - 12)+", -" + cellHeight/2+" )"; });
// 3) Main class rectangle and attributes rectangles
//
var field = schema.selectAll("g")
.data(data, function(d) { return d.label } )
.enter().append("g")
.attr("transform", function(d, i) { return "translate(" + (margins.left + relatedCellWidth + gap + cellWidth/2) + "," + (margins.top + (datareflen+1.5)*cellHeight + d.origin_index*cellHeight) + ")"; });
field.append("rect")
.attr("x", -cellWidth/2)
.attr("width", cellWidth)
.attr("class", function(d, i){return (d.relation_type == 2 ? "selfattr" : "extattr");})
.attr("height", cellHeight)
.attr("fill", "#fff")
.attr("stroke", "#000")
.attr("stroke-width", 1);
field.append("text")
.attr("x", 0)
.attr("y", cellHeight / 2)
.attr("dy", ".35em")
.text(function(d) { return d.label; });
field.append("text")
.attr("x", function(d){ return 7*d.label.length/2 })
.attr("y", cellHeight / 2)
.attr("dy", ".35em")
.attr("class", function(d, i){return (d.relation_type == 2 ? "selfattrtxt" : "");})
.text(function(d) { return ((d.relation_type == 2) ? '\uf01e' : '' ); });
// 4) Classes that our main class is refering to
//
field.filter(function(d) {
return (d.related != null) && (d.relation_type != 2);
}).append("a")
.attr("xlink:href",function(d){ return refClassLinkpre + d.related + refClassLinksuf})
.append("rect")
.attr("x", -relatedCellWidth/2)
.attr("width", relatedCellWidth)
.attr("height", cellHeight)
.attr("fill", "#fff")
.attr("stroke", "#000")
.attr("stroke-width", 1)
.attr("transform", function(d, i) { return "translate("+ d.related_position*(relatedCellWidth/2+cellWidth/2+gap) +", 0)"; });
field.filter(function(d) {
return (d.related != null) && (d.relation_type != 2);
}).append("a")
.attr("xlink:href",function(d){ return refClassLinkpre + d.related + refClassLinksuf})
.append("text")
.attr("x", 0)
.attr("y", cellHeight / 2)
.attr("dy", ".35em")
.text(function(d) { return d.related ? d.related : ''; })
.attr("transform", function(d, i) { return "translate("+ (d.related_position*(relatedCellWidth/2+cellWidth/2+gap)) +", 0)"; });
field.filter(function(d) {
return (d.related != null) && (d.relation_type != 2);
}).append("path")
.attr("d", "M"+cellWidth/2+" "+cellHeight/2+" h"+(gap-2))
.attr("fill", "transparent")
.attr("stroke", "black")
.attr("stroke-linecap", "round")
.attr("stroke-width", 2)
.attr("transform", function(d, i) { return (d.related_position < 0) ? "rotate(180, 0, "+(cellHeight/2)+")" : ""});
field.filter(function(d) {
return (d.related != null) && (d.relation_type == 3 || d.relation_type == 0);
}).append("path")
.attr("d","M"+ (gap - 2 + cellWidth/2) +" "+cellHeight/2+" m-10 0l-5 3 m5 -3 l-5 -3")
.attr("fill", "transparent")
.attr("stroke", "black")
.attr("stroke-linecap", "round")
.attr("stroke-width", 2)
.attr("transform", function(d, i) { return (d.related_position < 0) ? "rotate(180, 0, "+(cellHeight/2)+")" : ""});
field.filter(function(d) {
return (d.related != null) && (d.relation_type == 1 || d.relation_type == 0);
}).append("path")
.attr("d", "M"+cellWidth/1.9*-1+" "+cellHeight/2+" m-10 0l-5 3 m5 -3 l-5 -3")
.attr("fill", "transparent")
.attr("stroke", "black")
.attr("stroke-linecap", "round")
.attr("stroke-width", 2)
.attr("transform", function(d, i) { return (d.related_position < 0) ? "rotate(360, 0, "+(cellHeight/2)+")" : "rotate(180, 0, "+(cellHeight/2)+")"});
field.filter(function(d) {
return (d.related != null) && (d.relation_type == 0);
}).append("circle")
.attr("r", 5)
.attr("cy", cellHeight/2)
.on('mouseover',function(d){
divD3.transition()
.duration(200)
.style("opacity","1");
divD3.style("left", (d3.event.pageX - 7*d['tooltip_data']['class'].length/2) + "px");
divD3.style("top", (d3.event.pageY - 65) + "px");
divD3.html( '' + d['tooltip_data']['class'] + '
'
+ ( (d.related_position < 0) ? d['tooltip_data']['to_remote'] : d['tooltip_data']['to_me'] ) + ' '
+ ( (d.related_position < 0) ? d['tooltip_data']['to_me'] : d['tooltip_data']['to_remote'] ) + ' ');
})
.on('mouseout',function(d){
divD3.transition()
.duration(500)
.style("opacity","0");
})
.attr("transform", function(d, i) { return "translate("+ d.related_position*(cellWidth+gap+2)/2 +", 0)"; });
field.filter(function(d) {
return (d.related != null) && (d.relation_type != 2);
}).append("svg:image")
.attr("x", -relatedCellWidth/2)
.attr("width", cellHeight)
.attr("height", cellHeight)
.attr("xlink:href", function(d, i) { return d.related_icon })
.attr("transform", function(d, i) { return "translate("+ (d.related_position*(relatedCellWidth/2+cellWidth/2+gap) - 12)+", -" + cellHeight/2+" )"; });
field.append("rect")
.attr("x", -cellWidth/2 - 5)
.attr("width", 5)
.attr("height", cellHeight)
.attr("fill", function(d) { return aColors(aOrigins.indexOf(d.origin)); } )
.attr("stroke-width", 0)
.attr("class","liseret");
field.filter(function(d) {
return (d.icon != null);
}).append("svg:image")
.attr("x", -cellWidth/2)
.attr("width", 36)
.attr("height", 36)
.attr("xlink:href", function(d, i) { return d.icon })
.attr("transform", "translate(-12, -24)");
JS
);
}
catch (Exception $e)
{
$oPage->p(''.Dict::Format('UI:RunQuery:Error', $e->getMessage()).' ');
}
}
/**
* Display the details of a given class of objects
*
* @param \iTopWebPage $oPage
* @param string $sClass
* @param string $sContext
*
* @throws \CoreException
*/
function DisplayClassDetails($oPage, $sClass, $sContext)
{
DisplayClassHeader($oPage, $sClass);
$aParentClasses = array();
foreach (MetaModel::EnumParentClasses($sClass) as $sParentClass)
{
$aParentClasses[] = MakeClassHLink($sParentClass, $sContext);
}
if (count($aParentClasses) > 0)
{
$sParents = implode(' >> ', $aParentClasses)." >> $sClass ";
}
else
{
$sParents = '';
}
$oPage->p("[".Dict::S('UI:Schema:AllClasses')." ] $sParents");
if (MetaModel::HasChildrenClasses($sClass))
{
$oPage->add("");
$oPage->add("".$sClass."\n");
DisplaySubclasses($oPage, $sClass, $sContext);
$oPage->add(" \n");
$oPage->add(" \n");
$oPage->add_ready_script('$("#ClassHierarchy").treeview();');
}
$oPage->p('');
$oPage->add("");
$oPage->AddTabContainer('details');
$oPage->SetCurrentTabContainer('details');
// List the attributes of the object
$aForwardChangeTracking = MetaModel::GetTrackForwardExternalKeys($sClass);
$aDetails = array();
$aOrigins = array();
foreach (MetaModel::ListAttributeDefs($sClass) as $sAttCode => $oAttDef)
{
if ($oAttDef->IsExternalKey())
{
$sValue = Dict::Format('UI:Schema:ExternalKey_To', MakeClassHLink($oAttDef->GetTargetClass(), $sContext));
if (array_key_exists($sAttCode, $aForwardChangeTracking))
{
$oLinkSet = $aForwardChangeTracking[$sAttCode];
$sRemoteClass = $oLinkSet->GetHostClass();
$sValue = $sValue."* ";
}
}
elseif ($oAttDef->IsLinkSet())
{
$sValue = MakeClassHLink($oAttDef->GetLinkedClass(), $sContext);
}
else
{
$sValue = $oAttDef->GetDescription();
}
$sType = get_class($oAttDef);
$sTypeDict = $oAttDef->GetType();
$sTypeDesc = $oAttDef->GetTypeDesc();
$sOrigin = MetaModel::GetAttributeOrigin($sClass, $sAttCode);
$aOrigins[$sOrigin] = true;
$sAllowedValues = "";
$sMoreInfo = "";
$sDefaultNullValue = '""';
$aCols = array();
foreach ($oAttDef->GetSQLColumns() as $sCol => $sFieldDesc)
{
$aCols[] = "$sCol: $sFieldDesc";
}
if (count($aCols) > 0)
{
$aMoreInfo = array();
if ($oAttDef->IsNullAllowed())
{
$aMoreInfo[] = Dict::S('UI:Schema:NullAllowed');
$sDefaultNullValue = (!is_null($oAttDef->GetNullValue()) ? $oAttDef->GetNullValue() : null);
if (!is_null($sDefaultNullValue) && !is_string($sDefaultNullValue))
{
$sDefaultNullValue = json_encode($sDefaultNullValue);
}
$sDefaultNullValue = (!is_null($sDefaultNullValue) ? json_encode(Dict::Format('UI:Schema:DefaultNullValue',
$sDefaultNullValue)) : '""');
}
else
{
$aMoreInfo[] = Dict::S('UI:Schema:NullNotAllowed');
}
if ($oAttDef->GetDefaultValue())
{
$sDefaultValue = $oAttDef->GetDefaultValue();
if (!is_string($sDefaultValue))
{
$sDefaultValue = json_encode($sDefaultValue);
}
$aMoreInfo[] = Dict::Format("UI:Schema:Default_Description", $sDefaultValue);
}
$sMoreInfo .= implode(', ', $aMoreInfo);
}
$sAttrCode = $oAttDef->GetCode();
$sIsEnumValues = 'false';
$sAllowedValuesEscpd = '""';
if ($oAttDef instanceof AttributeEnum)
{
// Display localized values for the enum (which depend on the localization provided by the class)
$aLocalizedValues = MetaModel::GetAllowedValues_att($sClass, $sAttCode, array());
$aDescription = array();
foreach ($aLocalizedValues as $val => $sDisplay)
{
$aDescription[] = "".$sDisplay." ( ".$val." ) ";
}
$sAllowedValues = implode(', ', $aDescription);
$sIsEnumValues = 'true';
}
elseif (is_object($oAllowedValuesDef = $oAttDef->GetValuesDef()))
{
$sAllowedValues = str_replace("Filter: ", "", $oAllowedValuesDef->GetValuesDescription());
$sAllowedValuesEscpd = json_encode($sAllowedValues);
$sFilterURL = urlencode($sAllowedValues);
$sAllowedValues = "⚵ ".Dict::S('UI:Schema:Attribute/Filter')." ";
}
else
{
$sAllowedValues = '';
}
$sAttrValueEscpd = json_encode($sValue);
$sAttrTypeDescEscpd = json_encode($sTypeDesc);
$sAttrOriginEscpd = json_encode($sOrigin);
$aDetails[] = array(
'code' => "".$oAttDef->GetLabel()." ( ".$oAttDef->GetCode()." ) ",
'type' => "".$sTypeDict." ( ".$sType." ) ",
'origincolor' => " ",
'origin' => "$sOrigin ",
'values' => $sAllowedValues,
'moreinfo' => " $sMoreInfo ",
);
//tooltip construction
$oPage->add_ready_script(
<<SetCurrentTab('UI:Schema:Attributes');
$aConfig = array(
'origincolor' => array('label' => "", 'description' => ""),
'code' => array('label' => Dict::S('UI:Schema:AttributeCode'), 'description' => Dict::S('UI:Schema:AttributeCode+')),
'type' => array('label' => Dict::S('UI:Schema:Type'), 'description' => Dict::S('UI:Schema:Type+')),
'values' => array('label' => Dict::S('UI:Schema:AllowedValues'), 'description' => Dict::S('UI:Schema:AllowedValues+')),
'moreinfo' => array('label' => Dict::S('UI:Schema:MoreInfo'), 'description' => Dict::S('UI:Schema:MoreInfo+')),
'origin' => array('label' => Dict::S('UI:Schema:Origin'), 'description' => Dict::S('UI:Schema:Origin+')),
);
$oPage->table($aConfig, $aDetails);
$sOrigins = json_encode(array_keys($aOrigins));
//color calculation in order to keep 1 color for 1 extended class. Colors are interpolated and will be used for
// graph scheme color too
$oPage->add_ready_script(
<<< EOF
var aOrigins = $sOrigins;
var aColors = d3.scale.linear().domain([1,aOrigins.length])
.interpolate(d3.interpolateHcl)
.range([d3.rgb("#007AFF"), d3.rgb('#FFF500')]);
$.each(aOrigins,function(idx, origin){
$('.originColor'+origin).parent().css('background-color',aColors(aOrigins.indexOf(origin)));
});
Array.prototype.forEach.call($(".listResults").find('td:nth-child(1),th:nth-child(1)'), function(e){
$(e).removeClass("header").addClass("originColor");
});
EOF
);
$oPage->SetCurrentTab('UI:Schema:RelatedClasses');
DisplayRelatedClassesGraph($oPage, $sClass);
$oPage->SetCurrentTab('UI:Schema:ChildClasses');
DisplaySubclasses($oPage, $sClass, $sContext);
$oPage->SetCurrentTab('UI:Schema:LifeCycle');
DisplayLifecycle($oPage, $sClass);
$oPage->SetCurrentTab('UI:Schema:Triggers');
DisplayTriggers($oPage, $sClass);
$oPage->SetCurrentTab();
$oPage->SetCurrentTabContainer();
}
/**
* Display the dropdown that allow to select the attributes/class display granularity
*/
function DisplayGranularityDisplayer($oPage)
{
$oPage->add("
".Dict::S('UI:Schema:DisplayLabel').
"
".Dict::S('UI:Schema:DisplaySelector/LabelAndCode')."
".Dict::S('UI:Schema:DisplaySelector/Label')."
".Dict::S('UI:Schema:DisplaySelector/Code')."
");
$sDisplayDropDownValue = htmlentities(appUserPreferences::GetPref('datamodel_viewer_display_granularity', 'labelandcode'), ENT_QUOTES,
"UTF-8");
//granularity displayer listener
$oPage->add_ready_script(
<<add("");
//content header
$oPage->add("