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');