diff --git a/css/backoffice/pages/_all.scss b/css/backoffice/pages/_all.scss
index 90ffc7679..3a77ca2da 100644
--- a/css/backoffice/pages/_all.scss
+++ b/css/backoffice/pages/_all.scss
@@ -8,4 +8,5 @@
@import "attachments";
@import "impact-analysis";
@import "audit";
-@import "data-synchro";
\ No newline at end of file
+@import "data-synchro";
+@import "datamodel-viewer";
\ No newline at end of file
diff --git a/css/backoffice/pages/_datamodel-viewer.scss b/css/backoffice/pages/_datamodel-viewer.scss
new file mode 100644
index 000000000..1ddb75c0c
--- /dev/null
+++ b/css/backoffice/pages/_datamodel-viewer.scss
@@ -0,0 +1,124 @@
+$ibo-datamodel-viewer--parent--spacer--padding-y: 0 !default;
+$ibo-datamodel-viewer--parent--spacer--padding-x: 8px !default;
+
+$ibo-datamodel-viewer--attributes-table--first-column--width: 3px !default;
+
+$ibo-datamodel-viewer--origin-cell--diameter: 8px !default;
+$ibo-datamodel-viewer--origin-cell--border-radius: $ibo-border-radius-full !default;
+
+$ibo-datamodel-viewer--classes-list--height: 100% !default;
+$ibo-datamodel-viewer--classes-list--max-width: 350px !default;
+$ibo-datamodel-viewer--classes-list--padding-left: 24px !default;
+
+$ibo-datamodel-viewer--lifecycle--code--color: $ibo-color-grey-700 !default;
+$ibo-datamodel-viewer--lifecycle--stimuli--color: $ibo-color-blue-900 !default;
+$ibo-datamodel-viewer--lifecycle--attribute-option--color: $ibo-color-pink-900 !default;
+
+$ibo-datamodel-viewer--schema--rectangle--hover--fill: $ibo-color-grey-400 !default;
+$ibo-datamodel-viewer--schema--text--fill: $ibo-color-grey-900 !default;
+$ibo-datamodel-viewer--schema--self-referencing--hover--fill: $ibo-datamodel-viewer--schema--rectangle--hover--fill !default;
+$ibo-datamodel-viewer--schema--tooltip--fill: $ibo-color-white-100 !default;
+$ibo-datamodel-viewer--schema--tooltip--background-color: $ibo-color-grey-900 !default;
+$ibo-datamodel-viewer--schema--tooltip--border-color: $ibo-color-grey-700 !default;
+$ibo-datamodel-viewer--schema--tooltip--border-radius: $ibo-border-radius-300 !default;
+
+$ibo-datamodel-viewer--schema--tooltip--icon--font-size: $ibo-font-size-100 !default;
+$ibo-datamodel-viewer--schema--tooltip--span--margin: 3px !default;
+
+$ibo-datamodel-viewer--schema--tooltip-top--border-color: $ibo-color-grey-700 !default;
+$ibo-datamodel-viewer--schema--tooltip-top--padding: 3px !default;
+
+
+#ibo-datamodel-viewer{
+ display: flex;
+ flex-direction: row;
+}
+
+.ibo-datamodel-viewer--details{
+ flex-grow: 1;
+ .ibo-panel--title .ibo-panel--title-title .ibo-panel--title-title-subtitle{
+ @extend %ibo-font-ral-nor-150;
+ }
+}
+
+.ibo-datamodel-viewer--parent--spacer{
+ padding: $ibo-datamodel-viewer--parent--spacer--padding-y $ibo-datamodel-viewer--parent--spacer--padding-x;
+}
+
+#ibo-datamodel-viewer--attributes-table{
+ > tbody tr td:first-child{
+ width: $ibo-datamodel-viewer--attributes-table--first-column--width;
+ }
+}
+
+.ibo-datamodel-viewer--origin-cell{
+ vertical-align: middle;
+ > div {
+ height: $ibo-datamodel-viewer--origin-cell--diameter;
+ width: $ibo-datamodel-viewer--origin-cell--diameter;
+ border-radius: $ibo-datamodel-viewer--origin-cell--border-radius;
+ }
+}
+
+.ibo-datamodel-viewer--classes-list{
+ position: relative;
+ height: $ibo-datamodel-viewer--classes-list--height;
+ max-width: $ibo-datamodel-viewer--classes-list--max-width;
+ padding-left: $ibo-datamodel-viewer--classes-list--padding-left;
+ overflow-y: scroll;
+}
+
+.ibo-datamodel-viewer--lifecycle--code{
+ color: $ibo-datamodel-viewer--lifecycle--code--color;
+}
+
+.ibo-datamodel-viewer--lifecycle--stimuli{
+ color: $ibo-datamodel-viewer--lifecycle--stimuli--color;
+}
+
+.ibo-datamodel-viewer--lifecycle--attribute-option{
+ color: $ibo-datamodel-viewer--lifecycle--attribute-option--color;
+}
+
+
+.dataModelSchema g {
+ cursor: pointer;
+}
+
+.dataModelSchema g:hover rect:not(.liseret) {
+ fill: $ibo-datamodel-viewer--schema--rectangle--hover--fill;
+}
+
+.dataModelSchema text {
+ fill: $ibo-datamodel-viewer--schema--text--fill;
+ text-anchor: middle;
+ @extend %ibo-font-ral-nor-100;
+}
+
+#selfreferencing:hover ~ g > .selfattr {
+ fill: $ibo-datamodel-viewer--schema--self-referencing--hover--fill;
+}
+
+.tooltipD3 {
+ position: fixed;
+ text-align: center;
+ background: $ibo-datamodel-viewer--schema--tooltip--fill;
+ border: 1px solid $ibo-datamodel-viewer--schema--tooltip--border-color;
+ border-radius: $ibo-datamodel-viewer--schema--tooltip--border-radius;
+ pointer-events: none;
+ fill: $ibo-datamodel-viewer--schema--tooltip--background-color;
+ @extend %ibo-font-ral-nor-100;
+ text-anchor: middle;
+ i {
+ font-size: $ibo-datamodel-viewer--schema--tooltip--icon--font-size;
+ }
+ span {
+ margin: $ibo-datamodel-viewer--schema--tooltip--span--margin;
+ }
+}
+
+#tooltipD3_top {
+ @extend %ibo-font-ral-bol-100;
+ border-bottom: 1px solid $ibo-datamodel-viewer--schema--tooltip-top--border-color;
+ padding: $ibo-datamodel-viewer--schema--tooltip-top--padding;
+}
diff --git a/pages/schema.php b/pages/schema.php
index 2c4947d75..5bcd6d447 100644
--- a/pages/schema.php
+++ b/pages/schema.php
@@ -17,6 +17,14 @@
* You should have received a copy of the GNU Affero General Public License
*/
+use Combodo\iTop\Application\UI\Base\Component\Button\ButtonUIBlockFactory;
+use Combodo\iTop\Application\UI\Base\Component\DataTable\DataTableUIBlockFactory;
+use Combodo\iTop\Application\UI\Base\Component\Input\Select\Select;
+use Combodo\iTop\Application\UI\Base\Component\Input\Select\SelectOptionUIBlockFactory;
+use Combodo\iTop\Application\UI\Base\Component\Panel\PanelUIBlockFactory;
+use Combodo\iTop\Application\UI\Base\Component\Title\Title;
+use Combodo\iTop\Application\UI\Base\Layout\PageContent\PageContentWithSideContent;
+
require_once('../approot.inc.php');
require_once(APPROOT.'/application/application.inc.php');
@@ -35,16 +43,6 @@ function MakeClassHLink($sClass, $sContext)
'UTF-8')."\">".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
*/
@@ -78,6 +76,48 @@ function DisplaySubclasses($oPage, $sClass, $sContext)
$oPage->add("\n");
}
}
+/**
+ * Helper for the global list and the details of a given class
+ */
+function GetSubclasses($sClass, $sContext)
+{
+
+ $sHtml = '';
+ try{
+ $aChildClasses = MetaModel::EnumChildClasses($sClass);
+ if (count($aChildClasses) != 0)
+ {
+
+ $sHtml .= "
";
+ $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)
+ {
+ $sHtml .="".MakeClassHLink($sClassName, $sContext);
+ $sHtml .= GetSubclasses($sClassName, $sContext);
+ $sHtml .= " ";
+ }
+ }
+ $sHtml .= " ";
+ }
+ }
+
+ catch(Exception $e){
+ }
+ return $sHtml;
+}
/**
* Helper for the lifecycle details of a given class
@@ -100,27 +140,31 @@ function DisplayLifecycle($oPage, $sClass)
EOF
);
- $oPage->add("Open All Close All ");
- $oPage->add_ready_script(
- <<SetOnClickJsCode(
+ <<SetOnClickJsCode(
+ <<AddUiBlock($oOpenAllButton);
+ $oPage->AddUiBlock($oCloseAllButton);
$oPage->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("$sStateLabel ( $sStateCode ) $sStateDescription \n");
$oPage->add("\n");
foreach (MetaModel::EnumTransitions($sClass, $sStateCode) as $sStimulusCode => $aTransitionDef)
{
@@ -153,10 +197,10 @@ EOF
$sActions = "";
}
- $oPage->add("$sStimulusLabel
- ( $sStimulusCode )
- =>
- $sTargetStateLabel ( $sTargetState ) $sActions \n");
+ $oPage->add("$sStimulusLabel
+ ( $sStimulusCode )
+
+ $sTargetStateLabel ( $sTargetState ) $sActions \n");
}
$oPage->add(" \n");
}
@@ -167,7 +211,7 @@ EOF
{
$sStateLabel = MetaModel::GetStateLabel($sClass, $sStateCode);
$sStateDescription = MetaModel::GetStateDescription($sClass, $sStateCode);
- $oPage->add("$sStateLabel ( $sStateCode ) $sStateDescription \n");
+ $oPage->add("$sStateLabel ( $sStateCode ) $sStateDescription \n");
if (count($aStates[$sStateCode]['attribute_list']) > 0)
{
$oPage->add("\n");
@@ -206,7 +250,7 @@ EOF
$sOptions = "";
}
- $oPage->add("$sAttLabel $sOptions \n");
+ $oPage->add("$sAttLabel $sOptions \n");
}
$oPage->add(" \n");
}
@@ -236,67 +280,22 @@ function DisplayTriggers($oPage, $sClass)
/**
* Display the list of classes from the business model
*/
-function DisplayClassesList($oPage, $sContext)
+function DisplayClassesList($oPage, $oLayout, $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(
- <<]*)(" + term + ")(?![^<>]*>)(?![^&;]+;)", "gi"), "$1 ");
- return $( "" ).append( val ).appendTo( ul );
- };
-
- $("#search-model").on('input', function() {
- var search_result = [];
- $("#ClassesList").find("li").each(function(){
- if( ! ~$(this).children("a").text().toLowerCase().indexOf($("#search-model").val().toLowerCase())){
- $(this).hide();
- }
- else{
- search_result.push($(this));
- }
- });
- search_result.forEach(function(e){
- e.show();
- e.find('ul > li').show();
- e.parents().show();
- });
- });
- $("#delDataModelSearch").on ('click', function(){
- $("#search-model").val("");
- $("#search-model").trigger('input');
- });
-JS
-
- );
+ $oLayout->AddSideHtml("".Dict::S('UI:Schema:ClassFilter')." ");
+
+ $oListSearch = new Select("ibo-datamodel-viewer--class-search");
+ $oListSearch->SetName('aa');
// Get all the "root" classes for display
$aRootClasses = array();
$aClassLabelAndCodeAsJSON = [];
$aClassLabelAsJSON = array();
$aClassCodeAsJSON = array();
+
+ $oOptionSearch = SelectOptionUIBlockFactory::MakeForSelectOption('', "select option", true);
+ $oListSearch->AddOption($oOptionSearch->SetDisabled(true));
+
foreach (MetaModel::GetClasses() as $sClassName)
{
if (MetaModel::IsRootClass($sClassName))
@@ -309,6 +308,8 @@ JS
}
$sLabelClassName = MetaModel::GetName($sClassName);
+ $oOptionSearch = SelectOptionUIBlockFactory::MakeForSelectOption($sClassName, "$sLabelClassName ($sClassName)", false);
+ $oListSearch->AddOption($oOptionSearch);
//Fetch classes names for autocomplete purpose
// - Encode as JSON to escape quotes and other characters
array_push ($aClassLabelAndCodeAsJSON, ["value"=>$sClassName,"label"=>"$sLabelClassName ($sClassName)"]);
@@ -316,10 +317,35 @@ JS
array_push ($aClassCodeAsJSON, ["value"=>$sClassName,"label"=>"$sClassName"]);
}
usort($aClassLabelAndCodeAsJSON, "Label_sort");
- // - Push to autocomplete
- $oPage->add_script("autocompleteClassLabelAndCode=".json_encode($aClassLabelAndCodeAsJSON).";");
- $oPage->add_script("autocompleteClassLabel=".json_encode($aClassLabelAsJSON).";");
- $oPage->add_script("autocompleteClassCode=".json_encode($aClassCodeAsJSON).";");
+ $oLayout->AddSideBlock($oListSearch);
+ $oPage->add_ready_script(
+ <<AddSideHtml("\n");
+ $oPage->add_ready_script(
+ <<add("".MakeClassHLink($sClassName, $sContext)."\n");
- DisplaySubclasses($oPage, $sClassName, $sContext);
- $oPage->add(" \n");
+ $oLayout->AddSideHtml("".MakeClassHLink($sClassName, $sContext)."\n");
+ $oLayout->AddSideHtml(GetSubclasses($sClassName, $sContext));
+ $oLayout->AddSideHtml(" \n");
}
elseif (MetaModel::IsStandaloneClass($sClassName))
{
- $oPage->add("".MakeClassHLink($sClassName, $sContext)." \n");
+ $oLayout->AddSideHtml("".MakeClassHLink($sClassName, $sContext)." \n");
}
}
- $oPage->add(" \n");
- $oPage->add_ready_script('$("#ClassesList").treeview();');
+ $oLayout->AddSideHtml(" \n");
+ $oPage->add_ready_script('$("#ibo-datamodel-viewer--classes-list--list").treeview();');
}
function Label_sort($building_a, $building_b) {
@@ -815,7 +841,6 @@ JS
*/
function DisplayClassDetails($oPage, $sClass, $sContext)
{
- DisplayClassHeader($oPage, $sClass);
$aParentClasses = array();
foreach (MetaModel::EnumParentClasses($sClass) as $sParentClass)
{
@@ -823,27 +848,25 @@ function DisplayClassDetails($oPage, $sClass, $sContext)
}
if (count($aParentClasses) > 0)
{
- $sParents = implode(' >> ', $aParentClasses)." >> $sClass ";
+ $sParents = implode(' ', $aParentClasses).' '. MetaModel::GetName($sClass) . '('.$sClass.')';
}
else
{
$sParents = '';
}
- $oPage->p("[".Dict::S('UI:Schema:AllClasses')." ] $sParents");
+ $sClassHierarchy = ("[".Dict::S('UI:Schema:AllClasses')." ] $sParents");
- if (MetaModel::HasChildrenClasses($sClass))
+ $oEnchancedPanel = PanelUIBlockFactory::MakeEnhancedNeutral(MetaModel::GetName($sClass) . ' ('.$sClass.')' , MetaModel::GetClassIcon($sClass,false));
+ $sClassDescritpion = MetaModel::GetClassDescription($sClass);
+ $oEnchancedPanelSubtitle = $oEnchancedPanel->GetSubTitleBlock();
+ $oEnchancedPanelSubtitle->AddHtml($sClassHierarchy . ($sClassDescritpion == "" ? "" : ' - ' . $sClassDescritpion));
+ if (MetaModel::IsAbstract($sClass))
{
- $oPage->add("");
- $oPage->add("".$sClass."\n");
- DisplaySubclasses($oPage, $sClass, $sContext);
- $oPage->add(" \n");
- $oPage->add(" \n");
- $oPage->add_ready_script('$("#ClassHierarchy").treeview();');
+ $oEnchancedPanelSubtitle->AddHtml(' - ');
}
- $oPage->p('');
- $oPage->add("");
- $oPage->AddTabContainer('details');
+ $oPage->AddUiBlock($oEnchancedPanel);
+ $oPage->AddTabContainer('details', '', $oEnchancedPanel);
$oPage->SetCurrentTabContainer('details');
// List the attributes of the object
$aForwardChangeTracking = MetaModel::GetTrackForwardExternalKeys($sClass);
@@ -944,7 +967,7 @@ function DisplayClassDetails($oPage, $sClass, $sContext)
$aDetails[] = array(
'code' => ''.$oAttDef->GetLabel().' ( '.$oAttDef->GetCode().' ) ',
'type' => ''.$sTypeDict.' ( '.$sType.' ) ',
- 'origincolor' => ' ',
+ 'origincolor' => '
',
'origin' => "$sOrigin ",
'values' => $sAllowedValues,
'moreinfo' => ''.$sMoreInfo.' ',
@@ -960,7 +983,17 @@ function DisplayClassDetails($oPage, $sClass, $sContext)
'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);
+
+ $oAttributesPanel = DataTableUIBlockFactory::MakeForStaticData('', $aConfig, $aDetails, 'ibo-datamodel-viewer--attributes-table');
+ $sTableId = $oAttributesPanel->GetId();
+ $oPage->AddUiBlock($oAttributesPanel);
+ $oPage->add_script(
+ <<SetCurrentTab('UI:Schema:RelatedClasses');
DisplayRelatedClassesGraph($oPage, $sClass);
- $oPage->SetCurrentTab('UI:Schema:ChildClasses');
-
- DisplaySubclasses($oPage, $sClass, $sContext);
-
+
+ if (MetaModel::HasChildrenClasses($sClass))
+ {
+ $oPage->SetCurrentTab('UI:Schema:ChildClasses');
+ $oPage->add("");
+ $oPage->add("".$sClass."\n");
+ DisplaySubclasses($oPage, $sClass, $sContext);
+ $oPage->add(" \n");
+ $oPage->add(" \n");
+ $oPage->add_ready_script('$("#ClassHierarchy").treeview({collapsed: false,});');
+ }
+
$oPage->SetCurrentTab('UI:Schema:LifeCycle');
DisplayLifecycle($oPage, $sClass);
@@ -998,102 +1039,13 @@ EOF
}
-/**
- * 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("