datatable.class.inc.php 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768
  1. <?php
  2. // Copyright (C) 2012 Combodo SARL
  3. //
  4. // This program is free software; you can redistribute it and/or modify
  5. // it under the terms of the GNU General Public License as published by
  6. // the Free Software Foundation; version 3 of the License.
  7. //
  8. // This program is distributed in the hope that it will be useful,
  9. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. // GNU General Public License for more details.
  12. //
  13. // You should have received a copy of the GNU General Public License
  14. // along with this program; if not, write to the Free Software
  15. // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  16. /**
  17. * Data Table to display a set of objects in a tabular manner in HTML
  18. *
  19. * @author Erwan Taloc <erwan.taloc@combodo.com>
  20. * @author Romain Quetiez <romain.quetiez@combodo.com>
  21. * @author Denis Flaven <denis.flaven@combodo.com>
  22. * @license http://www.opensource.org/licenses/gpl-3.0.html GPL
  23. */
  24. class DataTable
  25. {
  26. protected $iListId; // Unique ID inside the web page
  27. protected $sTableId; // identifier for sqve the settings (combined with the class aliases)
  28. protected $oSet; // The set of objects to display
  29. protected $aClassAliases; // The aliases (alias => class) inside the set
  30. protected $iNbObjects; // Total number of objects inthe set
  31. protected $bUseCustomSettings; // Whether or not the current display uses custom settings
  32. protected $oDefaultSettings; // the default settings for displaying such a list
  33. /**
  34. * @param $iListId mixed Unique ID for this div/table in the page
  35. * @param $oSet DBObjectSet The set of data to display
  36. * @param $aClassAliases Hash The list of classes/aliases to be displayed in this set $sAlias => $sClassName
  37. * @param $sTableId mixed A string (or null) identifying this table in order to persist its settings
  38. */
  39. public function __construct($iListId, $oSet, $aClassAliases, $sTableId = null)
  40. {
  41. $this->iListId = $iListId;
  42. $this->oSet = $oSet;
  43. $this->aClassAliases = $aClassAliases;
  44. $this->sTableId = $sTableId;
  45. $this->iNbObjects = $oSet->Count();
  46. $this->bUseCustomSettings = false;
  47. $this->oDefaultSettings = null;
  48. }
  49. public function Display(WebPage $oPage, DataTableSettings $oSettings, $bActionsMenu, $sSelectMode, $bViewLink, $aExtraParams)
  50. {
  51. $this->oDefaultSettings = $oSettings;
  52. if ($this->sTableId != null)
  53. {
  54. // Identified tables can have their own specific settings
  55. $oCustomSettings = DataTableSettings::GetTableSettings($this->aClassAliases, $this->sTableId);
  56. }
  57. else
  58. {
  59. $oCustomSettings = null;
  60. }
  61. if ($oCustomSettings != null)
  62. {
  63. // Custom settings overload the default ones
  64. $this->bUseCustomSettings = true;
  65. }
  66. else
  67. {
  68. $oCustomSettings = $oSettings;
  69. }
  70. if ($oCustomSettings->iDefaultPageSize != -1)
  71. {
  72. $this->oSet->SetLimit($oCustomSettings->iDefaultPageSize);
  73. }
  74. return $this->GetAsHTML($oPage, $oCustomSettings->iDefaultPageSize, $oCustomSettings->iDefaultPageSize, 0, $oCustomSettings->aColumns, $bActionsMenu, true, $sSelectMode, $bViewLink, $aExtraParams);
  75. }
  76. public function GetAsHTML(WebPage $oPage, $iPageSize, $iDefaultPageSize, $iPageIndex, $aColumns, $bActionsMenu, $bToolkitMenu, $sSelectMode, $bViewLink, $aExtraParams)
  77. {
  78. $sObjectsCount = $this->GetObjectCount($oPage, $sSelectMode);
  79. $sPager = $this->GetPager($oPage, $iPageSize, $iDefaultPageSize, $iPageIndex);
  80. $sActionsMenu = '';
  81. $sToolkitMenu = '';
  82. if ($bActionsMenu)
  83. {
  84. $sActionsMenu = $this->GetActionsMenu($oPage, $aExtraParams);
  85. }
  86. if ($bToolkitMenu)
  87. {
  88. $sToolkitMenu = $this->GetToolkitMenu($oPage, $aExtraParams);
  89. }
  90. $sDataTable = $this->GetHTMLTable($oPage, $aColumns, $sSelectMode, $iPageSize, $bViewLink, $aExtraParams);
  91. $sConfigDlg = $this->GetTableConfigDlg($oPage, $aColumns, $bViewLink, $iDefaultPageSize);
  92. $sHtml = "<table id=\"datatable_{$this->iListId}\" class=\"datatable\">\n";
  93. $sHtml .= "<tr><td>$sObjectsCount</td><td class=\"menucontainer\">$sActionsMenu</td></tr>\n";
  94. $sHtml .= "<tr><td>$sPager</td><td class=\"menucontainer\">$sToolkitMenu</td></tr>\n";
  95. $sHtml .= "<tr><td class=\"datacontents\" colspan=\"2\">$sDataTable</td></tr>\n";
  96. $sHtml .= "</table>\n";
  97. $oPage->add_at_the_end($sConfigDlg);
  98. $aOptions = array(
  99. 'sPersistentId' => '',
  100. 'sFilter' => $this->oSet->GetFilter()->serialize(),
  101. 'oColumns' => $aColumns,
  102. 'sSelectMode' => $sSelectMode,
  103. 'sViewLink' => ($bViewLink ? 'true' : 'false'),
  104. 'iNbObjects' => $this->iNbObjects,
  105. 'iDefaultPageSize' => $iDefaultPageSize,
  106. 'iPageSize' => $iPageSize,
  107. 'iPageIndex' => $iPageIndex,
  108. 'oClassAliases' => $this->aClassAliases,
  109. 'sTableId' => $this->sTableId,
  110. 'oExtraParams' => $aExtraParams,
  111. 'sRenderUrl' => utils::GetAbsoluteUrlAppRoot().'pages/ajax.render.php',
  112. 'oRenderParameters' => array('str' => ''), // Forces JSON to encode this as a object...
  113. 'oDefaultSettings' => array('str' => ''), // Forces JSON to encode this as a object...
  114. 'oLabels' => array('moveup' => Dict::S('UI:Button:MoveUp'), 'movedown' => Dict::S('UI:Button:MoveDown')),
  115. );
  116. if($this->oDefaultSettings != null)
  117. {
  118. $aOptions['oDefaultSettings'] = $this->GetAsHash($this->oDefaultSettings);
  119. }
  120. $sJSOptions = json_encode($aOptions);
  121. $oPage->add_ready_script("$('#datatable_{$this->iListId}').datatable($sJSOptions);");
  122. return $sHtml;
  123. }
  124. /**
  125. * When refreshing the body of a paginated table, get the rows of the table (inside the TBODY)
  126. * return string The HTML rows to insert inside the <tbody> node
  127. */
  128. public function GetAsHTMLTableRows(WebPage $oPage, $iPageSize, $aColumns, $sSelectMode, $bViewLink, $aExtraParams)
  129. {
  130. $aAttribs = $this->GetHTMLTableConfig($aColumns, $sSelectMode, $bViewLink);
  131. $aValues = $this->GetHTMLTableValues($aColumns, $sSelectMode, $iPageSize, $bViewLink, $aExtraParams);
  132. $sHtml = '';
  133. foreach($aValues as $aRow)
  134. {
  135. $sHtml .= $oPage->GetTableRow($aRow, $aAttribs);
  136. }
  137. return $sHtml;
  138. }
  139. protected function GetObjectCount(WebPage $oPage, $sSelectMode)
  140. {
  141. if (($sSelectMode == 'single') || ($sSelectMode == 'multiple'))
  142. {
  143. $sHtml = Dict::Format('UI:Pagination:HeaderSelection', '<span id="total">'.$this->iNbObjects.'</span>', '<span class="selectedCount">0</span>');
  144. }
  145. else
  146. {
  147. $sHtml = Dict::Format('UI:Pagination:HeaderNoSelection', '<span id="total">'.$this->iNbObjects.'</span>');
  148. }
  149. return $sHtml;
  150. }
  151. protected function GetPager(WebPage $oPage, $iPageSize, $iDefaultPageSize, $iPageIndex)
  152. {
  153. $sHtml = '';
  154. if ($iPageSize == -1) // Display all
  155. {
  156. $sPagerStyle = 'style="display:none"'; // no limit: display the full table, so hide the "pager" UI
  157. }
  158. else
  159. {
  160. $sPagerStyle = '';
  161. }
  162. $sCombo = '<select class="pagesize">';
  163. for($iPage = 1; $iPage < 5; $iPage++)
  164. {
  165. $sSelected = ($iPage == $iPageSize) ? 'selected="selected"' : '';
  166. $iNbItems = $iPage * $iDefaultPageSize;
  167. $sCombo .= "<option $sSelected value=\"$iNbItems\">$iNbItems</option>";
  168. }
  169. $sSelected = (-1 == $iPageSize) ? 'selected="selected"' : '';
  170. $sCombo .= "<option $sSelected value=\"-1\">".Dict::S('UI:Pagination:All')."</option>";
  171. $sCombo .= '</select>';
  172. $sPages = Dict::S('UI:Pagination:PagesLabel');
  173. $sPageSizeCombo = Dict::Format('UI:Pagination:PageSize', $sCombo);
  174. $iNbPages = ($iPageSize == -1) ? 1 : ceil($this->iNbObjects / $iPageSize);
  175. $aPagesToDisplay = array();
  176. for($idx = 0; $idx <= min(4, $iNbPages-1); $idx++)
  177. {
  178. if ($idx == 0)
  179. {
  180. $aPagesToDisplay[$idx] = '<span page="0" class="curr_page">1</span>';
  181. }
  182. else
  183. {
  184. $aPagesToDisplay[$idx] = "<span id=\"gotopage_$idx\" class=\"gotopage\" page=\"$idx\">".(1+$idx)."</span>";
  185. }
  186. }
  187. $iLastPageIdx = $iNbPages - 1;
  188. if (!isset($aPagesToDisplay[$iLastPageIdx]))
  189. {
  190. unset($aPagesToDisplay[$idx - 1]); // remove the last page added to make room for the very last page
  191. $aPagesToDisplay[$iLastPageIdx] = "<span id=\"gotopage_$iLastPageIdx\" class=\"gotopage\" page=\"$iLastPageIdx\">... $iNbPages</span>";
  192. }
  193. $sPagesLinks = implode('', $aPagesToDisplay);
  194. $sPagesList = '['.implode(',', array_keys($aPagesToDisplay)).']';
  195. $sSelectionMode = ($iNbPages == 1) ? '' : 'positive';
  196. $sHtml =
  197. <<<EOF
  198. <div id="pager{$this->iListId}" class="pager" $sPagerStyle>
  199. <p><table class="pagination"><tr><td>$sPages</td><td><img src="../images/first.png" class="first"/></td>
  200. <td><img src="../images/prev.png" class="prev"/></td>
  201. <td><span id="index">$sPagesLinks</span></td>
  202. <td><img src="../images/next.png" class="next"/></td>
  203. <td><img src="../images/last.png" class="last"/></td>
  204. <td>$sPageSizeCombo</td>
  205. <td><span id="loading">&nbsp;</span></td>
  206. </tr>
  207. </table>
  208. <input type="hidden" name="selectionMode" value="$sSelectionMode"></input>
  209. </div>
  210. EOF;
  211. return $sHtml;
  212. }
  213. protected function GetActionsMenu(WebPage $oPage, $aExtraParams)
  214. {
  215. $oMenuBlock = new MenuBlock($this->oSet->GetFilter(), 'list');
  216. $sHtml = $oMenuBlock->GetRenderContent($oPage, $aExtraParams, $this->iListId);
  217. return $sHtml;
  218. }
  219. protected function GetToolkitMenu(WebPage $oPage, $aExtraParams)
  220. {
  221. $sMenuTitle = Dict::S('UI:ConfigureThisList');
  222. $sHtml = '<div class="itop_popup" id="tk_'.$this->iListId.'"><ul><li><img src="../images/toolkit_menu.png"><ul><li><a onclick="$(\'#datatable_dlg_'.$this->iListId.'\').dialog(\'open\');">'.$sMenuTitle.'</a></li></li></ul></div>';
  223. //$oPage->add_ready_script("$('#tk_{$this->iListId} > ul').popupmenu();");
  224. return $sHtml;
  225. }
  226. protected function GetTableConfigDlg(WebPage $oPage, $aColumns, $bViewLink, $iDefaultPageSize)
  227. {
  228. $sHtml = "<div id=\"datatable_dlg_{$this->iListId}\" style=\"display: none;\">";
  229. $sHtml .= "<form onsubmit=\"return false\">";
  230. $sChecked = ($this->bUseCustomSettings) ? '' : 'checked';
  231. $sHtml .= "<p><input type=\"radio\" name=\"settings\" $sChecked value=\"defaults\">&nbsp;".Dict::S('UI:UseDefaultSettings').'</p>';
  232. $sHtml .= "<fieldset>";
  233. $sChecked = ($this->bUseCustomSettings) ? 'checked': '';
  234. $sHtml .= "<legend class=\"transparent\"><input type=\"radio\" class=\"specific_settings\" name=\"settings\" $sChecked value=\"specific\">&nbsp;".Dict::S('UI:UseSpecificSettings')."</legend>";
  235. $sHtml .= Dict::S('UI:ColumnsAndSortOrder').'<br/><ul class="sortable_field_list" id="sfl_'.$this->iListId.'"></ul>';
  236. $sHtml .= '<p>'.Dict::Format('UI:Display_X_ItemsPerPage', '<input type="text" size="4" name="page_size" value="'.$iDefaultPageSize.'">').'</p>';
  237. $sHtml .= "</fieldset>";
  238. $sHtml .= "<fieldset>";
  239. $sSaveChecked = ($this->sTableId != null) ? 'checked' : '';
  240. $sCustomChecked = ($this->sTableId != null) ? 'checked' : '';
  241. $sGenericChecked = ($this->sTableId == null) ? 'checked' : '';
  242. $sHtml .= "<legend class=\"transparent\"><input type=\"checkbox\" $sSaveChecked name=\"save_settings\">&nbsp;".Dict::S('UI:UseSavetheSettings')."</legend>";
  243. $sHtml .= '<p><input type="radio" name="scope" '.$sCustomChecked.' value="this_list">&nbsp;'.Dict::S('UI:OnlyForThisList').'&nbsp;&nbsp;&nbsp;&nbsp;';
  244. $sHtml .= '<input type="radio" name="scope" '.$sGenericChecked.' value="defaults">&nbsp;'.Dict::S('UI:ForAllLists').'</p>';
  245. $sHtml .= "</fieldset>";
  246. $sHtml .= '<table style="width:100%"><tr><td style="text-align:center;">';
  247. $sHtml .= '<button type="button" onclick="$(\'#datatable_'.$this->iListId.'\').datatable(\'onDlgCancel\'); $(\'#datatable_dlg_'.$this->iListId.'\').dialog(\'close\')">'.Dict::S('UI:Button:Cancel').'</button>';
  248. $sHtml .= '</td><td style="text-align:center;">';
  249. $sHtml .= '<button type="submit" onclick="$(\'#datatable_'.$this->iListId.'\').datatable(\'onDlgOk\');$(\'#datatable_dlg_'.$this->iListId.'\').dialog(\'close\');">'.Dict::S('UI:Button:Ok').'</button>';
  250. $sHtml .= '</td></tr></table>';
  251. $sHtml .= "</form>";
  252. $sHtml .= "</div>";
  253. $sDlgTitle = addslashes(Dict::S('UI:ListConfigurationTitle'));
  254. $oPage->add_ready_script("$('#datatable_dlg_{$this->iListId}').dialog({autoOpen: false, title: '$sDlgTitle', width: 500, close: function() { $('#datatable_{$this->iListId}').datatable('onDlgCancel'); } });");
  255. return $sHtml;
  256. }
  257. public function GetAsHash($oSetting)
  258. {
  259. $aSettings = array('iDefaultPageSize' => $oSetting->iDefaultPageSize, 'oColumns' => $oSetting->aColumns);
  260. return $aSettings;
  261. }
  262. protected function GetHTMLTableConfig($aColumns, $sSelectMode, $bViewLink)
  263. {
  264. $aAttribs = array();
  265. if ($sSelectMode == 'multiple')
  266. {
  267. $aAttribs['form::select'] = array('label' => "<input type=\"checkbox\" onClick=\"CheckAll('.selectList{$this->iListId}:not(:disabled)', this.checked);\" class=\"checkAll\"></input>", 'description' => Dict::S('UI:SelectAllToggle+'));
  268. }
  269. else if ($sSelectMode == 'single')
  270. {
  271. $aAttribs['form::select'] = array('label' => "", 'description' => '');
  272. }
  273. foreach($this->aClassAliases as $sAlias => $sClassName)
  274. {
  275. foreach($aColumns[$sAlias] as $sAttCode => $aData)
  276. {
  277. if ($aData['checked'])
  278. {
  279. if ($sAttCode == '_key_')
  280. {
  281. $aAttribs['key_'.$sAlias] = array('label' => MetaModel::GetName($sClassName), 'description' => '');
  282. }
  283. else
  284. {
  285. $oAttDef = MetaModel::GetAttributeDef($sClassName, $sAttCode);
  286. $aAttribs[$sAttCode.'_'.$sAlias] = array('label' => MetaModel::GetLabel($sClassName, $sAttCode), 'description' => $oAttDef->GetOrderByHint());
  287. }
  288. }
  289. }
  290. }
  291. return $aAttribs;
  292. }
  293. protected function GetHTMLTableValues($aColumns, $sSelectMode, $iPageSize, $bViewLink, $aExtraParams)
  294. {
  295. $aValues = array();
  296. $this->oSet->Seek(0);
  297. $iMaxObjects = $iPageSize;
  298. while (($aObjects = $this->oSet->FetchAssoc()) && ($iMaxObjects != 0))
  299. {
  300. $bFirstObject = true;
  301. $aRow = array();
  302. foreach($this->aClassAliases as $sAlias => $sClassName)
  303. {
  304. $sHilightClass = $aObjects[$sAlias]->GetHilightClass();
  305. if ($sHilightClass != '')
  306. {
  307. $aRow['@class'] = $sHilightClass;
  308. }
  309. if ((($sSelectMode == 'single') || ($sSelectMode == 'multiple')) && $bFirstObject)
  310. {
  311. if (array_key_exists('selection_enabled', $aExtraParams) && isset($aExtraParams['selection_enabled'][$aObjects[$sAlias]->GetKey()]))
  312. {
  313. $sDisabled = ($aExtraParams['selection_enabled'][$aObjects[$sAlias]->GetKey()]) ? '' : ' disabled="disabled"';
  314. }
  315. else
  316. {
  317. $sDisabled = '';
  318. }
  319. if ($sSelectMode == 'single')
  320. {
  321. $aRow['form::select'] = "<input type=\"radio\" $sDisabled class=\"selectList{$this->iListId}\" name=\"selectObject\" value=\"".$aObjects[$sAlias]->GetKey()."\"></input>";
  322. }
  323. else
  324. {
  325. $aRow['form::select'] = "<input type=\"checkBox\" $sDisabled class=\"selectList{$this->iListId}\" name=\"selectObject[]\" value=\"".$aObjects[$sAlias]->GetKey()."\"></input>";
  326. }
  327. }
  328. foreach($aColumns[$sAlias] as $sAttCode => $aData)
  329. {
  330. if ($aData['checked'])
  331. {
  332. if ($sAttCode == '_key_')
  333. {
  334. $aRow['key_'.$sAlias] = $aObjects[$sAlias]->GetHyperLink();
  335. }
  336. else
  337. {
  338. $aRow[$sAttCode.'_'.$sAlias] = $aObjects[$sAlias]->GetAsHTML($sAttCode);
  339. }
  340. }
  341. }
  342. $bFirstObject = false;
  343. }
  344. $aValues[] = $aRow;
  345. $iMaxObjects--;
  346. }
  347. return $aValues;
  348. }
  349. public function GetHTMLTable(WebPage $oPage, $aColumns, $sSelectMode, $iPageSize, $bViewLink, $aExtraParams)
  350. {
  351. $iNbPages = ($iPageSize == -1) ? 1 : ceil($this->iNbObjects / $iPageSize);
  352. $aAttribs = $this->GetHTMLTableConfig($aColumns, $sSelectMode, $bViewLink);
  353. $aValues = $this->GetHTMLTableValues($aColumns, $sSelectMode, $iPageSize, $bViewLink, $aExtraParams);
  354. $sHtml = '<table class="listContainer">';
  355. foreach($this->oSet->GetFilter()->GetInternalParams() as $sName => $sValue)
  356. {
  357. $aExtraParams['query_params'][$sName] = $sValue;
  358. }
  359. $sHtml .= "<tr><td>";
  360. $sHtml .= $oPage->GetTable($aAttribs, $aValues);
  361. $sHtml .= '</td></tr>';
  362. $sHtml .= '</table>';
  363. $iCount = $this->iNbObjects;
  364. $aArgs = $this->oSet->GetArgs();
  365. $sExtraParams = addslashes(str_replace('"', "'", json_encode(array_merge($aExtraParams, $aArgs)))); // JSON encode, change the style of the quotes and escape them
  366. $sSelectModeJS = '';
  367. $sHeaders = '';
  368. if (($sSelectMode == 'single') || ($sSelectMode == 'multiple'))
  369. {
  370. $sSelectModeJS = $sSelectMode;
  371. $sHeaders = 'headers: { 0: {sorter: false}},';
  372. }
  373. $sDisplayKey = ($bViewLink) ? 'true' : 'false';
  374. // Protect against duplicate elements in the Zlist
  375. $aUniqueOrderedList = array();
  376. foreach($this->aClassAliases as $sAlias => $sClassName)
  377. {
  378. foreach($aColumns[$sAlias] as $sAttCode => $aData)
  379. {
  380. if ($aData['checked'])
  381. {
  382. $aUniqueOrderedList[$sAttCode] = true;
  383. }
  384. }
  385. }
  386. $aUniqueOrderedList = array_keys($aUniqueOrderedList);
  387. $sJSColumns = json_encode($aColumns);
  388. $sJSClassAliases = json_encode($this->aClassAliases);
  389. $sCssCount = isset($aExtraParams['cssCount']) ? ", cssCount: '{$aExtraParams['cssCount']}'" : '';
  390. $this->oSet->ApplyParameters();
  391. // Display the actual sort order of the table
  392. $aRealSortOrder = $this->oSet->GetRealSortOrder();
  393. $aDefaultSort = array();
  394. $iColOffset = 0;
  395. if (($sSelectMode == 'single') || ($sSelectMode == 'multiple'))
  396. {
  397. $iColOffset += 1;
  398. }
  399. if ($bViewLink)
  400. {
  401. $iColOffset += 1;
  402. }
  403. foreach($aRealSortOrder as $sColCode => $bAscending)
  404. {
  405. $iPos = array_search($sColCode, $aUniqueOrderedList);
  406. if ($iPos !== false)
  407. {
  408. $aDefaultSort[] = "[".($iColOffset+$iPos).",".($bAscending ? '0' : '1')."]";
  409. }
  410. else if($sColCode == 'friendlyname' && $bViewLink)
  411. {
  412. $aDefaultSort[] = "[".($iColOffset-1).",".($bAscending ? '0' : '1')."]";
  413. }
  414. }
  415. $sSortList = '';
  416. if (count($aDefaultSort) > 0)
  417. {
  418. $sSortList = ', sortList: ['.implode(',', $aDefaultSort).']';
  419. }
  420. $sOQL = addslashes($this->oSet->GetFilter()->serialize());
  421. $oPage->add_ready_script(
  422. <<<EOF
  423. var oTable = $('#{$this->iListId} table.listResults');
  424. oTable.tablesorter( { $sHeaders widgets: ['myZebra', 'truncatedList'] $sSortList} ).tablesorterPager({container: $('#pager{$this->iListId}'), totalRows:$iCount, size: $iPageSize, filter: '$sOQL', extra_params: '$sExtraParams', select_mode: '$sSelectModeJS', displayKey: $sDisplayKey, columns: $sJSColumns, class_aliases: $sJSClassAliases $sCssCount});
  425. EOF
  426. );
  427. //if ($iNbPages == 1)
  428. if (false)
  429. {
  430. if (isset($aExtraParams['cssCount']))
  431. {
  432. $sCssCount = $aExtraParams['cssCount'];
  433. if ($sSelectMode == 'single')
  434. {
  435. $sSelectSelector = ":radio[name^=selectObj]";
  436. }
  437. else if ($sSelectMode == 'multiple')
  438. {
  439. $sSelectSelector = ":checkbox[name^=selectObj]";
  440. }
  441. $oPage->add_ready_script(
  442. <<<EOF
  443. $('#{$this->iListId} table.listResults $sSelectSelector').change(function() {
  444. var c = $('{$sCssCount}');
  445. var v = $('#{$this->iListId} table.listResults $sSelectSelector:checked').length;
  446. c.val(v);
  447. $('#{$this->iListId} .selectedCount').text(v);
  448. c.trigger('change');
  449. });
  450. EOF
  451. );
  452. }
  453. }
  454. return $sHtml;
  455. }
  456. public function UpdatePager(WebPage $oPage, $iDefaultPageSize, $iStart)
  457. {
  458. $iPageSize = $iDefaultPageSize;
  459. $iPageIndex = 1 + floor($iStart / $iPageSize);
  460. $sHtml = $this->GetPager($oPage, $iPageSize, $iDefaultPageSize, $iPageIndex);
  461. $oPage->add_ready_script("$('#pager{$this->iListId}').html('".str_replace("\n", ' ', addslashes($sHtml))."');");
  462. }
  463. }
  464. class DataTableSettings implements Serializable
  465. {
  466. public $aClassAliases;
  467. public $sTableId;
  468. public $iDefaultPageSize;
  469. public $aColumns;
  470. public function __construct($aClassAliases, $sTableId = null)
  471. {
  472. $this->aClassAliases = $aClassAliases;
  473. $this->sTableId = $sTableId;
  474. $this->iDefaultPageSize = 10;
  475. $this->aColumns = array();
  476. }
  477. protected function Init($iDefaultPageSize, $aSortOrder, $aColumns)
  478. {
  479. $this->iDefaultPageSize = $iDefaultPageSize;
  480. $this->aColumns = $aColumns;
  481. $this->FixVisibleColumns();
  482. }
  483. public function serialize()
  484. {
  485. // Save only the 'visible' columns
  486. $aColumns = array();
  487. foreach($this->aClassAliases as $sAlias => $sClass)
  488. {
  489. $aColumns[$sAlias] = array();
  490. foreach($this->aColumns[$sAlias] as $sAttCode => $aData)
  491. {
  492. unset($aData['label']); // Don't save the display name
  493. unset($aData['alias']); // Don't save the alias (redundant)
  494. unset($aData['code']); // Don't save the code (redundant)
  495. if ($aData['checked'])
  496. {
  497. $aColumns[$sAlias][$sAttCode] = $aData;
  498. }
  499. }
  500. }
  501. return serialize(
  502. array(
  503. 'iDefaultPageSize' => $this->iDefaultPageSize,
  504. 'aColumns' => $aColumns,
  505. )
  506. );
  507. }
  508. public function unserialize($sData)
  509. {
  510. $aData = unserialize($sData);
  511. $this->iDefaultPageSize = $aData['iDefaultPageSize'];
  512. $this->aColumns = $aData['aColumns'];
  513. foreach($this->aClassAliases as $sAlias => $sClass)
  514. {
  515. foreach($this->aColumns[$sAlias] as $sAttCode => $aData)
  516. {
  517. $aFieldData = false;
  518. if ($sAttCode == '_key_')
  519. {
  520. $aFieldData = $this->GetFieldData($sAlias, $sAttCode, null, true /* bChecked */, $aData['sort']);
  521. }
  522. else if (MetaModel::isValidAttCode($sClass, $sAttCode))
  523. {
  524. $oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode);
  525. $aFieldData = $this->GetFieldData($sAlias, $sAttCode, $oAttDef, true /* bChecked */, $aData['sort']);
  526. }
  527. if ($aFieldData)
  528. {
  529. $this->aColumns[$sAlias][$sAttCode] = $aFieldData;
  530. }
  531. else
  532. {
  533. unset($this->aColumns[$sAlias][$sAttCode]);
  534. }
  535. }
  536. }
  537. $this->FixVisibleColumns();
  538. }
  539. static public function GetDataModelSettings($aClassAliases, $bViewLink, $aDefaultLists)
  540. {
  541. $oSettings = new DataTableSettings($aClassAliases);
  542. // Retrieve the class specific settings for each class/alias based on the 'list' ZList
  543. //TODO let the caller pass some other default settings (another Zlist, extre fields...)
  544. $aColumns = array();
  545. foreach($aClassAliases as $sAlias => $sClass)
  546. {
  547. if ($aDefaultLists == null)
  548. {
  549. $aList = cmdbAbstract::FlattenZList(MetaModel::GetZListItems($sClass, 'list'));
  550. }
  551. else
  552. {
  553. $aList = $aDefaultLists[$sAlias];
  554. }
  555. $aSortOrder = MetaModel::GetOrderByDefault($sClass);
  556. if ($bViewLink)
  557. {
  558. $sSort = 'none';
  559. if(array_key_exists('friendlyname', $aSortOrder))
  560. {
  561. $sSort = $aSortOrder['friendlyname'] ? 'asc' : 'desc';
  562. }
  563. $aColumns[$sAlias]['_key_'] = $oSettings->GetFieldData($sAlias, '_key_', null, true /* bChecked */, $sSort);
  564. }
  565. foreach($aList as $sAttCode)
  566. {
  567. $sSort = 'none';
  568. if(array_key_exists($sAttCode, $aSortOrder))
  569. {
  570. $sSort = $aSortOrder[$sAttCode] ? 'asc' : 'desc';
  571. }
  572. $oAttDef = Metamodel::GetAttributeDef($sClass, $sAttCode);
  573. $aFieldData = $oSettings->GetFieldData($sAlias, $sAttCode, $oAttDef, true /* bChecked */, $sSort);
  574. if ($aFieldData) $aColumns[$sAlias][$sAttCode] = $aFieldData;
  575. }
  576. }
  577. $iDefaultPageSize = appUserPreferences::GetPref('default_page_size', MetaModel::GetConfig()->GetMinDisplayLimit());
  578. $oSettings->Init($iDefaultPageSize, $aSortOrder, $aColumns);
  579. return $oSettings;
  580. }
  581. protected function FixVisibleColumns()
  582. {
  583. foreach($this->aClassAliases as $sAlias => $sClass)
  584. {
  585. foreach($this->aColumns[$sAlias] as $sAttCode => $aData)
  586. {
  587. // Remove non-existent columns
  588. // TODO: check if the existing ones are still valid (in case their type changed)
  589. if (($sAttCode != '_key_') && (!MetaModel::IsValidAttCode($sClass, $sAttCode)))
  590. {
  591. unset($this->aColumns[$sAlias][$sAttCode]);
  592. }
  593. }
  594. $aList = MetaModel::ListAttributeDefs($sClass);
  595. // Add the other (non visible ones)
  596. foreach($aList as $sAttCode => $oAttDef)
  597. {
  598. if ( (!array_key_exists($sAttCode, $this->aColumns[$sAlias])) && (!$oAttDef instanceof AttributeLinkSet))
  599. {
  600. $aFieldData = $this->GetFieldData($sAlias, $sAttCode, $oAttDef, false /* bChecked */, 'none');
  601. if ($aFieldData) $this->aColumns[$sAlias][$sAttCode] = $aFieldData;
  602. }
  603. }
  604. }
  605. }
  606. static public function GetTableSettings($aClassAliases, $sTableId = null)
  607. {
  608. $pref = null;
  609. $oSettings = new DataTableSettings($aClassAliases, $sTableId);
  610. if ($sTableId != null)
  611. {
  612. // An identified table, let's fetch its own settings (if any)
  613. $pref = appUserPreferences::GetPref($oSettings->GetPrefsKey($sTableId), null);
  614. }
  615. if ($pref == null)
  616. {
  617. // Try the global preferred values for this class / set of classes
  618. $pref = appUserPreferences::GetPref($oSettings->GetPrefsKey(null), null);
  619. if ($pref == null)
  620. {
  621. // no such settings, use the default values provided by the data model
  622. return null;
  623. }
  624. }
  625. $oSettings->unserialize($pref);
  626. return $oSettings;
  627. }
  628. public function Save()
  629. {
  630. if ($this->sTableId == null) return false; // Cannot save, the table is not identified, use SaveAsDefault instead
  631. $sSettings = $this->serialize();
  632. appUserPreferences::SetPref($this->GetPrefsKey($this->sTableId), $sSettings);
  633. return true;
  634. }
  635. public function SaveAsDefault()
  636. {
  637. $sSettings = $this->serialize();
  638. appUserPreferences::SetPref($this->GetPrefsKey(null), $sSettings);
  639. return true;
  640. }
  641. /**
  642. * Clear the preferences for this particular table
  643. * @param $bResetAll boolean If true,the settings for all tables of the same class(es)/alias(es) are reset
  644. */
  645. public function ResetToDefault($bResetAll)
  646. {
  647. if (($this->sTableId == null) && (!$bResetAll)) return false; // Cannot reset, the table is not identified, use force $bResetAll instead
  648. if ($bResetAll)
  649. {
  650. // Turn the key into a suitable PCRE pattern
  651. $sKey = $this->GetPrefsKey(null);
  652. $sPattern = '!^'.str_replace(array('*'), array('.*'), $sKey).'$!';
  653. appUserPreferences::UnsetPref($sPattern, true);
  654. }
  655. else
  656. {
  657. appUserPreferences::UnsetPref($this->GetPrefsKey($this->sTableId), false);
  658. }
  659. return true;
  660. }
  661. protected function GetPrefsKey($sTableId = null)
  662. {
  663. if ($sTableId == null) $sTableId = '*';
  664. $aKeys = array();
  665. foreach($this->aClassAliases as $sAlias => $sClass)
  666. {
  667. $aKeys[] = $sAlias.'-'.$sClass;
  668. }
  669. return implode('/', $aKeys).'|'.$sTableId;
  670. }
  671. protected function GetFieldData($sAlias, $sAttCode, $oAttDef, $bChecked, $sSort)
  672. {
  673. $ret = false;
  674. if ($sAttCode == '_key_')
  675. {
  676. $sLabel = Dict::Format('UI:ExtKey_AsLink', MetaModel::GetName($this->aClassAliases[$sAlias]));
  677. $ret = array(
  678. 'label' => $sLabel,
  679. 'checked' => true,
  680. 'disabled' => true,
  681. 'alias' => $sAlias,
  682. 'code' => $sAttCode,
  683. 'sort' => $sSort,
  684. );
  685. }
  686. else if (!$oAttDef->IsLinkSet())
  687. {
  688. $sLabel = $oAttDef->GetLabel();
  689. if ($oAttDef->IsExternalKey())
  690. {
  691. $sLabel = Dict::Format('UI:ExtKey_AsLink', $oAttDef->GetLabel());
  692. }
  693. else if ($oAttDef->IsExternalField())
  694. {
  695. $oExtAttDef = $oAttDef->GetExtAttDef();
  696. $sLabel = Dict::Format('UI:ExtField_AsRemoteField', $oAttDef->GetLabel(), $oExtAttDef->GetLabel());
  697. }
  698. elseif ($oAttDef instanceof AttributeFriendlyName)
  699. {
  700. $sLabel = Dict::Format('UI:ExtKey_AsFriendlyName', $oAttDef->GetLabel());
  701. }
  702. $ret = array(
  703. 'label' => $sLabel,
  704. 'checked' => $bChecked,
  705. 'disabled' => false,
  706. 'alias' => $sAlias,
  707. 'code' => $sAttCode,
  708. 'sort' => $sSort,
  709. );
  710. }
  711. return $ret;
  712. }
  713. }