From 4f7a1ea9da571dbb587ce8134d278263bb4413c7 Mon Sep 17 00:00:00 2001 From: Pierre Goiffon Date: Wed, 4 Jan 2023 15:53:32 +0100 Subject: [PATCH] =?UTF-8?q?N=C2=B03200=20Datatable=20"filter=20list"=20ico?= =?UTF-8?q?n=20:=20specific=20query=20for=201,n=20and=20n,n=20objects=20re?= =?UTF-8?q?lations?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This specific query passes attcodes list to display to UI.php, but this endpoint doesn't handle it yet. Adding this enhancements would require too much time for now (datatables legacy code), hopefully a refactoring work will be done soon and we'll get back to it ! Note that dedicated ContextTag is renamed from Search to ObjectSearch --- application/displayblock.class.inc.php | 2 +- application/menunode.class.inc.php | 1 + application/utils.inc.php | 99 +++++++++++++++++++++++--- core/contexttag.class.inc.php | 14 ++-- core/metamodel.class.php | 11 +-- pages/UI.php | 17 +++-- pages/ajax.searchform.php | 2 +- 7 files changed, 123 insertions(+), 23 deletions(-) diff --git a/application/displayblock.class.inc.php b/application/displayblock.class.inc.php index 0cc5a5e0f..f9049df2f 100644 --- a/application/displayblock.class.inc.php +++ b/application/displayblock.class.inc.php @@ -2223,7 +2223,7 @@ class MenuBlock extends DisplayBlock $oActionsToolbar->AddSubBlock($oActionButton); } else { // - Filter list - $sSearchUrl = utils::GetDataTableSearchUrl($this->m_oFilter); + $sSearchUrl = utils::GetDataTableSearchUrl($this->m_oFilter, $aExtraParams); if (!empty($sSearchUrl)) { $oActionButton = ButtonUIBlockFactory::MakeIconLink( 'fas fa-filter', diff --git a/application/menunode.class.inc.php b/application/menunode.class.inc.php index 23714443c..3ec169dbc 100644 --- a/application/menunode.class.inc.php +++ b/application/menunode.class.inc.php @@ -1111,6 +1111,7 @@ class OQLMenuNode extends MenuNode */ public function RenderContent(WebPage $oPage, $aExtraParams = array()) { + ContextTag::AddContext(ContextTag::TAG_OBJECT_SEARCH); ApplicationMenu::CheckMenuIdEnabled($this->GetMenuId()); OQLMenuNode::RenderOQLSearch ( diff --git a/application/utils.inc.php b/application/utils.inc.php index e28e1c5bb..1e91239cc 100644 --- a/application/utils.inc.php +++ b/application/utils.inc.php @@ -1542,20 +1542,20 @@ class utils } /** - * We cannot use iMenuId (corresponding values in iPopupMenuExtension constants) as value is always \iPopupMenuExtension::MENU_OBJLIST_TOOLKIT + * We cannot use iMenuId (corresponding values in {@see \iPopupMenuExtension} constants) as value is always {@see \iPopupMenuExtension::MENU_OBJLIST_TOOLKIT} * whenever we are in a datatable, whereas it is included in a object tab, a dashlet or a search. * * So a {@see \ContextTag} is set on the corresponding calls. * * @return bool true if we are in a search page context, either directly or by the datatable ajax call * - * @since 3.0.0 + * @since 3.1.0 N°3200 * - * @uses \ContextTag::TAG_SEARCH + * @uses \ContextTag::TAG_OBJECT_SEARCH */ public static function IsCurrentPageASearch(): bool { - if (ContextTag::Check(ContextTag::TAG_SEARCH)) { + if (ContextTag::Check(ContextTag::TAG_OBJECT_SEARCH)) { return true; } @@ -1564,30 +1564,113 @@ class utils /** * @param \DBObjectSearch $oFilter object list + * @param array $aExtraParams * * @return string|null null if we are already in a search, otherwise the URL to open this list in a search * * @throws \ArchivedObjectException * @throws \CoreException - * * @uses utils::IsCurrentPageASearch() * - * @since 3.0.0 + * @since 3.1.0 N°3200 */ - public static function GetDataTableSearchUrl(DBSearch $oFilter): ?string + public static function GetDataTableSearchUrl(DBSearch $oFilter, array $aExtraParams): ?string { if (static::IsCurrentPageASearch()) { // we don't want to add the link when already in a search page ! return null; } + $bIsObjectRelation = isset($aExtraParams['object_id'], $aExtraParams['target_attr']); + if ($bIsObjectRelation) { + [$oDataTableSearchFilter, $aParams] = static::GetDataTableSearchForRelations($oFilter, $aExtraParams); + } else { + $oDataTableSearchFilter = $oFilter; + $aParams = []; + } + + if (isset($aExtraParams['table_id'])) { + $aParams['table_id'] = $aExtraParams['table_id']; + } + $sParams = json_encode($aParams); + $sAppRootUrl = static::GetAbsoluteUrlAppRoot(); $oAppContext = new ApplicationContext(); - $sUrl = $sAppRootUrl.'pages/UI.php?operation=search&'.$oAppContext->GetForLink().'&filter='.rawurlencode($oFilter->serialize()); + + $sUrl = $sAppRootUrl + .'pages/UI.php?operation=search&' + .$oAppContext->GetForLink() + .'&filter='.rawurlencode($oDataTableSearchFilter->serialize()); + $sUrl .= '&aParams='.rawurlencode($sParams); // Not working... yet, cause not handled by UI.php return $sUrl; } + /** + * Rewrites filter for object relations, so that in the search page we will have the correct criteria and will be able to use "configure this list" + * + * @param \DBSearch $oFilter object list + * @param array{link_attr: string, target_attr: string, object_id: string} $aExtraParams + * + * @return array{\DBObjectSearch, string[]} + * @throws \CoreException + * @throws \OQLException + * + * @since 3.1.0 N°3200 + */ + private static function GetDataTableSearchForRelations(DBSearch $oFilter, array $aExtraParams): array + { + $sObjectId = $aExtraParams['object_id']; + $bIsLinkedSetIndirect = isset($aExtraParams['link_attr']); + if ($bIsLinkedSetIndirect) { + //--- AttributeLinkedSetIndirect (n,n => lnk class) + $sLnkClass = $oFilter->GetClass(); + $sExtKeyToObjectClass = $aExtraParams['link_attr']; + $sExtKeyToRemoteClass = $aExtraParams['target_attr']; + + /** @var \AttributeExternalKey $oLnkExtKeyToRemote */ + $oLnkExtKeyToRemote = MetaModel::GetAttributeDef($sLnkClass, $sExtKeyToRemoteClass); + $sRemoteClass = $oLnkExtKeyToRemote->GetTargetClass(); + + /** @var \AttributeExternalKey $oLnkExtKeyToRemote */ + $oLnkExtKeyToObject = MetaModel::GetAttributeDef($sLnkClass, $sExtKeyToObjectClass); + $sObjectClass = $oLnkExtKeyToObject->GetTargetClass(); + + /** @var \AttributeExternalKey $oLnkExtKeyToRemote */ + $oObjectExtKeyToLnk = $oLnkExtKeyToObject->GetMirrorLinkAttribute(); + $sObjectExtKeyToLnkClass = $oObjectExtKeyToLnk->GetCode(); + + $sRemoteClassAliasName = ormLinkSet::REMOTE_ALIAS; + $sLnkClassAliasName = ormLinkSet::LINK_ALIAS; + $sOql = << false, + 'extra_fields' => $sAttCodesToDisplay, + ]; + } else { + //--- AttributeLinkedSet (1,n => AttributeExternalKey) + $sClass = $oFilter->GetClass(); + $sExtKeyCode = $aExtraParams['target_attr']; + + $sOql = "SELECT $sClass WHERE $sExtKeyCode = $sObjectId"; + + $aParams = []; + } + + $oDataTableSearchFilter = DBSearch::FromOQL($sOql); + + return [$oDataTableSearchFilter, $aParams]; + } + /** * @param string $sEnvironment * diff --git a/core/contexttag.class.inc.php b/core/contexttag.class.inc.php index 064082d03..1b7c9c34a 100644 --- a/core/contexttag.class.inc.php +++ b/core/contexttag.class.inc.php @@ -52,13 +52,17 @@ */ class ContextTag { - public const TAG_PORTAL = 'GUI:Portal'; - public const TAG_CRON = 'CRON'; + public const TAG_PORTAL = 'GUI:Portal'; + public const TAG_CRON = 'CRON'; public const TAG_CONSOLE = 'GUI:Console'; - public const TAG_SETUP = 'Setup'; + public const TAG_SETUP = 'Setup'; public const TAG_SYNCHRO = 'Synchro'; - public const TAG_REST = 'REST/JSON'; - public const TAG_SEARCH = 'Search'; + public const TAG_REST = 'REST/JSON'; + /** + * @var string + * @since 3.1.0 N°3200 + */ + public const TAG_OBJECT_SEARCH = 'ObjectSearch'; protected static $aStack = array(); diff --git a/core/metamodel.class.php b/core/metamodel.class.php index 768eadf88..65678cfce 100644 --- a/core/metamodel.class.php +++ b/core/metamodel.class.php @@ -2072,6 +2072,9 @@ abstract class MetaModel * * @return string[] attcodes to display, containing aliases * @throws \CoreException + * + * @since 3.0.0 N°2334 added code for n-n relations in {@see BlockIndirectLinksViewTable::GetAttCodesToDisplay} + * @since 3.1.0 N°3200 method creation so that it can be used elsewhere */ public static function GetAttributeLinkedSetIndirectDatatableAttCodesToDisplay(string $sObjectClass, string $sObjectLinkedSetIndirectAttCode, string $sRemoteClass, string $sLnkExternalKeyToRemoteClassAttCode):array { @@ -2079,17 +2082,17 @@ abstract class MetaModel $aRemoteAttDefsToDisplay = MetaModel::GetZListAttDefsFilteredForIndirectRemoteClass($sRemoteClass); $aLnkAttCodesToDisplay = array_map( function ($oLnkAttDef) { - return \ormLinkSet::LINK_ALIAS.'.'.$oLnkAttDef->GetCode(); + return ormLinkSet::LINK_ALIAS.'.'.$oLnkAttDef->GetCode(); }, $aLnkAttDefsToDisplay ); - if (!in_array(\ormLinkSet::LINK_ALIAS.'.'.$sLnkExternalKeyToRemoteClassAttCode, $aLnkAttCodesToDisplay)) { + if (!in_array(ormLinkSet::LINK_ALIAS.'.'.$sLnkExternalKeyToRemoteClassAttCode, $aLnkAttCodesToDisplay)) { // we need to display a link to the remote class instance ! - $aLnkAttCodesToDisplay[] = \ormLinkSet::LINK_ALIAS.'.'.$sLnkExternalKeyToRemoteClassAttCode; + $aLnkAttCodesToDisplay[] = ormLinkSet::LINK_ALIAS.'.'.$sLnkExternalKeyToRemoteClassAttCode; } $aRemoteAttCodesToDisplay = array_map( function ($oRemoteAttDef) { - return \ormLinkSet::REMOTE_ALIAS.'.'.$oRemoteAttDef->GetCode(); + return ormLinkSet::REMOTE_ALIAS.'.'.$oRemoteAttDef->GetCode(); }, $aRemoteAttDefsToDisplay ); diff --git a/pages/UI.php b/pages/UI.php index d0c4c7527..e7f5fc663 100644 --- a/pages/UI.php +++ b/pages/UI.php @@ -143,12 +143,15 @@ function SetObjectBreadCrumbEntry(DBObject $oObj, WebPage $oPage) * @throws \CoreException * @throws \DictExceptionMissingString */ -function DisplaySearchSet($oP, $oFilter, $bSearchForm = true, $sBaseClass = '', $sFormat = '', $bDoSearch = true, $bSearchFormOpen = true) +function DisplaySearchSet($oP, $oFilter, $bSearchForm = true, $sBaseClass = '', $sFormat = '', $bDoSearch = true, $bSearchFormOpen = true, $aParams = []) { //search block $oBlockForm = null; if ($bSearchForm) { - $aParams = array('open' => $bSearchFormOpen, 'table_id' => 'result_1'); + $aParams['open'] = $bSearchFormOpen; + if (false === isset($aParams['table_id'])) { + $aParams['table_id'] = 'result_1'; + } if (!empty($sBaseClass)) { $aParams['baseClass'] = $sBaseClass; } @@ -519,7 +522,7 @@ try /////////////////////////////////////////////////////////////////////////////////////////// case 'search': // Serialized DBSearch - $oSearchContext = new ContextTag(ContextTag::TAG_SEARCH); + $oSearchContext = new ContextTag(ContextTag::TAG_OBJECT_SEARCH); $sFilter = utils::ReadParam('filter', '', false, 'raw_data'); $sFormat = utils::ReadParam('format', ''); $bSearchForm = utils::ReadParam('search_form', true); @@ -530,7 +533,13 @@ try $oP->set_title(Dict::S('UI:SearchResultsPageTitle')); $oFilter = DBSearch::unserialize($sFilter); // TO DO : check that the filter is valid $oFilter->UpdateContextFromUser(); - DisplaySearchSet($oP, $oFilter, $bSearchForm, '' /* sBaseClass */, $sFormat); + + //FIXME Params won't work as expected :( + // During the ajax call fetching the datatable data, the URL is rewritten and the info are lost, and we are getting a worse result :( +// $sParams = utils::ReadParam('aParams', '{}', false, \utils::ENUM_SANITIZATION_FILTER_RAW_DATA); +// $aParams = json_decode($sParams, true); + + DisplaySearchSet($oP, $oFilter, $bSearchForm, '' /* sBaseClass */, $sFormat, ''); //, true, $aParams break; /////////////////////////////////////////////////////////////////////////////////////////// diff --git a/pages/ajax.searchform.php b/pages/ajax.searchform.php index 0f4a816bf..283e0242c 100644 --- a/pages/ajax.searchform.php +++ b/pages/ajax.searchform.php @@ -29,7 +29,7 @@ try throw new AjaxSearchException("Invalid query (empty filter)", 400); } - $oSearchContext = new ContextTag(ContextTag::TAG_SEARCH); + $oSearchContext = new ContextTag(ContextTag::TAG_OBJECT_SEARCH); $oPage = new AjaxPage(""); $oPage->SetContentType('text/html');