managebrickcontroller.class.inc.php 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567
  1. <?php
  2. // Copyright (C) 2010-2017 Combodo SARL
  3. //
  4. // This file is part of iTop.
  5. //
  6. // iTop is free software; you can redistribute it and/or modify
  7. // it under the terms of the GNU Affero General Public License as published by
  8. // the Free Software Foundation, either version 3 of the License, or
  9. // (at your option) any later version.
  10. //
  11. // iTop is distributed in the hope that it will be useful,
  12. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. // GNU Affero General Public License for more details.
  15. //
  16. // You should have received a copy of the GNU Affero General Public License
  17. // along with iTop. If not, see <http://www.gnu.org/licenses/>
  18. namespace Combodo\iTop\Portal\Controller;
  19. use Combodo\iTop\Portal\Helper\ScopeValidatorHelper;
  20. use \Silex\Application;
  21. use \Symfony\Component\HttpFoundation\Request;
  22. use \UserRights;
  23. use \CMDBSource;
  24. use \IssueLog;
  25. use \MetaModel;
  26. use \AttributeDefinition;
  27. use \AttributeDate;
  28. use \AttributeDateTime;
  29. use \AttributeDuration;
  30. use \AttributeSubItem;
  31. use \DBSearch;
  32. use \DBObjectSearch;
  33. use \DBObjectSet;
  34. use \DBObject;
  35. use \FieldExpression;
  36. use \BinaryExpression;
  37. use \VariableExpression;
  38. use \SQLExpression;
  39. use \UnaryExpression;
  40. use \Dict;
  41. use \iPopupMenuExtension;
  42. use \URLButtonItem;
  43. use \JSButtonItem;
  44. use \Combodo\iTop\Portal\Helper\ApplicationHelper;
  45. use \Combodo\iTop\Portal\Helper\SecurityHelper;
  46. use \Combodo\iTop\Portal\Brick\AbstractBrick;
  47. use \Combodo\iTop\Portal\Brick\ManageBrick;
  48. class ManageBrickController extends BrickController
  49. {
  50. public function DisplayAction(Request $oRequest, Application $oApp, $sBrickId, $sGroupingTab, $sDataLoading = null)
  51. {
  52. /** @var \Combodo\iTop\Portal\Brick\ManageBrick $oBrick */
  53. $oBrick = ApplicationHelper::GetLoadedBrickFromId($oApp, $sBrickId);
  54. $aData = array();
  55. $aGroupingTabsValues = array();
  56. $aGroupingAreasValues = array();
  57. $aQueries = array();
  58. // Getting current dataloading mode (First from router parameter, then query parameter, then default brick value)
  59. $sDataLoading = ($sDataLoading !== null) ? $sDataLoading : ( ($oRequest->get('sDataLoading') !== null) ? $oRequest->get('sDataLoading') : $oBrick->GetDataLoading() );
  60. // Getting search value
  61. $sSearchValue = $oRequest->get('sSearchValue', null);
  62. // Getting area columns properties
  63. $aColumnsAttrs = $oBrick->GetFields();
  64. // Adding friendlyname attribute to the list is not already in it
  65. $sTitleAttrCode = 'friendlyname';
  66. if (($sTitleAttrCode !== null) && !in_array($sTitleAttrCode, $aColumnsAttrs))
  67. {
  68. $aColumnsAttrs = array_merge(array($sTitleAttrCode), $aColumnsAttrs);
  69. }
  70. // Starting to build query
  71. $oQuery = DBSearch::FromOQL($oBrick->GetOql());
  72. // - Adding search clause if necessary
  73. // Note : This is a very naive search at the moment
  74. if ($sSearchValue !== null)
  75. {
  76. $aSearchListItems = MetaModel::GetZListItems($oQuery->GetClass(), 'standard_search');
  77. $oFullBinExpr = null;
  78. for ($i = 0; $i < count($aSearchListItems); $i++)
  79. {
  80. $sSearchItemAttr = $aSearchListItems[$i];
  81. $oBinExpr = new BinaryExpression(new FieldExpression($sSearchItemAttr, $oQuery->GetClassAlias()), 'LIKE', new VariableExpression('search_value'));
  82. // At each iteration we build the complete expression for the search like ( (field1 LIKE %search%) OR (field2 LIKE %search%) OR (field3 LIKE %search%) ...)
  83. if ($i === 0)
  84. {
  85. $oFullBinExpr = $oBinExpr;
  86. }
  87. else
  88. {
  89. $oFullBinExpr = new BinaryExpression($oFullBinExpr, 'OR', $oBinExpr);
  90. }
  91. // Then on the last iteration we add the complete expression to the query
  92. // Note : We don't do it after the loop as there could be an empty search ZList
  93. if ($i === (count($aSearchListItems) - 1))
  94. {
  95. // - Adding expression to the query
  96. $oQuery->AddConditionExpression($oFullBinExpr);
  97. // - Setting expression parameters
  98. // Note : This could be way more simpler if we had a SetInternalParam($sParam, $value) verb
  99. $aQueryParams = $oQuery->GetInternalParams();
  100. $aQueryParams['search_value'] = '%' . $sSearchValue . '%';
  101. $oQuery->SetInternalParams($aQueryParams);
  102. }
  103. }
  104. }
  105. // Preparing tabs
  106. // - We need to retrieve distinct values for the grouping attribute
  107. if ($oBrick->HasGroupingTabs())
  108. {
  109. $aGroupingTabs = $oBrick->GetGroupingTabs();
  110. // If tabs are made of the distinct values of an attribute, we have a find them via a query
  111. if ($oBrick->IsGroupingTabsByDistinctValues())
  112. {
  113. $sGroupingTabAttCode = $aGroupingTabs['attribute'];
  114. $oDistinctQuery = DBSearch::FromOQL($oBrick->GetOql());
  115. // - Restricting query to scope
  116. $oScopeQuery = $oApp['scope_validator']->GetScopeFilterForProfiles(UserRights::ListProfiles(), $oDistinctQuery->GetClass(), UR_ACTION_READ);
  117. if ($oScopeQuery !== null)
  118. {
  119. $oDistinctQuery = $oDistinctQuery->Intersect($oScopeQuery);
  120. // - Allowing all data if necessary
  121. if ($oScopeQuery->IsAllDataAllowed())
  122. {
  123. $oDistinctQuery->AllowAllData();
  124. }
  125. }
  126. // - Adding field condition
  127. $oFieldExp = new FieldExpression($sGroupingTabAttCode, $oDistinctQuery->GetClassAlias());
  128. $sDistinctSql = $oDistinctQuery->MakeGroupByQuery(array(), array('grouped_by_1' => $oFieldExp), true);
  129. $aDistinctResults = CMDBSource::QueryToArray($sDistinctSql);
  130. if (!empty($aDistinctResults))
  131. {
  132. foreach ($aDistinctResults as $aDistinctResult)
  133. {
  134. $oConditionQuery = DBSearch::CloneWithAlias($oQuery, 'GTAB');
  135. $oExpression = new BinaryExpression(new FieldExpression($sGroupingTabAttCode, $oDistinctQuery->GetClassAlias()), '=', new UnaryExpression($aDistinctResult['grouped_by_1']));
  136. $oConditionQuery->AddConditionExpression($oExpression);
  137. $aGroupingTabsValues[$aDistinctResult['grouped_by_1']] = array(
  138. 'value' => $aDistinctResult['grouped_by_1'],
  139. 'label' => strip_tags($oFieldExp->MakeValueLabel($oDistinctQuery, $aDistinctResult['grouped_by_1'], '')),
  140. 'condition' => $oConditionQuery,
  141. 'count' => $aDistinctResult['_itop_count_'],
  142. );
  143. unset($oConditionQuery);
  144. }
  145. unset($aDistinctResults);
  146. }
  147. else
  148. {
  149. $aGroupingTabsValues['undefined'] = array(
  150. 'value' => 'undefined',
  151. 'label' => '',
  152. 'condition' => null,
  153. 'count' => null,
  154. );
  155. }
  156. }
  157. // Otherwise we create the tabs from the SQL expressions
  158. else
  159. {
  160. foreach ($aGroupingTabs['groups'] as $aGroup)
  161. {
  162. $oConditionQuery = $oQuery->Intersect( DBSearch::FromOQL($aGroup['condition']) );
  163. // - Restricting query to scope
  164. $oScopeQuery = $oApp['scope_validator']->GetScopeFilterForProfiles(UserRights::ListProfiles(), $oConditionQuery->GetClass(), UR_ACTION_READ);
  165. if ($oScopeQuery !== null)
  166. {
  167. $oConditionQuery = $oConditionQuery->Intersect($oScopeQuery);
  168. // - Allowing all data if necessary
  169. if ($oScopeQuery->IsAllDataAllowed())
  170. {
  171. $oConditionQuery->AllowAllData();
  172. }
  173. }
  174. // - Building ObjectSet
  175. $oConditionSet = new DBObjectSet($oConditionQuery);
  176. $aGroupingTabsValues[$aGroup['id']] = array(
  177. 'value' => $aGroup['id'],
  178. 'label' => Dict::S($aGroup['title']),
  179. 'condition' => $oConditionQuery,
  180. 'count' => $oConditionSet->Count(),
  181. );
  182. }
  183. }
  184. }
  185. // - Retrieving the current grouping tab to display and altering the query to do so
  186. if ($sGroupingTab === null)
  187. {
  188. if ($oBrick->HasGroupingTabs())
  189. {
  190. reset($aGroupingTabsValues);
  191. $sGroupingTab = key($aGroupingTabsValues);
  192. if ($aGroupingTabsValues[$sGroupingTab]['condition'] !== null)
  193. {
  194. $oQuery = $oQuery->Intersect($aGroupingTabsValues[$sGroupingTab]['condition']);
  195. }
  196. }
  197. else
  198. {
  199. // Do not group by tabs, display all in the same page
  200. }
  201. }
  202. else
  203. {
  204. if ($aGroupingTabsValues[$sGroupingTab]['condition'] !== null)
  205. {
  206. $oQuery = $oQuery->Intersect($aGroupingTabsValues[$sGroupingTab]['condition']);
  207. }
  208. }
  209. // Preparing areas
  210. // - We need to retrieve distinct values for the grouping attribute
  211. // Note : Will have to be changed when we consider grouping on something else than the finalclass
  212. $sParentAlias = $oQuery->GetClassAlias();
  213. if (true)
  214. {
  215. $sGroupingAreaAttCode = 'finalclass';
  216. // For root classes
  217. if (MetaModel::IsValidAttCode($oQuery->GetClass(), $sGroupingAreaAttCode))
  218. {
  219. $oDistinctQuery = DBSearch::FromOQL($oBrick->GetOql());
  220. // Checking if there is a scope to apply
  221. $oDistinctScopeQuery = $oApp['scope_validator']->GetScopeFilterForProfiles(UserRights::ListProfiles(), $oQuery->GetClass(), UR_ACTION_READ);
  222. if ($oDistinctScopeQuery != null)
  223. {
  224. $oDistinctQuery = $oDistinctQuery->Intersect($oDistinctScopeQuery);
  225. // - Allowing all data if necessary
  226. if ($oDistinctScopeQuery->IsAllDataAllowed())
  227. {
  228. $oDistinctQuery->AllowAllData();
  229. }
  230. }
  231. // Adding grouping conditions
  232. $oFieldExp = new FieldExpression($sGroupingAreaAttCode, $sParentAlias);
  233. $sDistinctSql = $oDistinctQuery->MakeGroupByQuery(array(), array('grouped_by_1' => $oFieldExp), true);
  234. $aDistinctResults = CMDBSource::QueryToArray($sDistinctSql);
  235. foreach ($aDistinctResults as $aDistinctResult)
  236. {
  237. $oConditionQuery = DBSearch::CloneWithAlias($oQuery, 'GARE');
  238. $oExpression = new BinaryExpression(new FieldExpression($sGroupingAreaAttCode, 'GARE'), '=', new UnaryExpression($aDistinctResult['grouped_by_1']));
  239. $oConditionQuery->AddConditionExpression($oExpression);
  240. $aGroupingAreasValues[$aDistinctResult['grouped_by_1']] = array(
  241. 'value' => $aDistinctResult['grouped_by_1'],
  242. 'label' => MetaModel::GetName($aDistinctResult['grouped_by_1']), // Caution : This works only because we froze the grouping areas on the finalclass attribute.
  243. 'condition' => $oConditionQuery,
  244. 'count' => $aDistinctResult['_itop_count_']
  245. );
  246. unset($oConditionQuery);
  247. }
  248. unset($aDistinctResults);
  249. }
  250. // For leaf classes
  251. else
  252. {
  253. $aGroupingAreasValues[$oQuery->GetClass()] = array(
  254. 'value' => $oQuery->GetClass(),
  255. 'label' => MetaModel::GetName($oQuery->GetClass()), // Caution : This works only because we froze the grouping areas on the finalclass attribute.
  256. 'condition' => null,
  257. 'count' => 0
  258. );
  259. }
  260. }
  261. // - Retrieving the grouping areas to display
  262. $sGroupingArea = $oRequest->get('sGroupingArea');
  263. // - If specified or lazy loading, we trunc the $aGroupingAreasValues to keep only this one
  264. if ($sGroupingArea !== null)
  265. {
  266. $aGroupingAreasValues = array($sGroupingArea => $aGroupingAreasValues[$sGroupingArea]);
  267. }
  268. // - Preapring the queries
  269. foreach ($aGroupingAreasValues as $sKey => $aGroupingAreasValue)
  270. {
  271. $oAreaQuery = DBSearch::CloneWithAlias($oQuery, $sParentAlias);
  272. if ($aGroupingAreasValue['condition'] !== null)
  273. {
  274. //$oAreaQuery->AddConditionExpression($aGroupingAreasValue['condition']);
  275. $oAreaQuery = $oAreaQuery->Intersect($aGroupingAreasValue['condition']);
  276. }
  277. // Restricting query to allowed scope on each classes
  278. // Note: Will need to moved the scope restriction on queries elsewhere when we consider grouping on something else than finalclass
  279. // Note: We now get view scope instead of edit scope as we allowed users to view/edit objects in the brick regarding their rights
  280. $oScopeQuery = $oApp['scope_validator']->GetScopeFilterForProfiles(UserRights::ListProfiles(), $aGroupingAreasValue['value'], UR_ACTION_READ);
  281. if ($oScopeQuery !== null)
  282. {
  283. $oAreaQuery = $oAreaQuery->Intersect($oScopeQuery);
  284. // - Allowing all data if necessary
  285. if ($oScopeQuery->IsAllDataAllowed())
  286. {
  287. $oAreaQuery->AllowAllData();
  288. }
  289. }
  290. else
  291. {
  292. $oAreaQuery = null;
  293. }
  294. $aQueries[$sKey] = $oAreaQuery;
  295. }
  296. // Testing appropriate data loading mode if we are in auto
  297. // - For all (html) tables, this doesn't care for the grouping ares (finalclass)
  298. if ($sDataLoading === AbstractBrick::ENUM_DATA_LOADING_AUTO)
  299. {
  300. // - Check how many records there is.
  301. // - Update $sDataLoading with its new value regarding the number of record and the threshold
  302. $oCountSet = new DBObjectSet($oQuery);
  303. $oCountSet->OptimizeColumnLoad(array());
  304. $fThreshold = (float) MetaModel::GetModuleSetting($oApp['combodo.portal.instance.id'], 'lazy_loading_threshold');
  305. $sDataLoading = ($oCountSet->Count() > $fThreshold) ? AbstractBrick::ENUM_DATA_LOADING_LAZY : AbstractBrick::ENUM_DATA_LOADING_FULL;
  306. unset($oCountSet);
  307. }
  308. // Preparing data sets
  309. $aSets = array();
  310. /** @var DBSearch $oQuery */
  311. foreach ($aQueries as $sKey => $oQuery)
  312. {
  313. // Checking if we have a valid query
  314. if ($oQuery !== null)
  315. {
  316. // Setting query pagination if needed
  317. if ($sDataLoading === AbstractBrick::ENUM_DATA_LOADING_LAZY)
  318. {
  319. // Retrieving parameters
  320. $iPageNumber = (int) $oRequest->get('iPageNumber', 1);
  321. $iListLength = (int) $oRequest->get('iListLength', ManageBrick::DEFAULT_LIST_LENGTH);
  322. // Getting total records number
  323. $oCountSet = new DBObjectSet($oQuery);
  324. $oCountSet->OptimizeColumnLoad(array($oQuery->GetClassAlias() => $aColumnsAttrs));
  325. $aData['recordsTotal'] = $oCountSet->Count();
  326. $aData['recordsFiltered'] = $oCountSet->Count();
  327. unset($oCountSet);
  328. $oSet = new DBObjectSet($oQuery);
  329. $oSet->SetLimit($iListLength, $iListLength * ($iPageNumber - 1));
  330. }
  331. else
  332. {
  333. $oSet = new DBObjectSet($oQuery);
  334. }
  335. // Adding always_in_tables attributes
  336. $aColumnsToLoad = array($oQuery->GetClassAlias() => $aColumnsAttrs);
  337. foreach($oQuery->GetSelectedClasses() as $sAlias => $sClass)
  338. {
  339. /** @var AttributeDefinition $oAttDef */
  340. foreach(MetaModel::ListAttributeDefs($sClass) as $sAttCode => $oAttDef)
  341. {
  342. if($oAttDef->AlwaysLoadInTables())
  343. {
  344. $aColumnsToLoad[$sAlias][] = $sAttCode;
  345. }
  346. }
  347. }
  348. $oSet->OptimizeColumnLoad($aColumnsToLoad);
  349. $oSet->SetOrderByClasses();
  350. SecurityHelper::PreloadForCache($oApp, $oSet->GetFilter(), $aColumnsToLoad[$oQuery->GetClassAlias()] /* preloading only extkeys from the main class */);
  351. $aSets[$sKey] = $oSet;
  352. }
  353. }
  354. // Retrieving and preparing data for rendering
  355. $aGroupingAreasData = array();
  356. $bHasObjectListItemExtension = false;
  357. foreach ($aSets as $sKey => $oSet)
  358. {
  359. // Set properties
  360. $sCurrentClass = $sKey;
  361. // Defining which attribute will open the edition form)
  362. $sMainActionAttrCode = $aColumnsAttrs[0];
  363. // Loading columns definition
  364. $aColumnsDefinition = array();
  365. foreach ($aColumnsAttrs as $sColumnAttr)
  366. {
  367. $oAttDef = MetaModel::GetAttributeDef($sKey, $sColumnAttr);
  368. $aColumnsDefinition[$sColumnAttr] = array(
  369. 'title' => $oAttDef->GetLabel(),
  370. 'type' => ($oAttDef instanceof AttributeDateTime) ? 'moment-'.$oAttDef->GetFormat()->ToMomentJS() : 'html', // Special sorting for Date & Time
  371. );
  372. }
  373. // Getting items
  374. $aItems = array();
  375. // ... For each item
  376. /** @var DBObject $oCurrentRow */
  377. while ($oCurrentRow = $oSet->Fetch())
  378. {
  379. // ... Retrieving item's attributes values
  380. $aItemAttrs = array();
  381. foreach ($aColumnsAttrs as $sItemAttr)
  382. {
  383. $aActions = array();
  384. // Set the edit action to the main (first) attribute only
  385. //if ($sItemAttr === $sTitleAttrCode)
  386. if ($sItemAttr === $sMainActionAttrCode)
  387. {
  388. // Checking if we can edit the object
  389. if (($oBrick->GetOpeningMode() === ManageBrick::ENUM_ACTION_EDIT) && SecurityHelper::IsActionAllowed($oApp, UR_ACTION_MODIFY, $sCurrentClass, $oCurrentRow->GetKey()))
  390. {
  391. $sActionType = ManageBrick::ENUM_ACTION_EDIT;
  392. }
  393. // - Otherwise, check if view is allowed
  394. elseif (SecurityHelper::IsActionAllowed($oApp, UR_ACTION_READ, $sCurrentClass, $oCurrentRow->GetKey()))
  395. {
  396. $sActionType = ManageBrick::ENUM_ACTION_VIEW;
  397. }
  398. else
  399. {
  400. $sActionType = null;
  401. }
  402. // - Then set allowed action
  403. if ($sActionType !== null)
  404. {
  405. $aActions[] = array(
  406. 'type' => $sActionType,
  407. 'class' => $sCurrentClass,
  408. 'id' => $oCurrentRow->GetKey(),
  409. 'opening_target' => $oBrick->GetOpeningTarget(),
  410. );
  411. }
  412. }
  413. /** @var AttributeDefinition $oAttDef */
  414. $oAttDef = MetaModel::GetAttributeDef($sCurrentClass, $sItemAttr);
  415. if ($oAttDef->IsExternalKey())
  416. {
  417. $sValue = $oCurrentRow->Get($sItemAttr . '_friendlyname');
  418. // Adding a view action on the external keys
  419. if ($oCurrentRow->Get($sItemAttr) !== $oAttDef->GetNullValue())
  420. {
  421. // Checking if we can view the object
  422. if ((SecurityHelper::IsActionAllowed($oApp, UR_ACTION_READ, $oAttDef->GetTargetClass(), $oCurrentRow->Get($sItemAttr))))
  423. {
  424. $aActions[] = array(
  425. 'type' => ManageBrick::ENUM_ACTION_VIEW,
  426. 'class' => $oAttDef->GetTargetClass(),
  427. 'id' => $oCurrentRow->Get($sItemAttr),
  428. 'opening_target' => $oBrick->GetOpeningTarget(),
  429. );
  430. }
  431. }
  432. }
  433. elseif ($oAttDef instanceof AttributeSubItem || $oAttDef instanceof AttributeDuration)
  434. {
  435. $sValue = $oAttDef->GetAsHTML($oCurrentRow->Get($sItemAttr));
  436. }
  437. else
  438. {
  439. $sValue = $oAttDef->GetValueLabel($oCurrentRow->Get($sItemAttr));
  440. }
  441. unset($oAttDef);
  442. $aItemAttrs[$sItemAttr] = array(
  443. 'att_code' => $sItemAttr,
  444. 'value' => $sValue,
  445. 'actions' => $aActions
  446. );
  447. }
  448. // ... Checking menu extensions
  449. $aItemButtons = array();
  450. foreach (MetaModel::EnumPlugins('iPopupMenuExtension') as $oExtensionInstance)
  451. {
  452. foreach($oExtensionInstance->EnumItems(iPopupMenuExtension::PORTAL_OBJLISTITEM_ACTIONS, array('portal_id' => $oApp['combodo.portal.instance.id'], 'object' => $oCurrentRow)) as $oMenuItem)
  453. {
  454. if (is_object($oMenuItem))
  455. {
  456. if($oMenuItem instanceof JSButtonItem)
  457. {
  458. $aItemButtons[] = $oMenuItem->GetMenuItem() + array('js_files' => $oMenuItem->GetLinkedScripts(), 'type' => 'button');
  459. }
  460. elseif($oMenuItem instanceof URLButtonItem)
  461. {
  462. $aItemButtons[] = $oMenuItem->GetMenuItem() + array('type' => 'link');
  463. }
  464. }
  465. }
  466. }
  467. // ... And item's properties
  468. $aItems[] = array(
  469. 'id' => $oCurrentRow->GetKey(),
  470. 'class' => $sCurrentClass,
  471. 'attributes' => $aItemAttrs,
  472. 'highlight_class' => $oCurrentRow->GetHilightClass(),
  473. 'actions' => $aItemButtons,
  474. );
  475. if(!empty($aItemButtons))
  476. {
  477. $bHasObjectListItemExtension = true;
  478. }
  479. }
  480. // Adding an extra column for object list item extensions
  481. if($bHasObjectListItemExtension === true)
  482. {
  483. $aColumnsDefinition['_ui_extensions'] = array(
  484. 'title' => Dict::S('Brick:Portal:Manage:Table:ItemActions'),
  485. 'type' => 'html',
  486. );
  487. }
  488. $aGroupingAreasData[$sKey] = array(
  489. 'sId' => $sKey,
  490. 'sTitle' => $aGroupingAreasValues[$sKey]['label'],
  491. 'aItems' => $aItems,
  492. 'iItemsCount' => $oSet->Count(),
  493. 'aColumnsDefinition' => $aColumnsDefinition
  494. );
  495. }
  496. // Preparing response
  497. if ($oRequest->isXmlHttpRequest())
  498. {
  499. $aData = $aData + array(
  500. 'data' => $aGroupingAreasData[$sGroupingArea]['aItems']
  501. );
  502. $oResponse = $oApp->json($aData);
  503. }
  504. else
  505. {
  506. $aData = $aData + array(
  507. 'oBrick' => $oBrick,
  508. 'sBrickId' => $sBrickId,
  509. 'sGroupingTab' => $sGroupingTab,
  510. 'aGroupingTabsValues' => $aGroupingTabsValues,
  511. 'sDataLoading' => $sDataLoading,
  512. 'aGroupingAreasData' => $aGroupingAreasData,
  513. 'sDateFormat' => AttributeDate::GetFormat()->ToMomentJS(),
  514. 'sDateTimeFormat' => AttributeDateTime::GetFormat()->ToMomentJS(),
  515. 'sSearchValue' => $sSearchValue,
  516. );
  517. $oResponse = $oApp['twig']->render($oBrick->GetPageTemplatePath(), $aData);
  518. }
  519. return $oResponse;
  520. }
  521. }