diff --git a/setup/modulediscovery.class.inc.php b/setup/modulediscovery.class.inc.php index bb642eb8d..fc86576d4 100644 --- a/setup/modulediscovery.class.inc.php +++ b/setup/modulediscovery.class.inc.php @@ -76,7 +76,7 @@ class ModuleDiscovery 'doc.manual_setup' => 'url', 'doc.more_information' => 'url', ); - + // Cache the results and the source directories protected static $m_aSearchDirs = null; @@ -148,7 +148,7 @@ class ModuleDiscovery self::$m_aModuleVersionByName[$sModuleName]['version'] = $sModuleVersion; self::$m_aModuleVersionByName[$sModuleName]['id'] = $sId; } - + self::$m_aModules[$sId] = $aArgs; // Now keep the relative paths, as provided @@ -220,8 +220,8 @@ class ModuleDiscovery public static function OrderModulesByDependencies($aModules, $bAbortOnMissingDependency = false, $aModulesToLoad = null) { // Order the modules to take into account their inter-dependencies - $aDependencies = array(); - $aSelectedModules = array(); + $aDependencies = []; + $aSelectedModules = []; foreach($aModules as $sId => $aModule) { list($sModuleName, ) = self::GetModuleName($sId); @@ -232,7 +232,7 @@ class ModuleDiscovery } } ksort($aDependencies); - $aOrderedModules = array(); + $aOrderedModules = []; $iLoopCount = 1; while(($iLoopCount < count($aModules)) && (count($aDependencies) > 0) ) { @@ -256,13 +256,24 @@ class ModuleDiscovery } if ($bAbortOnMissingDependency && count($aDependencies) > 0) { - $aModulesInfo = array(); - $aModuleDeps = array(); + $aModulesInfo = []; + $aModuleDeps = []; foreach($aDependencies as $sId => $aDeps) { $aModule = $aModules[$sId]; - $aModuleDeps[] = "{$aModule['label']} (id: $sId) depends on: ".implode(' + ', $aDeps); - $aModulesInfo[$sId] = array('module' => $aModule, 'dependencies' => $aDeps); + $aDepsWithIcons = []; + foreach($aDeps as $sIndex => $sDepId) + { + if (self::DependencyIsResolved($sDepId, $aOrderedModules, $aSelectedModules)) + { + $aDepsWithIcons[$sIndex] = '✅ ' . $sDepId; + } else + { + $aDepsWithIcons[$sIndex] = '❌ ' . $sDepId; + } + } + $aModuleDeps[] = "{$aModule['label']} (id: $sId) depends on: ".implode(' + ', $aDepsWithIcons); + $aModulesInfo[$sId] = array('module' => $aModule, 'dependencies' => $aDepsWithIcons); } $sMessage = "The following modules have unmet dependencies:\n".implode(",\n", $aModuleDeps); $oException = new MissingDependencyException($sMessage); @@ -289,7 +300,7 @@ class ModuleDiscovery // The de-duplication is now done directly by the AddModule method return $aModules; } - + protected static function DependencyIsResolved($sDepString, $aOrderedModules, $aSelectedModules) { $bResult = false; @@ -336,12 +347,12 @@ class ModuleDiscovery if (version_compare($sCurrentVersion, $sExpectedVersion, $sOperator)) { $aReplacements[$sModuleId] = '(true)'; // Add parentheses to protect against invalid condition causing - // a function call that results in a runtime fatal error + // a function call that results in a runtime fatal error } else { $aReplacements[$sModuleId] = '(false)'; // Add parentheses to protect against invalid condition causing - // a function call that results in a runtime fatal error + // a function call that results in a runtime fatal error } } else @@ -400,20 +411,20 @@ class ModuleDiscovery { self::ResetCache(); } - + if (is_null(self::$m_aSearchDirs)) { self::$m_aSearchDirs = $aSearchDirs; - + // Not in cache, let's scan the disk foreach($aSearchDirs as $sSearchDir) { - $sLookupDir = realpath($sSearchDir); + $sLookupDir = realpath($sSearchDir); if ($sLookupDir == '') { throw new Exception("Invalid directory '$sSearchDir'"); } - + clearstatcache(); self::ListModuleFiles(basename($sSearchDir), dirname($sSearchDir)); } @@ -425,7 +436,7 @@ class ModuleDiscovery return self::GetModules($bAbortOnMissingDependency, $aModulesToLoad); } } - + public static function ResetCache() { self::$m_aSearchDirs = null; @@ -437,7 +448,7 @@ class ModuleDiscovery * Helper function to interpret the name of a module * @param $sModuleId string Identifier of the module, in the form 'name/version' * @return array(name, version) - */ + */ public static function GetModuleName($sModuleId) { $aMatches = array(); @@ -466,7 +477,7 @@ class ModuleDiscovery { static $iDummyClassIndex = 0; $sDirectory = $sRootDir.'/'.$sRelDir; - + if ($hDir = opendir($sDirectory)) { // This is the correct way to loop over the directory. (according to the documentation) @@ -502,12 +513,12 @@ class ModuleDiscovery $idx++; } $bRet = eval($sModuleFileContents); - + if ($bRet === false) { SetupLog::Warning("Eval of $sRelDir/$sFile returned false"); } - + //echo "

Done.

\n"; } catch(ParseError $e) @@ -535,7 +546,7 @@ class ModuleDiscovery /** Alias for backward compatibility with old module files in which * the declaration of a module invokes SetupWebPage::AddModule() * whereas the new form is ModuleDiscovery::AddModule() - */ + */ class SetupWebPage extends ModuleDiscovery { // For backward compatibility with old modules... @@ -562,9 +573,9 @@ class SetupWebPage extends ModuleDiscovery public static function log($sText) { SetupLog::Ok($sText); - } + } } - + /** Ugly patch !!! * In order to be able to analyse / load several times * the same module file, we rename the class (to avoid duplicate class definitions) diff --git a/sources/Application/UI/Base/Component/DataTable/DataTableUIBlockFactory.php b/sources/Application/UI/Base/Component/DataTable/DataTableUIBlockFactory.php index 7501ecf1f..ba210bee9 100644 --- a/sources/Application/UI/Base/Component/DataTable/DataTableUIBlockFactory.php +++ b/sources/Application/UI/Base/Component/DataTable/DataTableUIBlockFactory.php @@ -1,6 +1,6 @@ - * @package UIBlockAPI + * @package UIBlockExtensibilityAPI * @api * @since 3.0.0 */ @@ -78,11 +70,7 @@ class DataTableUIBlockFactory extends AbstractUIBlockFactory public static function MakeForResult(WebPage $oPage, string $sListId, DBObjectSet $oSet, $aExtraParams = array()) { $oDataTable = DataTableUIBlockFactory::MakeForRendering($sListId, $oSet, $aExtraParams); - if ($oPage->IsPrintableVersion()) { - $oDataTable->AddOption('printVersion', true); - } - - return self::RenderDataTable($oDataTable, DisplayBlock::ENUM_STYLE_LIST, $oPage, $sListId, $oSet, $aExtraParams); + return self::RenderDataTable($oDataTable, 'list', $oPage, $sListId, $oSet, $aExtraParams); } /** @@ -107,12 +95,12 @@ class DataTableUIBlockFactory extends AbstractUIBlockFactory */ public static function MakeForObject(WebPage $oPage, string $sListId, DBObjectSet $oSet, $aExtraParams = array()) { - $oDataTable = DataTableUIBlockFactory::MakeForRendering($sListId, $oSet, $aExtraParams); + $oDataTable = DataTableUIBlockFactory::MakeForRenderingObject($sListId, $oSet, $aExtraParams); if ($oPage->IsPrintableVersion()) { $oDataTable->AddOption('printVersion', true); } - return self::RenderDataTable($oDataTable, DisplayBlock::ENUM_STYLE_LIST_IN_OBJECT, $oPage, $sListId, $oSet, $aExtraParams); + return self::RenderDataTable($oDataTable, 'listInObject', $oPage, $sListId, $oSet, $aExtraParams); } /** @@ -141,13 +129,7 @@ class DataTableUIBlockFactory extends AbstractUIBlockFactory */ protected static function RenderDataTable(DataTable $oDataTable, string $sStyle, WebPage $oPage, string $sListId, DBObjectSet $oSet, array $aExtraParams) { - // Filter this list action - $sFilterListUrl = utils::GetDataTableSearchUrl($oSet->GetFilter(), $aExtraParams); - if (utils::IsNotNullOrEmptyString($sFilterListUrl)) { - $aExtraParams['filter_this_list_url'] = $sFilterListUrl; - } - - if (!isset($aExtraParams['menu']) || $aExtraParams['menu'] === "1" || $aExtraParams['menu'] === true) { + if (!isset($aExtraParams['menu']) || $aExtraParams['menu']) { $oMenuBlock = new MenuBlock($oSet->GetFilter(), $sStyle); $aExtraParams['refresh_action'] = $oDataTable->GetJSRefresh(); $oBlockMenu = $oMenuBlock->GetRenderContent($oPage, $aExtraParams, $sListId); @@ -161,6 +143,7 @@ class DataTableUIBlockFactory extends AbstractUIBlockFactory $bToolkitMenu = false; } if ($bToolkitMenu) { + $aExtraParams['selection_mode'] = true; $oMenuBlock = new MenuBlock($oSet->GetFilter(), $sStyle); $oBlockMenu = $oMenuBlock->GetRenderContent($oPage, $aExtraParams, $sListId); } else { @@ -168,50 +151,33 @@ class DataTableUIBlockFactory extends AbstractUIBlockFactory } } - // Default behavor, surrond table with a panel for better display if (!isset($aExtraParams['surround_with_panel']) || $aExtraParams['surround_with_panel']) { - $oContainer = PanelUIBlockFactory::MakeForClass($oSet->GetClass(), '') - ->AddCSSClass('ibo-datatable-panel'); - - // Panel title - if (isset($aExtraParams['panel_title'])) { - if (isset($aExtraParams['panel_title_is_html']) && $aExtraParams['panel_title_is_html'] === true) { - $oContainer->AddTitleBlock(HtmlFactory::MakeRaw($aExtraParams['panel_title'])); - } else { - $oContainer->SetTitle($aExtraParams['panel_title']); - } - } - // - Description - if (isset($aExtraParams['panel_title_tooltip'])) { - $oContainerTitleBlock = $oContainer->GetTitleBlock() - ->AddDataAttribute('tooltip-content', $aExtraParams['panel_title_tooltip']) - ->AddDataAttribute('tooltip-max-width', 'min(600px, 90vw)') // Allow big description to be wide enough while shrinking on small screens - ->AddCSSClass('ibo-has-description'); - } - - // Panel subtitle if(!empty($oDataTable->GetInitDisplayData()) && isset($oDataTable->GetInitDisplayData()['recordsTotal'])){ $iCount = $oDataTable->GetInitDisplayData()['recordsTotal']; } else { $iCount = $oSet->Count(); } - $sCountHtml = ''.$iCount.''; - if ($oDataTable->GetOption('select_mode') === 'multiple') { - $sSubTitle = Dict::Format('UI:Pagination:HeaderSelection', $sCountHtml, '0'); - } else { - $sSubTitle = Dict::Format('UI:Pagination:HeaderNoSelection', $sCountHtml); + $oContainer = PanelUIBlockFactory::MakeForClass($oSet->GetClass(), '')->AddCSSClass('ibo-datatable-panel'); + if(isset($aExtraParams['panel_title'])){ + if(isset($aExtraParams['panel_title_is_html']) && $aExtraParams['panel_title_is_html'] === true) { + $oContainer->AddTitleBlock(HtmlFactory::MakeRaw($aExtraParams['panel_title'])); + } + else { + $oContainer->SetTitle($aExtraParams['panel_title']); + } } - - if (utils::IsNotNullOrEmptyString($sFilterListUrl)) { - $sSubTitle = ''.$sSubTitle.''; + if ($oDataTable->GetOption("select_mode") == 'multiple') + { + $sSubTitle =Dict::Format('UI:Pagination:HeaderSelection', ''.$iCount.'', '0'); + } + else + { + $sSubTitle = Dict::Format('UI:Pagination:HeaderNoSelection', ''.$iCount.''); } $oContainer->AddSubTitleBlock(new Html($sSubTitle)); - - // Panel icon - if (isset($aExtraParams['panel_icon']) && strlen($aExtraParams['panel_icon']) > 0) { - $oContainer->SetIcon($aExtraParams['panel_icon']); + if(isset($aExtraParams["panel_icon"]) && strlen($aExtraParams["panel_icon"]) > 0){ + $oContainer->SetIcon($aExtraParams["panel_icon"]); } - $oContainer->AddToolbarBlock($oBlockMenu); $oContainer->AddMainBlock($oDataTable); } else { @@ -225,58 +191,6 @@ class DataTableUIBlockFactory extends AbstractUIBlockFactory return $oContainer; } - /** - * Make a row actions toolbar template. - * - * @param iUIBlock $oTable datatable object that needs to use tTableRowActions trait - * - * @return \Combodo\iTop\Application\UI\Base\Component\Template\Template - * @throws \Exception - * @since 3.1.0 - */ - public static function MakeActionRowToolbarTemplate(iUIBlock $oTable) - { - // test trait - $sTableClass = get_class($oTable); - if (!utils::IsTraitUsedByClass(tTableRowActions::class, $sTableClass)) { - throw new \Exception("DataTableUIBlockFactory::MakeActionRowToolbarTemplate: {$sTableClass} iUIBlock needs tTableRowActions trait"); - } - - // row actions template - $oTemplate = TemplateUIBlockFactory::MakeStandard($oTable->GetId().'_actions_buttons_template'); - - // row actions toolbar container - $oToolbar = ToolbarUIBlockFactory::MakeStandard(); - $oToolbar->AddCSSClass('ibo-datatable--row-actions-toolbar'); - - // for each action...create an icon button - foreach ($oTable->GetRowActions() as $iKey => $aAction) { - $oButton = ButtonUIBlockFactory::MakeIconAction( - array_key_exists('icon_classes', $aAction) ? $aAction['icon_classes'] : 'fas fa-question', - array_key_exists('tooltip', $aAction) ? Dict::S($aAction['tooltip']) : '', - array_key_exists('name', $aAction) ? $aAction['name'] : 'undefined' - ); - if (array_key_exists('color', $aAction)) { - $oButton->SetColor($aAction['color']); - } - $oButton->SetDataAttributes(['label' => Dict::S($aAction['label']), 'action-id' => $iKey, 'table-id' => $oTable->GetId()]); - if (array_key_exists('metadata', $aAction)) { - $aMetadata = $aAction['metadata']; - if (is_array($aMetadata)) { - foreach ($aMetadata as $key => $value) { - $oButton->AddDataAttribute($key, $value); - } - } - } - - $oToolbar->AddSubBlock($oButton); - } - - $oTemplate->AddSubBlock($oToolbar); - - return $oTemplate; - } - /** * Make a basis Panel component * @@ -296,14 +210,11 @@ class DataTableUIBlockFactory extends AbstractUIBlockFactory public static function MakeForRendering(string $sListId, DBObjectSet $oSet, $aExtraParams = array()) { $oDataTable = new DataTable('datatable_'.$sListId); - $aLists = array(); + + $oAppRoot = utils::GetAbsoluteUrlAppRoot(); // Initialize and check the parameters $bViewLink = isset($aExtraParams['view_link']) ? $aExtraParams['view_link'] : true; - // Check if there is a list of aliases to limit the display to... - $aDisplayAliases = isset($aExtraParams['display_aliases']) ? explode(',', $aExtraParams['display_aliases']) : array(); - $sZListName = isset($aExtraParams['zlist']) ? ($aExtraParams['zlist']) : 'list'; - $sLinkageAttribute = isset($aExtraParams['link_attr']) ? $aExtraParams['link_attr'] : ''; $iLinkedObjectId = isset($aExtraParams['object_id']) ? $aExtraParams['object_id'] : 0; $sTargetAttr = isset($aExtraParams['target_attr']) ? $aExtraParams['target_attr'] : ''; @@ -317,121 +228,90 @@ class DataTableUIBlockFactory extends AbstractUIBlockFactory throw new ApplicationException(Dict::S('UI:Error:MandatoryTemplateParameter_target_attr')); } } + $bSelectMode = isset($aExtraParams['selection_mode']) ? $aExtraParams['selection_mode'] == true : false; + $bSingleSelectMode = isset($aExtraParams['selection_type']) ? ($aExtraParams['selection_type'] == 'single') : false; $aExtraFieldsRaw = isset($aExtraParams['extra_fields']) ? explode(',', trim($aExtraParams['extra_fields'])) : array(); $aExtraFields = array(); - $sAttCode = ''; foreach ($aExtraFieldsRaw as $sFieldName) { // Ignore attributes not of the main queried class if (preg_match('/^(.*)\.(.*)$/', $sFieldName, $aMatches)) { $sClassAlias = $aMatches[1]; $sAttCode = $aMatches[2]; - if (array_key_exists($sClassAlias, $oSet->GetSelectedClasses())) { - $aExtraFields[$sClassAlias][] = $sAttCode; + if ($sClassAlias == $oSet->GetFilter()->GetClassAlias()) {//$oSet->GetFilter()->GetSelectedClasses() + $aExtraFields[] = $sAttCode; } } else { - $aExtraFields['*'][] = $sFieldName; + $aExtraFields[] = $sFieldName; + } + } + $sClassName = $oSet->GetFilter()->GetClass(); + $sZListName = isset($aExtraParams['zlist']) ? ($aExtraParams['zlist']) : 'list'; + if ($sZListName !== false) { + $aList = cmdbAbstractObject::FlattenZList(MetaModel::GetZListItems($sClassName, $sZListName)); + $aList = array_merge($aList, $aExtraFields); + } else { + $aList = $aExtraFields; + } + + // Filter the list to removed linked set since we are not able to display them here + foreach ($aList as $index => $sAttCode) { + $oAttDef = MetaModel::GetAttributeDef($sClassName, $sAttCode); + if ($oAttDef instanceof AttributeLinkedSet) { + // Removed from the display list + unset($aList[$index]); } } - $aClassAliases = $oSet->GetFilter()->GetSelectedClasses(); - $aAuthorizedClasses = array(); - foreach ($aClassAliases as $sAlias => $sClassName) { - if ((UserRights::IsActionAllowed($sClassName, UR_ACTION_READ, $oSet) != UR_ALLOWED_NO) && - ((count($aDisplayAliases) == 0) || (in_array($sAlias, $aDisplayAliases)))) { - $aAuthorizedClasses[$sAlias] = $sClassName; - } - } - foreach ($aAuthorizedClasses as $sAlias => $sClassName) { - // In case there is only 1 "alias" for the extra fields and it is the fallback ("*"), then consider that all fields are for the current alias. - // This is for the particular use case when the zlist is set to false and extra fields are specified. - if ( (count($aExtraFields) === 1) && (array_keys($aExtraFields)[0] === '*') ) { - $aLists[$sAlias] = $aExtraFields['*']; - } - // Regular use case, dispatch fields to their corresponding aliases - else if (array_key_exists($sAlias, $aExtraFields)) { - $aLists[$sAlias] = $aExtraFields[$sAlias]; - } - // Finally, if unknown alias, ignore fields - else { - $aLists[$sAlias] = array(); - } - - // If zlist specified, merge its fields with the currently present - if ($sZListName !== false) { - $aDefaultList = MetaModel::FlattenZList(MetaModel::GetZListItems($sClassName, $sZListName)); - $aLists[$sAlias] = array_merge($aDefaultList, $aLists[$sAlias]); - } - - // Filter the list to removed linked set since we are not able to display them here - foreach ($aLists[$sAlias] as $index => $sAttCode) { - $oAttDef = MetaModel::GetAttributeDef($sClassName, $sAttCode); - if ($oAttDef instanceof AttributeLinkedSet) { - // Removed from the display list - unset($aLists[$sAlias][$index]); + if (!empty($sLinkageAttribute)) { + // The set to display is in fact a set of links between the object specified in the $sLinkageAttribute + // and other objects... + // The display will then group all the attributes related to the link itself: + // | Link_attr1 | link_attr2 | ... || Object_attr1 | Object_attr2 | Object_attr3 | .. | Object_attr_n | + $aDisplayList = array(); + $aAttDefs = MetaModel::ListAttributeDefs($sClassName); + assert(isset($aAttDefs[$sLinkageAttribute])); + $oAttDef = $aAttDefs[$sLinkageAttribute]; + assert($oAttDef->IsExternalKey()); + // First display all the attributes specific to the link record + foreach ($aList as $sLinkAttCode) { + $oLinkAttDef = $aAttDefs[$sLinkAttCode]; + if ((!$oLinkAttDef->IsExternalKey()) && (!$oLinkAttDef->IsExternalField())) { + $aDisplayList[] = $sLinkAttCode; } } - - if (empty($aLists[$sAlias])) { - unset($aLists[$sAlias], $aAuthorizedClasses[$sAlias]); - } - - // Only for main class - if (!empty($sLinkageAttribute) && $sClassName === $oSet->GetFilter()->GetClass()) { - // The set to display is in fact a set of links between the object specified in the $sLinkageAttribute - // and other objects... - // The display will then group all the attributes related to the link itself: - // | Link_attr1 | link_attr2 | ... || Object_attr1 | Object_attr2 | Object_attr3 | .. | Object_attr_n | - $aDisplayList = array(); - $aAttDefs = MetaModel::ListAttributeDefs($sClassName); - assert(isset($aAttDefs[$sLinkageAttribute])); - $oAttDef = $aAttDefs[$sLinkageAttribute]; - assert($oAttDef->IsExternalKey()); - // First display all the attributes specific to the link record - foreach ($aLists[$sAlias] as $sLinkAttCode) { - $oLinkAttDef = $aAttDefs[$sLinkAttCode]; - if ((!$oLinkAttDef->IsExternalKey()) && (!$oLinkAttDef->IsExternalField())) { - $aDisplayList[] = $sLinkAttCode; - } + // Then display all the attributes neither specific to the link record nor to the 'linkage' object (because the latter are constant) + foreach ($aList as $sLinkAttCode) { + $oLinkAttDef = $aAttDefs[$sLinkAttCode]; + if (($oLinkAttDef->IsExternalKey() && ($sLinkAttCode != $sLinkageAttribute)) + || ($oLinkAttDef->IsExternalField() && ($oLinkAttDef->GetKeyAttCode() != $sLinkageAttribute))) { + $aDisplayList[] = $sLinkAttCode; } - // Then display all the attributes neither specific to the link record nor to the 'linkage' object (because the latter are constant) - foreach ($aLists[$sAlias] as $sLinkAttCode) { - $oLinkAttDef = $aAttDefs[$sLinkAttCode]; - if (($oLinkAttDef->IsExternalKey() && ($sLinkAttCode != $sLinkageAttribute)) - || ($oLinkAttDef->IsExternalField() && ($oLinkAttDef->GetKeyAttCode() != $sLinkageAttribute))) { - $aDisplayList[] = $sLinkAttCode; - } - } - // First display all the attributes specific to the link - // Then display all the attributes linked to the other end of the relationship - $aLists[$sAlias] = $aDisplayList; } + // First display all the attributes specific to the link + // Then display all the attributes linked to the other end of the relationship + $aList = $aDisplayList; } - // N°6356 Check if there is at least 1 class remaining to display - if (count($aLists) === 0) { - IssueLog::Debug('Could not find any class / attribute to display in the list. Did you ensure the selected classes have the requested zlist? As a fallback, we will just display the friendlyname for the first selected class.', LogChannels::DATATABLE, [ - 'selected_classes' => $aClassAliases, - 'zlist' => $sZListName, - ]); - - $sFirstClassAlias = array_keys($aClassAliases)[0]; - $aAuthorizedClasses[$sFirstClassAlias] = $aClassAliases[$sFirstClassAlias]; - $aLists[$sFirstClassAlias] = []; + $sSelectMode = ''; + if ($bSelectMode) { + $sSelectMode = $bSingleSelectMode ? 'single' : 'multiple'; } - $oDefaultSettings = DataTableSettings::GetDataModelSettings($aAuthorizedClasses, $bViewLink, $aLists); - + $sClassAlias = $oSet->GetClassAlias(); $bDisplayLimit = isset($aExtraParams['display_limit']) ? $aExtraParams['display_limit'] : true; + + $sTableId = isset($aExtraParams['table_id']) ? $aExtraParams['table_id'] : null; + $aClassAliases = array($sClassAlias => $sClassName); + $oDefaultSettings = DataTableSettings::GetDataModelSettings($aClassAliases, $bViewLink, array($sClassAlias => $aList)); + if ($bDisplayLimit) { $iDefaultPageSize = appUserPreferences::GetPref('default_page_size', MetaModel::GetConfig()->GetMinDisplayLimit()); $oDefaultSettings->iDefaultPageSize = $iDefaultPageSize; } else { $oDefaultSettings->iDefaultPageSize = 0; } - - $sTableId = isset($aExtraParams['table_id']) ? $aExtraParams['table_id'] : null; $oDefaultSettings->aSortOrder = MetaModel::GetOrderByDefault($sClassName); $bUseCustomSettings = false; @@ -487,38 +367,32 @@ class DataTableUIBlockFactory extends AbstractUIBlockFactory } } $oSet->OptimizeColumnLoad($aColumnsToLoad); - + $aSortOrder=[]; + $aSortDatable=[]; $aColumnDefinition = []; - $iIndexColumn = 0; - - $bSelectMode = isset($aExtraParams['selection_mode']) ? $aExtraParams['selection_mode'] == true : false; - $bSingleSelectMode = isset($aExtraParams['selection_type']) ? ($aExtraParams['selection_type'] == 'single') : false; - $sSelectMode = ''; - if ($bSelectMode) { - $sSelectMode = $bSingleSelectMode ? 'single' : 'multiple'; + $iIndexColumn=0; + if($sSelectMode!="") { $iIndexColumn++; } - - $aSortOrder = []; - $aSortDatable = []; - foreach ($aAuthorizedClasses as $sClassAlias => $sClassName) { - if (false === isset($oCustomSettings->aColumns[$sClassAlias])) { - continue; - } - + foreach ($aClassAliases as $sClassAlias => $sClassName) { foreach ($oCustomSettings->aColumns[$sClassAlias] as $sAttCode => $aData) { $sCode = ($aData['code'] == '_key_') ? 'friendlyname' : $aData['code']; if ($aData['sort'] != 'none') { - $aSortOrder[$sClassAlias.'.'.$sCode] = ($aData['sort'] == 'asc'); // true for ascending, false for descending - $aSortDatable = [$iIndexColumn, $aData['sort']]; + $aSortOrder[$sAlias.'.'.$sCode] = ($aData['sort'] == 'asc'); // true for ascending, false for descending + $aSortDatable=[$iIndexColumn,$aData['sort']]; } elseif (isset($oCustomSettings->aSortOrder[$sAttCode])){ - $aSortOrder[$sClassAlias.'.'.$sCode] = $oCustomSettings->aSortOrder[$sAttCode]; // true for ascending, false for descending + $aSortOrder[$sAlias.'.'.$sCode] = $oCustomSettings->aSortOrder[$sAttCode]; // true for ascending, false for descending } if ($aData['checked']) { if ($sAttCode == '_key_') { if ($bViewLink) { + if (MetaModel::IsValidAttCode($sClassName, 'obsolescence_flag')) { + $sDisplayFunction = "let displayField = ''+row['".$sClassAlias."/friendlyname']+''; if (row['".$sClassAlias."/obsolescence_flag'].indexOf('no') == -1){displayField = ''+row['".$sClassAlias."/friendlyname']+'';} return displayField;"; + } else { + $sDisplayFunction = "let displayField = ''+row['".$sClassAlias."/friendlyname']+''; return displayField;"; + } $aColumnDefinition[] = [ 'description' => $aData['label'], 'object_class' => $sClassName, @@ -532,7 +406,7 @@ class DataTableUIBlockFactory extends AbstractUIBlockFactory } } else { $oAttDef = MetaModel::GetAttributeDef($sClassName, $sAttCode); - if ($oAttDef instanceof AttributeCaseLog) { + if ($oAttDef instanceof \AttributeCaseLog) { // Add JS files for display caselog // Dummy collapsible section created in order to get JS files $oCollapsibleSection = new CollapsibleSection(''); @@ -560,8 +434,7 @@ class DataTableUIBlockFactory extends AbstractUIBlockFactory if ($oDefaultSettings != null) { $aOptions['oDefaultSettings'] = json_encode(array('iDefaultPageSize' => $oDefaultSettings->iDefaultPageSize, 'oColumns' => $oDefaultSettings->aColumns)); } - - // Selection mode + $aOptions['sort'] = $aSortDatable; if ($sSelectMode == 'multiple') { $aOptions['select_mode'] = "multiple"; } else { @@ -571,18 +444,13 @@ class DataTableUIBlockFactory extends AbstractUIBlockFactory } $aOptions['selectionMode'] = $aExtraParams['selectionMode']?? 'positive'; - // Sort - $aOptions['sort'] = $aSortDatable; - - // Items count selector if (isset($aExtraParams['cssCount'])) { $aOptions['sCountSelector'] = $aExtraParams['cssCount']; } - // Pages length $aOptions['iPageSize'] = 10; if ($oCustomSettings->iDefaultPageSize > 0) { - $aOptions['iPageSize'] = (int)$oCustomSettings->iDefaultPageSize; + $aOptions['iPageSize'] = $oCustomSettings->iDefaultPageSize; } // Max height is only set if necessary, otherwise we want the list to occupy all the height it can depending on its pagination @@ -592,19 +460,18 @@ class DataTableUIBlockFactory extends AbstractUIBlockFactory $aOptions['processing'] = true; $aOptions['sTableId'] = $sTableId; - $aOptions['sListId'] = $sListId; $aOptions['bUseCustomSettings'] = $bUseCustomSettings; $aOptions['bViewLink'] = $bViewLink; + $aOptions['sListId'] = $sListId; $aOptions['oClassAliases'] = json_encode($aClassAliases); - if (isset($aExtraParams['selected_rows']) && !empty($aExtraParams['selected_rows'])) { $aOptions['sSelectedRows'] = json_encode($aExtraParams['selected_rows']); } else { $aOptions['sSelectedRows'] = '[]'; } + $aExtraParams['table_id']=$sTableId; + $aExtraParams['list_id']=$sListId; - $aExtraParams['table_id'] = $sTableId; - $aExtraParams['list_id'] = $sListId; $oDataTable->SetOptions($aOptions); $oDataTable->SetAjaxUrl(utils::GetAbsoluteUrlAppRoot()."pages/ajax.render.php"); @@ -620,20 +487,11 @@ class DataTableUIBlockFactory extends AbstractUIBlockFactory $oDataTable->SetResultColumns($oCustomSettings->aColumns); $oDataTable->SetInitDisplayData(AjaxRenderController::GetDataForTable($oSet, $aClassAliases, $aColumnsToLoad, $sIdName, $aExtraParams)); - // row actions - if (isset($aExtraParams['row_actions'])) { - $oDataTable->SetRowActions($aExtraParams['row_actions']); - } - - if (isset($aExtraParams['creation_in_modal_js_handler'])){ - $oDataTable->SetModalCreationHandler($aExtraParams['creation_in_modal_js_handler']); - } - return $oDataTable; } /** - * @deprecated 3.1.0 N°6261 Use \DataTableUIBlockFactory::MakeForRendering instead + * @api * @param string $sListId * @param DBObjectSet $oSet * @param array $aExtraParams @@ -647,9 +505,228 @@ class DataTableUIBlockFactory extends AbstractUIBlockFactory */ public static function MakeForRenderingObject(string $sListId, DBObjectSet $oSet, $aExtraParams = array()) { - DeprecatedCallsLog::NotifyDeprecatedPhpMethod('use DataTableUIBlockFactory::MakeForRendering instead'); + $oDataTable = new DataTable('datatable_'.$sListId); + $aList = array(); + $oAppRoot = utils::GetAbsoluteUrlAppRoot(); - return static::MakeForRendering($sListId, $oSet, $aExtraParams); + // Initialize and check the parameters + $bViewLink = isset($aExtraParams['view_link']) ? $aExtraParams['view_link'] : true; + // Check if there is a list of aliases to limit the display to... + $aDisplayAliases = isset($aExtraParams['display_aliases']) ? explode(',', + $aExtraParams['display_aliases']) : array(); + $sZListName = isset($aExtraParams['zlist']) ? ($aExtraParams['zlist']) : 'list'; + + $aExtraFieldsRaw = isset($aExtraParams['extra_fields']) ? explode(',', + trim($aExtraParams['extra_fields'])) : array(); + $aExtraFields = array(); + $sAttCode = ''; + foreach ($aExtraFieldsRaw as $sFieldName) { + // Ignore attributes not of the main queried class + if (preg_match('/^(.*)\.(.*)$/', $sFieldName, $aMatches)) { + $sClassAlias = $aMatches[1]; + $sAttCode = $aMatches[2]; + if (array_key_exists($sClassAlias, $oSet->GetSelectedClasses())) { + $aExtraFields[$sClassAlias][] = $sAttCode; + } + } else { + $aExtraFields['*'] = $sAttCode; + } + } + + $aClassAliases = $oSet->GetFilter()->GetSelectedClasses(); + $aAuthorizedClasses = array(); + foreach ($aClassAliases as $sAlias => $sClassName) { + if ((UserRights::IsActionAllowed($sClassName, UR_ACTION_READ, $oSet) != UR_ALLOWED_NO) && + ((count($aDisplayAliases) == 0) || (in_array($sAlias, $aDisplayAliases)))) { + $aAuthorizedClasses[$sAlias] = $sClassName; + } + } + foreach ($aAuthorizedClasses as $sAlias => $sClassName) { + if (array_key_exists($sAlias, $aExtraFields)) { + $aList[$sAlias] = $aExtraFields[$sAlias]; + } else { + $aList[$sAlias] = array(); + } + if ($sZListName !== false) { + $aDefaultList = MetaModel::FlattenZList(MetaModel::GetZListItems($sClassName, $sZListName)); + $aList[$sAlias] = array_merge($aDefaultList, $aList[$sAlias]); + } + + // Filter the list to removed linked set since we are not able to display them here + foreach ($aList[$sAlias] as $index => $sAttCode) { + $oAttDef = MetaModel::GetAttributeDef($sClassName, $sAttCode); + if ($oAttDef instanceof AttributeLinkedSet) { + // Removed from the display list + unset($aList[$sAlias][$index]); + } + } + + if (empty($aList[$sAlias])) { + unset($aList[$sAlias], $aAuthorizedClasses[$sAlias]); + } + } + + $oDefaultSettings = DataTableSettings::GetDataModelSettings($aAuthorizedClasses, $bViewLink, $aList); + + $bDisplayLimit = isset($aExtraParams['display_limit']) ? $aExtraParams['display_limit'] : true; + if ($bDisplayLimit) { + $iDefaultPageSize = appUserPreferences::GetPref('default_page_size', + MetaModel::GetConfig()->GetMinDisplayLimit()); + $oDefaultSettings->iDefaultPageSize = $iDefaultPageSize; + } + + $sTableId = isset($aExtraParams['table_id']) ? $aExtraParams['table_id'] : null; + $oDefaultSettings->aSortOrder = MetaModel::GetOrderByDefault($sClassName); + + $bUseCustomSettings = false; + // Identified tables can have their own specific settings + $oCustomSettings = DataTableSettings::GetTableSettings($aClassAliases, $sTableId); + + if ($oCustomSettings != null) { + // Custom settings overload the default ones + $bUseCustomSettings = true; + if ($oDefaultSettings->iDefaultPageSize == 0) { + $oCustomSettings->iDefaultPageSize = 0; + } + } else { + $oCustomSettings = $oDefaultSettings; + } + + if ($oCustomSettings->iDefaultPageSize > 0) { + $oSet->SetLimit($oCustomSettings->iDefaultPageSize); + } + + $sIdName = isset($extraParams["id_for_select"]) ? $extraParams["id_for_select"] : ""; + // Load only the requested columns + $aColumnsToLoad = array(); + foreach ($oCustomSettings->aColumns as $sAlias => $aColumnsInfo) { + foreach ($aColumnsInfo as $sAttCode => $aData) { + if ($sAttCode != '_key_') { + if ($aData['checked']) { + $aColumnsToLoad[$sAlias][] = $sAttCode; + } else { + // See if this column is a must to load + $sClass = $aClassAliases[$sAlias]; + $oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode); + if ($oAttDef->AlwaysLoadInTables()) { + $aColumnsToLoad[$sAlias][] = $sAttCode; + } + } + } else { + if ($sIdName == "") { + $sIdName = $sAlias."/_key_"; + } + } + } + } + $oSet->OptimizeColumnLoad($aColumnsToLoad); + + $aColumnDefinition = []; + $iIndexColumn = 0; + + $bSelectMode = isset($aExtraParams['selection_mode']) ? $aExtraParams['selection_mode'] == true : false; + $bSingleSelectMode = isset($aExtraParams['selection_type']) ? ($aExtraParams['selection_type'] == 'single') : false; + $sSelectMode = ''; + if ($bSelectMode) { + $sSelectMode = $bSingleSelectMode ? 'single' : 'multiple'; + $iIndexColumn++; + } + + $aSortDatable = []; + foreach ($aAuthorizedClasses as $sClassAlias => $sClassName) { + if (isset($oCustomSettings->aColumns[$sClassAlias])) { + foreach ($oCustomSettings->aColumns[$sClassAlias] as $sAttCode => $aData) { + if ($aData['sort'] != 'none' && $aSortDatable == []) { + $aSortDatable = [$index, $aData['sort']]; + } + + if ($aData['checked']) { + if ($sAttCode == '_key_') { + if ($bViewLink) { + $sAttLabel = MetaModel::GetName($sClassName); + $aColumnDefinition[] = [ + 'description' => $aData['label'], + 'object_class' => $sClassName, + 'class_alias' => $sClassAlias, + 'attribute_code' => $sAttCode, + 'attribute_type' => '_key_', + 'attribute_label' => $sAttLabel, + "render" => "return row['".$sClassAlias."/hyperlink'];", + ]; + } + } else { + $oAttDef = MetaModel::GetAttributeDef($sClassName, $sAttCode); + if ($oAttDef instanceof \AttributeCaseLog) { + // Removed from the display list + // Dummy collapsible section created in order to get JS files + $sCollapsibleSection = new CollapsibleSection(''); + $oDataTable->AddMultipleJsFilesRelPaths($sCollapsibleSection->GetJsFilesUrlRecursively()); + } + $sAttDefClass = get_class($oAttDef); + $sAttLabel = MetaModel::GetLabel($sClassName, $sAttCode); + $aColumnDefinition[] = [ + 'description' => $oAttDef->GetOrderByHint(), + 'object_class' => $sClassName, + 'class_alias' => $sClassAlias, + 'attribute_code' => $sAttCode, + 'attribute_type' => $sAttDefClass, + 'attribute_label' => $sAttLabel, + "render" => $oAttDef->GetRenderForDataTable($sClassAlias), + ]; + } + $iIndexColumn++; + } + } + } + } + $oSet->SetOrderBy($oCustomSettings->GetSortOrder()); + + $aOptions = []; + if ($oDefaultSettings != null) { + $aOptions['oDefaultSettings'] = json_encode(array('iDefaultPageSize' => $oDefaultSettings->iDefaultPageSize, 'oColumns' => $oDefaultSettings->aColumns)); + } + + if ($sSelectMode == 'multiple') { + $aOptions['select_mode'] = "multiple"; + } else { + if ($sSelectMode == 'single') { + $aOptions['select_mode'] = "single"; + } + } + $aOptions['selectionMode'] = $aExtraParams['selectionMode']?? 'positive'; + + $aOptions['sort'] = $aSortDatable; + + $aOptions['iPageSize'] = 10; + if ($oCustomSettings->iDefaultPageSize > 0) { + $aOptions['iPageSize'] = $oCustomSettings->iDefaultPageSize; + } + + // Max height is only set if necessary, otherwise we want the list to occupy all the height it can depending on its pagination + if (isset($aExtraParams['max_height'])) { + $aOptions['sMaxHeight'] = $aExtraParams['max_height']; + } + + $aOptions['sTableId'] = $sTableId; + $aOptions['bUseCustomSettings'] = $bUseCustomSettings; + $aOptions['bViewLink'] = $bViewLink; + $aOptions['oClassAliases'] = json_encode($aClassAliases); + + $oDataTable->SetOptions($aOptions); + $oDataTable->SetAjaxUrl("ajax.render.php"); + $oDataTable->SetAjaxData([ + "operation" => 'search', + "filter" => $oSet->GetFilter()->serialize(), + "columns" => $oCustomSettings->aColumns, + "extra_params" => $aExtraParams, + "class_aliases" => $aClassAliases, + "select_mode" => $sSelectMode, + ]); + $oDataTable->SetDisplayColumns($aColumnDefinition); + $oDataTable->SetResultColumns($oCustomSettings->aColumns); + $oDataTable->SetInitDisplayData(AjaxRenderController::GetDataForTable($oSet, $aClassAliases, $aColumnsToLoad, $sIdName, $aExtraParams)); + + return $oDataTable; } /** @@ -714,14 +791,14 @@ class DataTableUIBlockFactory extends AbstractUIBlockFactory $aColumnDefinition["type"] = "html"; if ($sAttCode == '_key_') { - $sAttrLabel = $aData['alias']; + $sAttLabel = $aData['alias']; $aColumnDefinition["title"] = $aData['alias']; $aColumnDefinition['metadata'] = [ 'object_class' => $sClassName, 'class_alias' => $sClassAlias, 'attribute_code' => $sAttCode, 'attribute_type' => '_key_', - 'attribute_label' => $sAttrLabel, + 'attribute_label' => $sAttLabel, ]; $aColumnDefinition["data"] = $sClassAlias."/".$sAttCode; $aColumnDefinition["render"] = [ @@ -729,15 +806,15 @@ class DataTableUIBlockFactory extends AbstractUIBlockFactory "_" => $sClassAlias."/".$sAttCode, ]; $aColumnDefinition["createdCell"] = << $sClassAlias."/".$sAttCode, ]; $aColumnDefinition["createdCell"] = << ['label' => labelFIeld1, 'description' => descriptionField1], @@ -857,7 +933,7 @@ JS; * * @return \Combodo\iTop\Application\UI\Base\Layout\UIContentBlock */ - public static function MakeForStaticData(string $sTitle, array $aColumns, array $aData, ?string $sId = null, array $aExtraParams = [], string $sFilter = "", array $aOptions = [], array $aRowActions = null) + public static function MakeForStaticData(string $sTitle, array $aColumns, array $aData, ?string $sId = null, array $aExtraParams = [], string $sFilter = "", array $aOptions = []) { $oBlock = new UIContentBlock(); if ($sTitle != "") { @@ -865,13 +941,6 @@ JS; $oBlock->AddSubBlock($oTitle); } $oTable = new StaticTable($sId, [], $aExtraParams); - if ($aRowActions != null) { - $oTable->SetRowActions($aRowActions); - $aColumns['actions'] = [ - 'label' => Dict::S('UI:Datatables:Column:RowActions:Label'), - 'description' => Dict::S('UI:Datatables:Column:RowActions:Description'), - ]; - } $oTable->SetColumns($aColumns); $oTable->SetData($aData); $oTable->SetFilter($sFilter); @@ -888,7 +957,6 @@ JS; * @param array $aColumns * @param array $aData * @param string $sFilter - * @param array $aRowActions @since 3.1.0 * * $aColumns =[ * 'nameField1' => ['label' => labelFIeld1, 'description' => descriptionField1], @@ -898,17 +966,10 @@ JS; * * @return \Combodo\iTop\Application\UI\Base\Component\DataTable\StaticTable\FormTable\FormTable */ - public static function MakeForForm(string $sRef, array $aColumns, array $aData = [], string $sFilter = '', array $aRowActions = null) + public static function MakeForForm(string $sRef, array $aColumns, array $aData = [], string $sFilter = '') { $oTable = new FormTable("datatable_".$sRef); $oTable->SetRef($sRef); - if ($aRowActions != null) { - $oTable->SetRowActions($aRowActions); - $aColumns['actions'] = [ - 'label' => Dict::S('UI:Datatables:Column:RowActions:Label'), - 'description' => Dict::S('UI:Datatables:Column:RowActions:Description'), - ]; - } $oTable->SetColumns($aColumns); $oTable->SetFilter($sFilter); @@ -926,54 +987,24 @@ JS; public static function GetAllowedParams(): array { return [ - 'surround_with_panel', - /** bool embed table into a Panel */ - 'menu', - /** bool display table menu */ - 'view_link', - /** bool display the friendlyname column with links to the objects details */ - 'link_attr', - /** string link att code */ - 'object_id', - /** int Id of the object linked */ - 'target_attr', - /** string target att code of the link */ - 'selection_mode', - /** bool activate selection */ - 'selection_type', - /** string 'multiple' or 'single' */ - 'extra_fields', - /** string comma separated list of link att code to display ('alias.attcode')*/ - 'zlist', - /** string name of the zlist to display when 'extra_fields' is not set */ - 'display_limit', - /** bool if true pagination is used (default = true) */ - 'table_id', - /** string datatable id */ - 'cssCount', - /** string external counter (input hidden) js selector */ - 'selected_rows', - /** array list of Ids already selected when displaying the datatable */ - 'display_aliases', - /** string comma separated list of class aliases to display */ - 'list_id', - /** string list outer id */ - 'selection_enabled', - /** list of id in witch select is allowed, if not exists all lines are selectable */ - 'id_for_select', - /**give definition of id for select checkbox*/ - 'row_actions', - /** array of blocks displayed on every row */ - 'creation_in_modal', - /** bool to perform a creation of a new object of this type in a modal */ - 'creation_in_modal_tooltip', - /** creation in modal button tooltip */ - 'creation_in_modal_form_title', - /** creation in modal form title */ - 'creation_in_modal_js_handler', - /** Handler to call when trying to create a new object in modal */ - 'creation_disallowed', - /** Don't provide the standard object creation feature */ + 'surround_with_panel', /** bool embed table into a Panel */ + 'menu', /** bool display table menu */ + 'view_link', /** bool display the friendlyname column with links to the objects details */ + 'link_attr', /** string link att code */ + 'object_id', /** int Id of the object linked */ + 'target_attr', /** string target att code of the link */ + 'selection_mode', /** bool activate selection */ + 'selection_type', /** string 'multiple' or 'single' */ + 'extra_fields', /** string comma separated list of link att code to display ('alias.attcode')*/ + 'zlist', /** string name of the zlist to display when 'extra_fields' is not set */ + 'display_limit', /** bool if true pagination is used (default = true) */ + 'table_id', /** string datatable id */ + 'cssCount', /** string external counter (input hidden) js selector */ + 'selected_rows', /** array list of Ids already selected when displaying the datatable */ + 'display_aliases', /** string comma separated list of class aliases to display */ + 'list_id', /** string list outer id */ + 'selection_enabled', /** list of id in witch select is allowed, if not exists all lines are selectable */ + 'id_for_select', /**give definition of id for select checkbox*/ ]; } } \ No newline at end of file