export-V2.phpем>.',
'UI:Schema:Title' => ITOP_APPLICATION_SHORT.' схема объектов',
'UI:Schema:TitleForClass' => '%1$s schema~~',
+ 'UI:Schema:NoClassSelected' => 'No class selected, please choose one~~',
'UI:Schema:CategoryMenuItem' => 'Категория %1$s',
'UI:Schema:Relationships' => 'Отношения',
'UI:Schema:AbstractClass' => 'Абстрактный класс: используется для наследования свойств, объекты этого класса не создаются.',
diff --git a/dictionaries/sk.dictionary.itop.ui.php b/dictionaries/sk.dictionary.itop.ui.php
index 35c8ed9244..539d06bbab 100644
--- a/dictionaries/sk.dictionary.itop.ui.php
+++ b/dictionaries/sk.dictionary.itop.ui.php
@@ -734,6 +734,7 @@ Dict::Add('SK SK', 'Slovak', 'Slovenčina', [
'UI:Query:UrlV1' => 'The list of fields has been left unspecified. The page export-V2.php cannot be invoked without this information. Therefore, the URL suggested here below points to the legacy page: export.php. This legacy version of the export has the following limitation: the list of exported fields may vary depending on the output format and the data model of '.ITOP_APPLICATION_SHORT.'.
Should you want to guarantee that the list of exported columns will remain stable on the long run, then you must specify a value for the attribute "Fields" and use the page export-V2.php.~~',
'UI:Schema:Title' => ITOP_APPLICATION_SHORT.' objektová schéma',
'UI:Schema:TitleForClass' => '%1$s schema~~',
+ 'UI:Schema:NoClassSelected' => 'No class selected, please choose one~~',
'UI:Schema:CategoryMenuItem' => 'Kategória %1$s',
'UI:Schema:Relationships' => 'Vzťahy',
'UI:Schema:AbstractClass' => 'Abstraktná trieda: žiadny objekt z tejto triedy nemôže byť inštancovaný.',
diff --git a/dictionaries/tr.dictionary.itop.ui.php b/dictionaries/tr.dictionary.itop.ui.php
index b74e3f264b..8e5907c968 100644
--- a/dictionaries/tr.dictionary.itop.ui.php
+++ b/dictionaries/tr.dictionary.itop.ui.php
@@ -732,6 +732,7 @@ Dict::Add('TR TR', 'Turkish', 'Türkçe', [
'UI:Query:UrlV1' => 'Alanların listesi belirtilmeden bırakılmıştır. export-V2.php sayfası bu bilgi olmadan çağrılamaz. Bu nedenle, aşağıda önerilen URL eski sayfaya işaret etmektedir: export.php. Dışa aktarmanın bu eski sürümü aşağıdaki sınırlamaya sahiptir: dışa aktarılan alanların listesi, '.ITOP_APPLICATION_SHORT.'\'un çıktı biçimine ve veri modeline bağlı olarak değişebilir. Dışa aktarılan sütunların listesinin uzun vadede sabit kalacağını garanti etmek istiyorsanız, "Alanlar" özelliği için bir değer belirtmeli ve export-V2.php sayfasını kullanmalısınız.',
'UI:Schema:Title' => 'iTop objects schema',
'UI:Schema:TitleForClass' => '%1$s schema~~',
+ 'UI:Schema:NoClassSelected' => 'No class selected, please choose one~~',
'UI:Schema:CategoryMenuItem' => 'Kategori %1$s',
'UI:Schema:Relationships' => 'İlişkiler',
'UI:Schema:AbstractClass' => 'Soyut sınıf: bu sınıftan nesne türetilemez.',
diff --git a/dictionaries/zh_cn.dictionary.itop.ui.php b/dictionaries/zh_cn.dictionary.itop.ui.php
index ffd02dd33f..bfbfbcf33f 100644
--- a/dictionaries/zh_cn.dictionary.itop.ui.php
+++ b/dictionaries/zh_cn.dictionary.itop.ui.php
@@ -731,6 +731,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [
'UI:Query:UrlV1' => '没有定义字段列表. 没有这个信息页面export-V2.php无法调用. 因此, 建议的以下 URL 指向传统页面: export.php. 该传统版本导出具有以下限制: 导出的字段列表很大程度依赖于导出格式和'.ITOP_APPLICATION_SHORT.'数据模型.
如果您需要确保导出的列保持长期稳定, 则必须为属性 "Fields" 指定值并使用页面export-V2.php.',
'UI:Schema:Title' => ITOP_APPLICATION_SHORT.'对象模型',
'UI:Schema:TitleForClass' => '%1$s 模式',
+ 'UI:Schema:NoClassSelected' => 'No class selected, please choose one~~',
'UI:Schema:CategoryMenuItem' => '类别 %1$s',
'UI:Schema:Relationships' => '关联',
'UI:Schema:AbstractClass' => '抽象类型: 此类型不能实例化对象.',
diff --git a/pages/schema.php b/pages/schema.php
index bde481ac81..84b1279997 100644
--- a/pages/schema.php
+++ b/pages/schema.php
@@ -6,8 +6,10 @@
*/
use Combodo\iTop\Application\Helper\WebResourcesHelper;
+use Combodo\iTop\Application\UI\Base\Component\Alert\AlertUIBlockFactory;
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\Html\HtmlFactory;
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;
@@ -27,15 +29,235 @@ LoginWebPage::DoLogin(); // Check user rights and prompt if needed
ApplicationMenu::CheckMenuIdEnabled('DataModelMenu');
/**
- * Helper for this page -> link to a class
+ * Get a short or full class label.
+ *
+ * Short: Class Label
+ * Full: Class Label (ClassName)
+ *
+ * Short: UserRequest
+ * Full: User Request (UserRequest)
+ *
+ * @param string $sClass
+ *
+ * @return string
+ * @throws CoreException
+ * @throws DictExceptionMissingString
+ * @throws ReflectionException
*/
-function MakeClassHLink($sClass, $sContext)
+function GetClassLabel(string $sClass): string
{
+ $sName = MetaModel::GetName($sClass);
+
+ $oClass = new ReflectionClass($sClass);
+
+ $sAbstract = '';
+ if ($oClass->isAbstract()) {
+ $sAbstract = '';
+ }
+
+ return "$sAbstract $sName ($sClass)";
+}
+
+/**
+ * Get class hierarchy as breadcrumb.
+ *
+ * @param string $sClass
+ * @param string $sContext
+ *
+ * @return string
+ * @throws CoreException
+ * @throws ReflectionException
+ */
+function GetClassHierarchy(string $sClass, string $sContext): string
+{
+ $sClassSpacer = '';
+ $aParentClasses = [];
+ foreach (MetaModel::EnumParentClasses($sClass) as $sParentClass) {
+ $aParentClasses[] = MakeClassHLink($sParentClass, $sContext, "ibo-button ibo-button ibo-block ibo-is-alternative ibo-is-neutral", true);
+ }
+
+ if (count($aParentClasses) > 0) {
+ $sParents = $sClassSpacer.implode($sClassSpacer, $aParentClasses);
+ } else {
+ $sParents = '';
+ }
+
+ return "";
+}
+
+/**
+ * Make a class link.
+ *
+ * @param string $sClass
+ * @param string $sContext
+ * @param string $sCssClasses
+ *
+ * @return string
+ * @throws CoreException
+ * @throws DictExceptionMissingString
+ * @throws ReflectionException
+ */
+function MakeClassHLink(string $sClass, string $sContext, string $sCssClasses = ''): string
+{
+ $sLabel = GetClassLabel($sClass);
+
return "".MetaModel::GetName($sClass)." (".$sClass.")";
+ )."\" class=\"$sCssClasses\">$sLabel";
+}
+
+/**
+ * Display class information.
+ *
+ * @param string $sClass
+ * @param string $sContext
+ *
+ * @return array
+ * @throws CoreException
+ * @throws DictExceptionMissingString
+ * @throws ReflectionException
+ */
+function GetClassInformation(string $sClass, string $sContext): array
+{
+ // List the attributes of the object
+ $aForwardChangeTracking = MetaModel::GetTrackForwardExternalKeys($sClass);
+ $aDetails = [];
+
+ $aOrigins = [];
+ 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();
+ }
+
+ [$classShortName, $label, $description] = array_values($oAttDef->GetTypeInformation());
+
+ $sOrigin = MetaModel::GetAttributeOrigin($sClass, $sAttCode);
+ $aOrigins[$sOrigin] = true;
+ $sMoreInfo = "";
+ $sDefaultNullValue = '';
+ if (call_user_func([get_class($oAttDef), 'IsBasedOnDBColumns'])) {
+
+ $aMoreInfo = [];
+ 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) ? 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();
+
+ 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, []);
+ $aDescription = [];
+ foreach ($aLocalizedValues as $val => $sDisplay) {
+ $aDescription[] = $sDisplay." (".$val.")";
+ }
+ $sAllowedValues = implode(', ', $aDescription);
+ } elseif (is_object($oAllowedValuesDef = $oAttDef->GetValuesDef())) {
+ $sAllowedValues = str_replace("Filter: ", "", $oAllowedValuesDef->GetValuesDescription());
+ $sAllowedValuesEscpd = utils::HtmlEntities($sAllowedValues);
+
+ $sFilterURL = urlencode($sAllowedValues);
+ $sAllowedValues = ' '.Dict::S('UI:Schema:Attribute/Filter')."";
+ } else {
+ $sAllowedValues = '';
+ }
+ $sAttrValueEscpd = utils::HtmlEntities($sValue);
+ $sAttrTypeDescEscpd = utils::HtmlEntities($description);
+ $sAttrOriginEscpd = utils::HtmlEntities($sOrigin);
+ $sDefaultNullValueEscpd = utils::HtmlEntities($sDefaultNullValue);
+
+ $aDetails[] = [
+ 'code' => ''.$oAttDef->GetLabel().' ('.$oAttDef->GetCode().')',
+ 'type' => ''.$label.' ('.$classShortName.')',
+ 'origincolor' => '',
+ 'origin' => "$sOrigin",
+ 'values' => $sAllowedValues,
+ 'moreinfo' => ''.$sMoreInfo.'',
+ ];
+
+ }
+
+ return [
+ 'details' => $aDetails,
+ 'origins' => $aOrigins,
+ ];
+}
+
+/**
+ * Display class attributes.
+ *
+ * @param iTopWebPage $oPage
+ * @param string $sClass
+ * @param string $sContext
+ *
+ * @return void
+ */
+function DisplayClassAttributes(iTopWebPage $oPage, string $sClass, string $sContext): void
+{
+ $aClassInformation = GetClassInformation($sClass, $sContext);
+
+ $aConfig = [
+ 'origincolor' => ['label' => "", 'description' => ""],
+ 'code' => ['label' => Dict::S('UI:Schema:AttributeCode'), 'description' => Dict::S('UI:Schema:AttributeCode+')],
+ 'type' => ['label' => Dict::S('UI:Schema:Type'), 'description' => Dict::S('UI:Schema:Type+')],
+ 'values' => ['label' => Dict::S('UI:Schema:AllowedValues'), 'description' => Dict::S('UI:Schema:AllowedValues+')],
+ 'moreinfo' => ['label' => Dict::S('UI:Schema:MoreInfo'), 'description' => Dict::S('UI:Schema:MoreInfo+')],
+ 'origin' => ['label' => Dict::S('UI:Schema:Origin'), 'description' => Dict::S('UI:Schema:Origin+')],
+ ];
+ $oTablePanel = PanelUIBlockFactory::MakeForClass($sClass, '');
+ $oTablePanel->AddCSSClass('ibo-datatable-panel');
+
+ $oAttributesTable = DataTableUIBlockFactory::MakeForStaticData('', $aConfig, $aClassInformation['details'], 'ibo-datamodel-viewer--attributes-table', [], "", ['pageLength' => -1]);
+ $oTablePanel->AddSubBlock($oAttributesTable);
+ $oPage->AddUiBlock($oTablePanel);
+ $sOrigins = json_encode(array_keys($aClassInformation['origins']));
+
+ //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).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("ibo-datamodel-viewer--origin-cell");
+ });
+
+EOF
+ );
}
/**
@@ -222,11 +444,18 @@ JS
/**
* Helper for the trigger
*/
-function DisplayTriggers($oPage, $sClass)
+function DisplayTriggers(iTopWebPage $oPage, string $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, ['block_id' => 'triggers']);
+ try {
+ cmdbAbstractObject::DisplaySet($oPage, $oSet, ['block_id' => 'triggers']);
+ } catch (Exception $e) {
+ $oPage->AddUiBlock(AlertUIBlockFactory::MakeForFailure('Unable to load current class triggers.'));
+ IssueLog::Error('Unable to load current class triggers.', null, [
+ 'root_cause' => $e->getMessage(),
+ ]);
+ }
}
function DisplayEvents(WebPage $oPage, $sClass)
@@ -333,7 +562,7 @@ function DisplayClassesList($oPage, $oLayout, $sContext)
$oLayout->AddSideHtml("
");
$oListSearch = new Select("ibo-datamodel-viewer--class-search");
- $oListSearch->SetName('aa');
+ $oListSearch->SetName('datamodel_class_select');
// Get all the "root" classes for display
$aRootClasses = [];
$aClassLabelAndCodeAsJSON = [];
@@ -897,156 +1126,38 @@ JS
* @param string $sClass
* @param string $sContext
*
- * @throws \CoreException
+ * @throws CoreException|ReflectionException
*/
function DisplayClassDetails($oPage, $sClass, $sContext)
{
- $aParentClasses = [];
- foreach (MetaModel::EnumParentClasses($sClass) as $sParentClass) {
- $aParentClasses[] = MakeClassHLink($sParentClass, $sContext);
- }
- if (count($aParentClasses) > 0) {
- $sParents = implode(' ', $aParentClasses).' '.MetaModel::GetName($sClass).'('.$sClass.')';
- } else {
- $sParents = '';
- }
- $sClassHierarchy = ("[".Dict::S('UI:Schema:AllClasses')."] $sParents");
+ // Hierarchy
+ $sClassHierarchy = GetClassHierarchy($sClass, $sContext);
+ $oPage->AddUiBlock(HtmlFactory::MakeRaw($sClassHierarchy));
- $oPanel = PanelUIBlockFactory::MakeForClass($sClass, MetaModel::GetName($sClass).' ('.$sClass.')')
- ->SetIcon(MetaModel::GetClassIcon($sClass, false));
- $sClassDescritpion = MetaModel::GetClassDescription($sClass);
+ // Class Title
+ $oPanel = PanelUIBlockFactory::MakeForClass($sClass, MetaModel::GetName($sClass).' ('.$sClass.')')->SetIcon(MetaModel::GetClassIcon($sClass, false));
$oEnhancedPanelSubtitle = $oPanel->GetSubTitleBlock();
- $sEnhancedPanelSubtitle = $sClassHierarchy.($sClassDescritpion == "" ? "" : ' - '.$sClassDescritpion);
- if (MetaModel::IsAbstract($sClass)) {
- $sEnhancedPanelSubtitle .= ' - ';
+ $sClassDescription = MetaModel::GetClassDescription($sClass);
+ $sTags = '';
+ if (utils::IsNotNullOrEmptyString($sClassDescription)) {
+ $sTags .= ''.$sClassDescription.'';
}
- $oEnhancedPanelSubtitle->AddHtml($sEnhancedPanelSubtitle);
+ $sTags .= ''.str_replace(',', ' - ', MetaModel::GetCategory($sClass)).'';
+ if (MetaModel::IsAbstract($sClass)) {
+ $sTags .= ''.Dict::S('UI:Schema:AbstractClass').'';
+ }
+ $oEnhancedPanelSubtitle->AddSubBlock(HtmlFactory::MakeHtmlContent($sTags));
$oPage->AddUiBlock($oPanel);
+
+ // Details container
$oPage->AddTabContainer('details', '', $oPanel);
$oPage->SetCurrentTabContainer('details');
- // List the attributes of the object
- $aForwardChangeTracking = MetaModel::GetTrackForwardExternalKeys($sClass);
- $aDetails = [];
- $aOrigins = [];
- 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 = '';
- if (call_user_func([get_class($oAttDef), 'IsBasedOnDBColumns'])) {
-
- $aMoreInfo = [];
- 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) ? 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, []);
- $aDescription = [];
- 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 = utils::HtmlEntities($sAllowedValues);
-
- $sFilterURL = urlencode($sAllowedValues);
- $sAllowedValues = ' '.Dict::S('UI:Schema:Attribute/Filter')."";
- } else {
- $sAllowedValues = '';
- }
- $sAttrValueEscpd = utils::HtmlEntities($sValue);
- $sAttrTypeDescEscpd = utils::HtmlEntities($sTypeDesc);
- $sAttrOriginEscpd = utils::HtmlEntities($sOrigin);
- $sDefaultNullValueEscpd = utils::HtmlEntities($sDefaultNullValue);
-
- $aDetails[] = [
- 'code' => ''.$oAttDef->GetLabel().' ('.$oAttDef->GetCode().')',
- 'type' => ''.$sTypeDict.' ('.$sType.')',
- 'origincolor' => '',
- 'origin' => "$sOrigin",
- 'values' => $sAllowedValues,
- 'moreinfo' => ''.$sMoreInfo.'',
- ];
-
- }
+ // Attributes
$oPage->SetCurrentTab('UI:Schema:Attributes');
- $aConfig = [
- 'origincolor' => ['label' => "", 'description' => ""],
- 'code' => ['label' => Dict::S('UI:Schema:AttributeCode'), 'description' => Dict::S('UI:Schema:AttributeCode+')],
- 'type' => ['label' => Dict::S('UI:Schema:Type'), 'description' => Dict::S('UI:Schema:Type+')],
- 'values' => ['label' => Dict::S('UI:Schema:AllowedValues'), 'description' => Dict::S('UI:Schema:AllowedValues+')],
- 'moreinfo' => ['label' => Dict::S('UI:Schema:MoreInfo'), 'description' => Dict::S('UI:Schema:MoreInfo+')],
- 'origin' => ['label' => Dict::S('UI:Schema:Origin'), 'description' => Dict::S('UI:Schema:Origin+')],
- ];
- $oTablePanel = PanelUIBlockFactory::MakeForClass($sClass, '');
- $oTablePanel->AddCSSClass('ibo-datatable-panel');
-
- $oAttributesTable = DataTableUIBlockFactory::MakeForStaticData('', $aConfig, $aDetails, 'ibo-datamodel-viewer--attributes-table', [], "", ['pageLength' => -1]);
- $oTablePanel->AddSubBlock($oAttributesTable);
- $oPage->AddUiBlock($oTablePanel);
- $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).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("ibo-datamodel-viewer--origin-cell");
- });
-
-EOF
- );
+ DisplayClassAttributes($oPage, $sClass, $sContext);
+ // Related classes
$oPage->SetCurrentTab('UI:Schema:RelatedClasses');
DisplayRelatedClassesGraph($oPage, $sClass);
@@ -1060,12 +1171,15 @@ EOF
$oPage->add_ready_script('$("#ClassHierarchy").treeview({collapsed: false,});');
}
+ // Lifecycle
$oPage->SetCurrentTab('UI:Schema:LifeCycle');
DisplayLifecycle($oPage, $sClass);
+ // Triggers
$oPage->SetCurrentTab('UI:Schema:Triggers');
DisplayTriggers($oPage, $sClass);
+ // Events
$oPage->SetCurrentTab('UI:Schema:Events');
DisplayEvents($oPage, $sClass);
@@ -1100,7 +1214,6 @@ $oPage->SetBreadCrumbEntry(
);
$oTitle = TitleUIBlockFactory::MakeForPage(Dict::S('UI:Schema:Title'));
-$oPage->AddUiBlock($oTitle);
$oLayout->AddSideHtml(" ");
DisplayClassesList($oPage, $oLayout, $sContext);
$oLayout->AddSideHtml("
");
@@ -1118,6 +1231,11 @@ switch ($operation) {
}
// no break
default:
+ $sSource = APPROOT."images/illustrations/undraw_reading_time.svg";
+ $oEmpty = HtmlFactory::MakeRaw("".
+ file_get_contents($sSource)."
".Dict::S('UI:Schema:NoClassSelected')."
");
+ $oLayout->AddSubBlock($oEmpty);
+ break;
}
$oPage->add("");
$oPage->add("");
diff --git a/sources/Application/UI/Base/Component/DataTable/DataTableUIBlockFactory.php b/sources/Application/UI/Base/Component/DataTable/DataTableUIBlockFactory.php
index 01e1acab80..f061f841e5 100644
--- a/sources/Application/UI/Base/Component/DataTable/DataTableUIBlockFactory.php
+++ b/sources/Application/UI/Base/Component/DataTable/DataTableUIBlockFactory.php
@@ -27,9 +27,9 @@ use Combodo\iTop\Application\UI\Base\Component\Title\TitleUIBlockFactory;
use Combodo\iTop\Application\UI\Base\Component\Toolbar\ToolbarUIBlockFactory;
use Combodo\iTop\Application\UI\Base\iUIBlock;
use Combodo\iTop\Application\UI\Base\Layout\UIContentBlock;
+use Combodo\iTop\Application\WebPage\WebPage;
use Combodo\iTop\Controller\AjaxRenderController;
use DBObjectSet;
-use DeprecatedCallsLog;
use Dict;
use DisplayBlock;
use IssueLog;
@@ -38,7 +38,6 @@ use MenuBlock;
use MetaModel;
use UserRights;
use utils;
-use Combodo\iTop\Application\WebPage\WebPage;
/**
* Class DataTableUIBlockFactory
@@ -556,7 +555,7 @@ class DataTableUIBlockFactory extends AbstractUIBlockFactory
'object_class' => $sClassName,
'class_alias' => $sClassAlias,
'attribute_code' => $sAttCode,
- 'attribute_type' => $sAttDefClass,
+ 'attribute_type' => $oAttDef->GetType(),
'attribute_label' => $sAttLabel,
'render' => $oAttDef->GetRenderForDataTable($sClassAlias),
];
@@ -738,7 +737,7 @@ JS;
'object_class' => $sClassName,
'class_alias' => $sClassAlias,
'attribute_code' => $sAttCode,
- 'attribute_type' => $sAttDefClass,
+ 'attribute_type' => $oAttDef->GetType(),
'attribute_label' => $sAttLabel,
];
$aColumnDefinition["data"] = $sClassAlias."/".$sAttCode;
diff --git a/sources/Core/AttributeDefinition/AttributeBoolean.php b/sources/Core/AttributeDefinition/AttributeBoolean.php
index 0be6dafa69..68e720c8ee 100644
--- a/sources/Core/AttributeDefinition/AttributeBoolean.php
+++ b/sources/Core/AttributeDefinition/AttributeBoolean.php
@@ -11,6 +11,7 @@ use CMDBChangeOpSetAttributeScalar;
use Combodo\iTop\Form\Field\SelectField;
use DBObject;
use Dict;
+use ReflectionClass;
/**
* Map a boolean column to an attribute
@@ -80,11 +81,13 @@ class AttributeBoolean extends AttributeInteger
public function GetValueLabel($bValue)
{
+ $oClass = new ReflectionClass(get_class($this));
+
if (is_null($bValue)) {
- $sLabel = Dict::S('Core:'.get_class($this).'/Value:null');
+ $sLabel = Dict::S('Core:'.$oClass->getShortName().'/Value:null');
} else {
$sValue = $bValue ? 'yes' : 'no';
- $sDefault = Dict::S('Core:'.get_class($this).'/Value:'.$sValue);
+ $sDefault = Dict::S('Core:'.$oClass->getShortName().'/Value:'.$sValue);
$sLabel = $this->SearchLabel('/Attribute:'.$this->m_sCode.'/Value:'.$sValue, $sDefault, true /*user lang*/);
}
@@ -93,11 +96,13 @@ class AttributeBoolean extends AttributeInteger
public function GetValueDescription($bValue)
{
+ $oClass = new ReflectionClass(get_class($this));
+
if (is_null($bValue)) {
- $sDescription = Dict::S('Core:'.get_class($this).'/Value:null+');
+ $sDescription = Dict::S('Core:'.$oClass->getShortName().'/Value:null+');
} else {
$sValue = $bValue ? 'yes' : 'no';
- $sDefault = Dict::S('Core:'.get_class($this).'/Value:'.$sValue.'+');
+ $sDefault = Dict::S('Core:'.$oClass->getShortName().'/Value:'.$sValue.'+');
$sDescription = $this->SearchLabel(
'/Attribute:'.$this->m_sCode.'/Value:'.$sValue.'+',
$sDefault,
diff --git a/sources/Core/AttributeDefinition/AttributeDefinition.php b/sources/Core/AttributeDefinition/AttributeDefinition.php
index 0d46996370..359d5c7de0 100644
--- a/sources/Core/AttributeDefinition/AttributeDefinition.php
+++ b/sources/Core/AttributeDefinition/AttributeDefinition.php
@@ -19,6 +19,7 @@ use Exception;
use Expression;
use FieldExpression;
use MetaModel;
+use ReflectionClass;
use Str;
use utils;
use VariableExpression;
@@ -131,12 +132,30 @@ abstract class AttributeDefinition
public function GetType()
{
- return Dict::S('Core:'.get_class($this));
+ $oClass = new ReflectionClass(get_class($this));
+ return Dict::S('Core:'.$oClass->getShortName());
}
public function GetTypeDesc()
{
- return Dict::S('Core:'.get_class($this).'+');
+ $oClass = new ReflectionClass(get_class($this));
+ return Dict::S('Core:'.$oClass->getShortName().'+');
+ }
+
+ /**
+ * Return type information.
+ *
+ * @since 3.3
+ * @return array
+ */
+ public function GetTypeInformation(): array
+ {
+ $oClass = new ReflectionClass(get_class($this));
+ return [
+ 'classShortName' => $oClass->getShortName(),
+ 'label' => Dict::S('Core:'.$oClass->getShortName()),
+ 'description' => Dict::S('Core:'.$oClass->getShortName().'+'),
+ ];
}
abstract public function GetEditClass();
@@ -1076,7 +1095,7 @@ abstract class AttributeDefinition
// Metadata
$oFormField->AddMetadata('attribute-code', $this->GetCode());
- $oFormField->AddMetadata('attribute-type', get_class($this));
+ $oFormField->AddMetadata('attribute-type', $this->GetType());
$oFormField->AddMetadata('attribute-label', $this->GetLabel());
// - Attribute flags
$aPossibleAttFlags = MetaModel::EnumPossibleAttributeFlags();
diff --git a/sources/Renderer/Bootstrap/FieldRenderer/BsLinkedSetFieldRenderer.php b/sources/Renderer/Bootstrap/FieldRenderer/BsLinkedSetFieldRenderer.php
index 3bc3b39f09..59553b6b60 100644
--- a/sources/Renderer/Bootstrap/FieldRenderer/BsLinkedSetFieldRenderer.php
+++ b/sources/Renderer/Bootstrap/FieldRenderer/BsLinkedSetFieldRenderer.php
@@ -852,7 +852,7 @@ JS
'object_class' => $sClass,
'object_id' => $oItem->GetKey(),
'attribute_code' => $sAttCode,
- 'attribute_type' => get_class($oAttDef),
+ 'attribute_type' => $oAttDef->GetType(),
];
// - Value raw
// For simple fields, we get the raw (stored) value as well