sortable_table.class.php 42 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133
  1. <?php
  2. /* For licensing terms, see /license.txt */
  3. use ChamiloSession as Session;
  4. /**
  5. * This class allows you to display a sortable data-table. It is possible to
  6. * split the data in several pages.
  7. * Using this class you can:
  8. * - automatically create checkboxes of the first table column
  9. * - a "select all" and "deselect all" link is added
  10. * - only if you provide a list of actions for the selected items
  11. * - click on the table header to sort the data
  12. * - choose how many items you see per page
  13. * - navigate through all data-pages
  14. * @package chamilo.library
  15. */
  16. class SortableTable extends HTML_Table
  17. {
  18. /**
  19. * A name for this table
  20. */
  21. public $table_name;
  22. /**
  23. * The page to display
  24. */
  25. public $page_nr;
  26. /**
  27. * The column to sort the data
  28. */
  29. public $column;
  30. /**
  31. * The sorting direction (ASC or DESC)
  32. */
  33. public $direction;
  34. /**
  35. * Number of items to display per page
  36. */
  37. public $per_page;
  38. /**
  39. * The default number of items to display per page
  40. */
  41. public $default_items_per_page;
  42. /**
  43. * A prefix for the URL-parameters, can be used on pages with multiple
  44. * SortableTables
  45. */
  46. public $param_prefix;
  47. /**
  48. * The pager object to split the data in several pages
  49. */
  50. public $pager;
  51. /**
  52. * The total number of items in the table
  53. */
  54. public $total_number_of_items;
  55. /**
  56. * The function to get the total number of items
  57. */
  58. public $get_total_number_function;
  59. /**
  60. * The function to the the data to display
  61. */
  62. public $get_data_function;
  63. /**
  64. * An array with defined column-filters
  65. */
  66. public $column_filters;
  67. /**
  68. * A list of actions which will be available through a select list
  69. */
  70. public $form_actions;
  71. /**
  72. * Additional parameters to pass in the URL
  73. */
  74. public $additional_parameters;
  75. /**
  76. * Additional attributes for the th-tags
  77. */
  78. public $th_attributes;
  79. /**
  80. * Additional attributes for the td-tags
  81. */
  82. public $td_attributes;
  83. /**
  84. * Array with names of the other tables defined on the same page of this
  85. * table
  86. */
  87. public $other_tables;
  88. /**
  89. * Activates the odd even rows
  90. * **/
  91. public $odd_even_rows_enabled = true;
  92. public $use_jqgrid = false;
  93. public $table_id = null;
  94. public $headers = array();
  95. /**
  96. * @var array
  97. * Columns to hide
  98. */
  99. private $columnsToHide = array();
  100. /**
  101. * Create a new SortableTable
  102. * @param string $table_name A name for the table (default = 'table')
  103. * @param string $get_total_number_function A user defined function to get
  104. * the total number of items in the table
  105. * @param string $get_data_function A function to get the data to display on
  106. * the current page
  107. * @param int $default_column The default column on which the data should be
  108. * sorted
  109. * @param int $default_items_per_page The default number of items to show
  110. * on one page
  111. * @param string $default_order_direction The default order direction;
  112. * either the constant 'ASC' or 'DESC'
  113. * @param string $table_id
  114. */
  115. public function __construct(
  116. $table_name = 'table',
  117. $get_total_number_function = null,
  118. $get_data_function = null,
  119. $default_column = 1,
  120. $default_items_per_page = 20,
  121. $default_order_direction = 'ASC',
  122. $table_id = null
  123. ) {
  124. if (empty($table_id)) {
  125. $table_id = $table_name.uniqid();
  126. }
  127. $this->table_id = $table_id;
  128. parent::__construct(array('class' => 'data_table table', 'id' => $table_id));
  129. $this->table_name = $table_name;
  130. $this->additional_parameters = array();
  131. $this->param_prefix = $table_name.'_';
  132. $this->page_nr = Session::read($this->param_prefix.'page_nr', 1);
  133. $this->page_nr = isset($_GET[$this->param_prefix.'page_nr']) ? intval($_GET[$this->param_prefix.'page_nr']) : $this->page_nr;
  134. $this->column = Session::read($this->param_prefix.'column', $default_column);
  135. $this->column = isset($_GET[$this->param_prefix.'column']) ? intval($_GET[$this->param_prefix.'column']) : $this->column;
  136. // Default direction.
  137. if (in_array(strtoupper($default_order_direction), array('ASC', 'DESC'))) {
  138. $this->direction = $default_order_direction;
  139. }
  140. $my_session_direction = Session::read($this->param_prefix.'direction');
  141. if (!empty($my_session_direction)) {
  142. if (!in_array($my_session_direction, array('ASC', 'DESC'))) {
  143. $this->direction = 'ASC';
  144. } else {
  145. if ($my_session_direction == 'ASC') {
  146. $this->direction = 'ASC';
  147. } elseif ($my_session_direction == 'DESC') {
  148. $this->direction = 'DESC';
  149. }
  150. }
  151. }
  152. if (isset($_GET[$this->param_prefix.'direction'])) {
  153. $my_get_direction = $_GET[$this->param_prefix.'direction'];
  154. if (!in_array($my_get_direction, array('ASC', 'DESC'))) {
  155. $this->direction = 'ASC';
  156. } else {
  157. if ($my_get_direction == 'ASC') {
  158. $this->direction = 'ASC';
  159. } elseif ($my_get_direction == 'DESC') {
  160. $this->direction = 'DESC';
  161. }
  162. }
  163. }
  164. // Allow to change paginate in multiples tabs
  165. Session::erase($this->param_prefix.'per_page');
  166. $this->per_page = Session::read($this->param_prefix.'per_page', $default_items_per_page);
  167. $this->per_page = isset($_GET[$this->param_prefix.'per_page']) ? intval($_GET[$this->param_prefix.'per_page']) : $this->per_page;
  168. Session::write($this->param_prefix.'per_page', $this->per_page);
  169. Session::write($this->param_prefix.'direction', $this->direction);
  170. Session::write($this->param_prefix.'page_nr', $this->page_nr);
  171. Session::write($this->param_prefix.'column', $this->column);
  172. $this->pager = null;
  173. $this->default_items_per_page = $default_items_per_page;
  174. $this->total_number_of_items = -1;
  175. $this->get_total_number_function = $get_total_number_function;
  176. $this->get_data_function = $get_data_function;
  177. $this->column_filters = array();
  178. $this->form_actions = array();
  179. $this->checkbox_name = null;
  180. $this->td_attributes = array();
  181. $this->th_attributes = array();
  182. $this->other_tables = array();
  183. }
  184. /**
  185. * Get the Pager object to split the showed data in several pages
  186. */
  187. public function get_pager()
  188. {
  189. if (is_null($this->pager)) {
  190. $total_number_of_items = $this->get_total_number_of_items();
  191. $params['mode'] = 'Sliding';
  192. $params['perPage'] = $this->per_page;
  193. $params['totalItems'] = $total_number_of_items;
  194. $params['urlVar'] = $this->param_prefix.'page_nr';
  195. $params['currentPage'] = $this->page_nr;
  196. $icon_attributes = array('style' => 'vertical-align: middle;');
  197. $params['prevImg'] = Display:: return_icon('action_prev.png', get_lang('PreviousPage'), $icon_attributes);
  198. $params['nextImg'] = Display:: return_icon('action_next.png', get_lang('NextPage'), $icon_attributes);
  199. $params['firstPageText'] = Display:: return_icon('action_first.png', get_lang('FirstPage'), $icon_attributes);
  200. $params['lastPageText'] = Display:: return_icon('action_last.png', get_lang('LastPage'), $icon_attributes);
  201. $params['firstPagePre'] = '';
  202. $params['lastPagePre'] = '';
  203. $params['firstPagePost'] = '';
  204. $params['lastPagePost'] = '';
  205. $params['spacesBeforeSeparator'] = '';
  206. $params['spacesAfterSeparator'] = '';
  207. $query_vars = array_keys($_GET);
  208. $query_vars_needed = array($this->param_prefix.'column', $this->param_prefix.'direction', $this->param_prefix.'per_page');
  209. if (count($this->additional_parameters) > 0) {
  210. $query_vars_needed = array_merge($query_vars_needed, array_keys($this->additional_parameters));
  211. }
  212. $query_vars_exclude = array_diff($query_vars, $query_vars_needed);
  213. $params['excludeVars'] = $query_vars_exclude;
  214. $this->pager = & Pager::factory($params);
  215. }
  216. return $this->pager;
  217. }
  218. /**
  219. * Display the table
  220. */
  221. public function display()
  222. {
  223. echo $this->return_table();
  224. }
  225. /**
  226. * Displays the table, complete with navigation buttons to browse through
  227. * the data-pages.
  228. */
  229. public function return_table()
  230. {
  231. $empty_table = false;
  232. $content = $this->get_table_html();
  233. if ($this->get_total_number_of_items() == 0) {
  234. $cols = $this->getColCount();
  235. $this->setCellAttributes(1, 0, 'style="font-style: italic;text-align:center;" colspan='.$cols);
  236. $message_empty = api_xml_http_response_encode(get_lang('TheListIsEmpty'));
  237. $this->setCellContents(1, 0, $message_empty);
  238. $empty_table = true;
  239. }
  240. $html = '';
  241. if (!$empty_table) {
  242. $table_id = 'form_'.$this->table_name.'_id';
  243. $form = $this->get_page_select_form();
  244. $nav = $this->get_navigation_html();
  245. // Only show pagination info when there are items to paginate
  246. if ($this->get_total_number_of_items() > $this->default_items_per_page) {
  247. $html = '<div class="table-well">';
  248. $html .= '<table class="data_table_pagination">';
  249. $html .= '<tr>';
  250. $html .= '<td style="width:25%;">';
  251. $html .= $form;
  252. $html .= '</td>';
  253. $html .= '<td style="text-align:center;">';
  254. $html .= $this->get_table_title();
  255. $html .= '</td>';
  256. $html .= '<td style="text-align:right;width:25%;">';
  257. $html .= $nav;
  258. $html .= '</td>';
  259. $html .= '</tr>';
  260. $html .= '</table>';
  261. $html .= '</div>';
  262. }
  263. if (count($this->form_actions) > 0) {
  264. $params = $this->get_sortable_table_param_string().'&amp;'.$this->get_additional_url_paramstring();
  265. $html .= '<form id ="'.$table_id.'" class="form-search" method="post" action="'.api_get_self().'?'.$params.'" name="form_'.$this->table_name.'">';
  266. }
  267. }
  268. $html .= $content;
  269. if (!$empty_table) {
  270. if (!empty($this->additional_parameters)) {
  271. foreach ($this->additional_parameters as $key => $value) {
  272. $html .= '<input type="hidden" name ="'.Security::remove_XSS($key).'" value ="'.Security::remove_XSS($value).'" />';
  273. }
  274. }
  275. $html .= '<input type="hidden" name="action">';
  276. $html .= '<table style="width:100%;">';
  277. $html .= '<tr>';
  278. $html .= '<td>';
  279. if (count($this->form_actions) > 0) {
  280. $html .= '<div class="btn-toolbar">';
  281. $html .= '<div class="btn-group">';
  282. $html .= '<a class="btn btn-default" href="?'.$params.'&amp;'.$this->param_prefix.'selectall=1" onclick="javascript: setCheckbox(true, \''.$table_id.'\'); return false;">'.get_lang('SelectAll').'</a>';
  283. $html .= '<a class="btn btn-default" href="?'.$params.'" onclick="javascript: setCheckbox(false, \''.$table_id.'\'); return false;">'.get_lang('UnSelectAll').'</a> ';
  284. $html .= '</div>';
  285. $html .= '<div class="btn-group">
  286. <button class="btn btn-default" onclick="javascript:return false;">'.get_lang('Actions').'</button>
  287. <button class="btn btn-default dropdown-toggle" data-toggle="dropdown">
  288. <span class="caret"></span>
  289. </button>';
  290. $html .= '<ul class="dropdown-menu">';
  291. foreach ($this->form_actions as $action => & $label) {
  292. $html .= '<li><a data-action ="'.$action.'" href="#" onclick="javascript:action_click(this, \''.$table_id.'\');">'.$label.'</a></li>';
  293. }
  294. $html .= '</ul>';
  295. $html .= '</div>'; //btn-group
  296. $html .= '</div>'; //toolbar
  297. } else {
  298. $html .= $form;
  299. }
  300. $html .= '</td>';
  301. // Pagination
  302. if ($this->get_total_number_of_items() > $this->default_items_per_page) {
  303. $html .= '<td style="text-align:right;">';
  304. $html .= $nav;
  305. $html .= '</td>';
  306. } else {
  307. $html .= '<td> ';
  308. $html .= '</td>';
  309. }
  310. $html .= '</tr>';
  311. $html .= '</table>';
  312. if (count($this->form_actions) > 0) {
  313. $html .= '</form>';
  314. }
  315. }
  316. return '<div class="table-responsive">'.$html.'</div>';
  317. }
  318. /**
  319. * This function shows the content of a table in a grid.
  320. * Should not be use to edit information (edit/delete rows) only.
  321. **/
  322. public function display_grid()
  323. {
  324. $empty_table = false;
  325. if ($this->get_total_number_of_items() == 0) {
  326. $cols = $this->getColCount();
  327. //$this->setCellAttributes(1, 0, 'style="font-style: italic;text-align:center;" colspan='.$cols);
  328. $message_empty = api_xml_http_response_encode(get_lang('TheListIsEmpty'));
  329. $this->setCellContents(1, 0, $message_empty);
  330. $empty_table = true;
  331. }
  332. $html = '';
  333. if (!$empty_table) {
  334. $form = $this->get_page_select_form();
  335. $nav = $this->get_navigation_html();
  336. // @todo This style css must be moved to default.css only for dev
  337. echo '<style>
  338. .main-grid { width:100%;}
  339. .sub-header { width:100%; padding-top: 10px; padding-right: 10px; padding-left: 10px; height:30px;}
  340. .grid_container { width:100%;}
  341. .grid_item { height: 120px; width:98px; float:left; padding:5px; margin:8px;}
  342. .grid_element_0 { width:100px; height: 100px; float:left; text-align:center; margin-bottom:5px;}
  343. .grid_element_1 { width:100px; float:left; text-align:center;margin-bottom:5px;}
  344. .grid_element_2 { width:150px; float:left;}
  345. .grid_selectbox { width:30%; float:left;}
  346. .grid_title { width:30%; float:left;}
  347. .grid_nav { }
  348. </style>';
  349. // @todo This also must be moved
  350. // Show only navigations if there are more than 1 page
  351. $my_pager = $this->get_pager();
  352. $html .= '<div class="main-grid">';
  353. if ($my_pager->numPages() > 1) {
  354. $html .= '<div class="sub-header">';
  355. $html .= '<div class="grid_selectbox">'.$form.'</div>';
  356. $html .= '<div class="grid_title">'.$this->get_table_title().'</div>';
  357. $html .= '<div class="grid_nav">'.$nav.'</div>';
  358. $html .= '</div>';
  359. }
  360. $html .= '<div class="clear"></div>';
  361. if (count($this->form_actions) > 0) {
  362. $script = '<script>
  363. /*<![CDATA[*/
  364. function setCheckbox(value) {
  365. d = document.form_'.$this->table_name.';
  366. for (i = 0; i < d.elements.length; i++) {
  367. if (d.elements[i].type == "checkbox") {
  368. d.elements[i].checked = value;
  369. }
  370. if (value) {
  371. $(d.elements[i]).parentsUntil("tr").parent().addClass("row_selected");
  372. } else {
  373. $(d.elements[i]).parentsUntil("tr").parent().removeClass("row_selected");
  374. }
  375. }
  376. }
  377. /*]]>*/
  378. </script>';
  379. $params = $this->get_sortable_table_param_string().'&amp;'.$this->get_additional_url_paramstring();
  380. $html .= '<form method="post" action="'.api_get_self().'?'.$params.'" name="form_'.$this->table_name.'">';
  381. }
  382. }
  383. // Getting the items of the table
  384. $items = $this->get_clean_html(false); //no sort
  385. // Generation of style classes must be improved. Maybe we need a a table name to create style on the fly:
  386. // i.e: .whoisonline_table_grid_container instead of .grid_container
  387. // where whoisonline is the table's name like drupal's template engine
  388. $html .= '<div class="grid_container">';
  389. if (is_array($items) && count($items) > 0) {
  390. foreach ($items as & $row) {
  391. $html .= '<div class="grid_item">';
  392. $i = 0;
  393. foreach ($row as & $element) {
  394. $html .= '<div class="grid_element_'.$i.'">'.$element.'</div>';
  395. $i++;
  396. }
  397. $html .= '</div>';
  398. }
  399. }
  400. $html .= '</div>'; //close grid_container
  401. $html .= '</div>'; //close main grid
  402. $html .= '<div class="clear"></div>';
  403. echo $html;
  404. }
  405. /**
  406. * This function returns the content of a table in a grid
  407. * Should not be use to edit information (edit/delete rows) only.
  408. * @param array options of visibility
  409. * @param bool hide navigation optionally
  410. * @param int content per page when show navigation (optional)
  411. * @param bool sort data optionally
  412. * @return string grid html
  413. */
  414. public function display_simple_grid($visibility_options, $hide_navigation = true, $per_page = 20, $sort_data = true, $grid_class = array())
  415. {
  416. $empty_table = false;
  417. $total = $this->get_total_number_of_items();
  418. if ($this->get_total_number_of_items() == 0) {
  419. $cols = $this->getColCount();
  420. //$this->setCellAttributes(1, 0, 'style="font-style: italic;text-align:center;" colspan='.$cols);
  421. $message_empty = api_xml_http_response_encode(get_lang('TheListIsEmpty'));
  422. $this->setCellContents(1, 0, $message_empty);
  423. $empty_table = true;
  424. }
  425. $html = '';
  426. if (!$empty_table) {
  427. // If we show the pagination
  428. if (!$hide_navigation) {
  429. $form = '&nbsp;';
  430. if ($this->get_total_number_of_items() > $per_page) {
  431. if ($per_page > 10) {
  432. $form = $this->get_page_select_form();
  433. }
  434. $nav = $this->get_navigation_html();
  435. // This also must be moved
  436. $html = '<div class="sub-header">';
  437. $html .= '<div class="grid_selectbox">'.$form.'</div>';
  438. $html .= '<div class="grid_title">'.$this->get_table_title().'</div>';
  439. $html .= '<div class="grid_nav">'.$nav.'</div>';
  440. $html .= '</div>';
  441. }
  442. }
  443. $html .= '<div class="clear"></div>';
  444. if (count($this->form_actions) > 0) {
  445. $script = '<script>
  446. /*<![CDATA[*/
  447. function setCheckbox(value) {
  448. d = document.form_'.$this->table_name.';
  449. for (i = 0; i < d.elements.length; i++) {
  450. if (d.elements[i].type == "checkbox") {
  451. d.elements[i].checked = value;
  452. }
  453. if (value) {
  454. $(d.elements[i]).parentsUntil("tr").parent().addClass("row_selected");
  455. } else {
  456. $(d.elements[i]).parentsUntil("tr").parent().removeClass("row_selected");
  457. }
  458. }
  459. }
  460. /*]]>*/
  461. </script>';
  462. $params = $this->get_sortable_table_param_string().'&amp;'.$this->get_additional_url_paramstring();
  463. $html .= '<form method="post" action="'.api_get_self().'?'.$params.'" name="form_'.$this->table_name.'">';
  464. }
  465. }
  466. if ($hide_navigation) {
  467. $items = $this->table_data; // This is a faster way to get what we want
  468. } else {
  469. // The normal way
  470. $items = $this->get_clean_html($sort_data); // Getting the items of the table
  471. }
  472. // Generation of style classes must be improved. Maybe we need a a table name to create style on the fly:
  473. // i.e: .whoisonline_table_grid_container instead of .grid_container
  474. // where whoisonline is the table's name like drupal's template engine
  475. if (is_array($visibility_options)) {
  476. $filter = false; // The 2nd condition of the if will be loaded
  477. } else {
  478. $filter = $visibility_options !== false;
  479. }
  480. $item_css_class = $item_css_style = $grid_css_class = $grid_css_style = '';
  481. if (!empty($grid_class)) {
  482. $grid_css_class = $grid_class['main']['class'];
  483. $item_css_class = $grid_class['item']['class'];
  484. $grid_css_style = isset($grid_class['main']['style']) ? $grid_class['main']['style'] : null;
  485. $item_css_style = isset($grid_class['item']['style']) ? $grid_class['item']['style'] : null;
  486. }
  487. $div = '';
  488. if (is_array($items) && count($items) > 0) {
  489. foreach ($items as & $row) {
  490. $i = 0;
  491. $rows = '';
  492. foreach ($row as & $element) {
  493. if ($filter ||
  494. isset($visibility_options[$i]) && $visibility_options[$i]
  495. ) {
  496. $rows .= '<div class="'.$this->table_name.'_grid_element_'.$i.'">'.$element.'</div>';
  497. }
  498. $i++;
  499. }
  500. $div .= Display::div($rows, array('class'=>$item_css_class.' '.$this->table_name.'_grid_item', 'style' => $item_css_style));
  501. }
  502. }
  503. $html .= Display::div($div, array('class'=>$grid_css_class.' '.$this->table_name.'_grid_container', 'style' => $grid_css_style));
  504. $html .= '<div class="clear"></div>';
  505. return $html;
  506. }
  507. /**
  508. * Get the HTML-code with the navigational buttons to browse through the
  509. * data-pages.
  510. */
  511. public function get_navigation_html()
  512. {
  513. $pager = $this->get_pager();
  514. $pager_links = $pager->getLinks();
  515. $nav = $pager_links['first'].' '.$pager_links['back'];
  516. $nav .= ' '.$pager->getCurrentPageId().' / '.$pager->numPages().' ';
  517. $nav .= $pager_links['next'].' '.$pager_links['last'];
  518. return $nav;
  519. }
  520. /**
  521. * Get the HTML-code with the data-table.
  522. */
  523. public function get_table_html()
  524. {
  525. $pager = $this->get_pager();
  526. $offset = $pager->getOffsetByPageId();
  527. $from = $offset[0] - 1;
  528. $table_data = $this->get_table_data($from);
  529. $this->processHeaders();
  530. if (is_array($table_data)) {
  531. $count = 1;
  532. foreach ($table_data as & $row) {
  533. $row = $this->filter_data($row);
  534. $newRow = array();
  535. if (!empty($this->columnsToHide)) {
  536. $counter = 0;
  537. foreach ($row as $index => $rowInfo) {
  538. if (!isset($this->columnsToHide[$index])) {
  539. $newRow[$counter] = $rowInfo;
  540. $counter++;
  541. }
  542. }
  543. $row = $newRow;
  544. }
  545. $this->addRow($row);
  546. if (isset($row['child_of'])) {
  547. $this->setRowAttributes($count, array('class' => 'hidden hidden_'.$row['child_of']), true);
  548. }
  549. $count++;
  550. }
  551. }
  552. if ($this->odd_even_rows_enabled == true) {
  553. $this->altRowAttributes(0, array('class' => 'row_odd'), array('class' => 'row_even'), true);
  554. }
  555. foreach ($this->th_attributes as $column => $attributes) {
  556. $this->setCellAttributes(0, $column, $attributes);
  557. }
  558. foreach ($this->td_attributes as $column => $attributes) {
  559. $this->setColAttributes($column, $attributes);
  560. }
  561. return $this->toHTML();
  562. }
  563. /**
  564. * This function return the items of the table
  565. * @param bool true for sorting table data or false otherwise
  566. * @return array table row items
  567. */
  568. public function get_clean_html($sort = true)
  569. {
  570. $pager = $this->get_pager();
  571. $offset = $pager->getOffsetByPageId();
  572. $from = $offset[0] - 1;
  573. $table_data = $this->get_table_data($from, null, null, null, $sort);
  574. $new_table_data = array();
  575. if (is_array($table_data)) {
  576. foreach ($table_data as $index => & $row) {
  577. $row = $this->filter_data($row);
  578. $new_table_data[] = $row;
  579. }
  580. }
  581. return $new_table_data;
  582. }
  583. /**
  584. * Get the HTML-code which represents a form to select how many items a page
  585. * should contain.
  586. */
  587. public function get_page_select_form()
  588. {
  589. $total_number_of_items = $this->get_total_number_of_items();
  590. if ($total_number_of_items <= $this->default_items_per_page) {
  591. return '';
  592. }
  593. $result[] = '<form method="GET" action="'.api_get_self().'" style="display:inline;">';
  594. $param[$this->param_prefix.'direction'] = $this->direction;
  595. $param[$this->param_prefix.'page_nr'] = $this->page_nr;
  596. $param[$this->param_prefix.'column'] = $this->column;
  597. if (is_array($this->additional_parameters)) {
  598. $param = array_merge($param, $this->additional_parameters);
  599. }
  600. foreach ($param as $key => & $value) {
  601. $result[] = '<input type="hidden" name="'.$key.'" value="'.$value.'"/>';
  602. }
  603. $result[] = '<select name="'.$this->param_prefix.'per_page" onchange="javascript: this.form.submit();">';
  604. for ($nr = 10; $nr <= min(50, $total_number_of_items); $nr += 10) {
  605. $result[] = '<option value="'.$nr.'" '.($nr == $this->per_page ? 'selected="selected"' : '').'>'.$nr.'</option>';
  606. }
  607. // @todo no limits
  608. //if ($total_number_of_items < 500) {
  609. $result[] = '<option value="'.$total_number_of_items.'" '.($total_number_of_items == $this->per_page ? 'selected="selected"' : '').'>'.api_ucfirst(get_lang('All')).'</option>';
  610. //}
  611. $result[] = '</select>';
  612. $result[] = '<noscript>';
  613. $result[] = '<button class="btn btn-success" type="submit">'.get_lang('Save').'</button>';
  614. $result[] = '</noscript>';
  615. $result[] = '</form>';
  616. $result = implode("\n", $result);
  617. return $result;
  618. }
  619. /**
  620. * Get the table title.
  621. */
  622. public function get_table_title()
  623. {
  624. $pager = $this->get_pager();
  625. $showed_items = $pager->getOffsetByPageId();
  626. return $showed_items[0].' - '.$showed_items[1].' / '.$this->get_total_number_of_items();
  627. }
  628. /**
  629. * @return array
  630. */
  631. public function getHeaders()
  632. {
  633. return $this->headers;
  634. }
  635. /**
  636. * Process headers
  637. */
  638. public function processHeaders()
  639. {
  640. $counter = 0;
  641. foreach ($this->headers as $column => $columnInfo) {
  642. $label = $columnInfo['label'];
  643. $sortable = $columnInfo['sortable'];
  644. $th_attributes = $columnInfo['th_attributes'];
  645. $td_attributes = $columnInfo['td_attributes'];
  646. if (!empty($this->columnsToHide)) {
  647. if (isset($this->columnsToHide[$column])) {
  648. continue;
  649. }
  650. }
  651. $column = $counter;
  652. $param['direction'] = 'ASC';
  653. if ($this->column == $column && $this->direction == 'ASC') {
  654. $param['direction'] = 'DESC';
  655. }
  656. $param['page_nr'] = $this->page_nr;
  657. $param['per_page'] = $this->per_page;
  658. $param['column'] = $column;
  659. if ($sortable) {
  660. $link = '<a href="'.api_get_self().'?'.api_get_cidreq().'&amp;';
  661. foreach ($param as $key => & $value) {
  662. $link .= $this->param_prefix.$key.'='.urlencode($value).'&amp;';
  663. }
  664. $link .= $this->get_additional_url_paramstring();
  665. $link .= '">'.$label.'</a>';
  666. if ($this->column == $column) {
  667. $link .= $this->direction == 'ASC' ? ' &#8595;' : ' &#8593;';
  668. }
  669. } else {
  670. $link = $label;
  671. }
  672. $this->setHeaderContents(0, $column, $link);
  673. if (!is_null($td_attributes)) {
  674. $this->td_attributes[$column] = $td_attributes;
  675. }
  676. if (!is_null($th_attributes)) {
  677. $this->th_attributes[$column] = $th_attributes;
  678. }
  679. $counter++;
  680. }
  681. }
  682. /**
  683. * Set the header-label
  684. * @param int $column The column number
  685. * @param string $label The label
  686. * @param boolean $sortable Is the table sortable by this column? (defatult
  687. * = true)
  688. * @param string $th_attributes Additional attributes for the th-tag of the
  689. * table header
  690. * @param string $td_attributes Additional attributes for the td-tags of the
  691. * column
  692. */
  693. public function set_header(
  694. $column,
  695. $label,
  696. $sortable = true,
  697. $th_attributes = array('class' => 'th-header'),
  698. $td_attributes = null
  699. ) {
  700. $this->headers[$column] = array(
  701. 'label' => $label,
  702. 'sortable' => $sortable,
  703. 'th_attributes' => $th_attributes,
  704. 'td_attributes' => $td_attributes,
  705. );
  706. }
  707. /**
  708. * Get the parameter-string with additional parameters to use in the URLs
  709. * generated by this SortableTable
  710. */
  711. public function get_additional_url_paramstring()
  712. {
  713. $param_string_parts = array();
  714. if (is_array($this->additional_parameters) && count($this->additional_parameters) > 0) {
  715. foreach ($this->additional_parameters as $key => & $value) {
  716. $param_string_parts[] = urlencode($key).'='.urlencode($value);
  717. }
  718. }
  719. $result = implode('&amp;', $param_string_parts);
  720. foreach ($this->other_tables as $index => & $tablename) {
  721. $param = array();
  722. if (isset($_GET[$tablename.'_direction'])) {
  723. //$param[$tablename.'_direction'] = $_GET[$tablename.'_direction'];
  724. $my_get_direction = $_GET[$tablename.'_direction'];
  725. if (!in_array($my_get_direction, array('ASC', 'DESC'))) {
  726. $param[$tablename.'_direction'] = 'ASC';
  727. } else {
  728. $param[$tablename.'_direction'] = $my_get_direction;
  729. }
  730. }
  731. if (isset($_GET[$tablename.'_page_nr'])) {
  732. $param[$tablename.'_page_nr'] = intval($_GET[$tablename.'_page_nr']);
  733. }
  734. if (isset($_GET[$tablename.'_per_page'])) {
  735. $param[$tablename.'_per_page'] = intval($_GET[$tablename.'_per_page']);
  736. }
  737. if (isset($_GET[$tablename.'_column'])) {
  738. $param[$tablename.'_column'] = intval($_GET[$tablename.'_column']);
  739. }
  740. $param_string_parts = array();
  741. foreach ($param as $key => & $value) {
  742. $param_string_parts[] = urlencode($key).'='.urlencode($value);
  743. }
  744. if (count($param_string_parts) > 0) {
  745. $result .= '&amp;'.implode('&amp;', $param_string_parts);
  746. }
  747. }
  748. return $result;
  749. }
  750. /**
  751. * Get the parameter-string with the SortableTable-related parameters to use
  752. * in URLs
  753. */
  754. public function get_sortable_table_param_string()
  755. {
  756. $param[$this->param_prefix.'direction'] = $this->direction;
  757. $param[$this->param_prefix.'page_nr'] = $this->page_nr;
  758. $param[$this->param_prefix.'per_page'] = $this->per_page;
  759. $param[$this->param_prefix.'column'] = $this->column;
  760. $param_string_parts = array();
  761. foreach ($param as $key => & $value) {
  762. $param_string_parts[] = urlencode($key).'='.urlencode($value);
  763. }
  764. $res = implode('&amp;', $param_string_parts);
  765. return $res;
  766. }
  767. /**
  768. * Add a filter to a column. If another filter was allready defined for the
  769. * given column, it will be overwritten.
  770. * @param int $column The number of the column
  771. * @param string $function The name of the filter-function. This should be a
  772. * function wich requires 1 parameter and returns the filtered value.
  773. */
  774. public function set_column_filter($column, $function)
  775. {
  776. $this->column_filters[$column] = $function;
  777. }
  778. /**
  779. * List of columns to hide
  780. * @param int $column
  781. */
  782. public function setHideColumn($column)
  783. {
  784. $this->columnsToHide[$column] = $column;
  785. }
  786. /**
  787. * Define a list of actions which can be performed on the table-date.
  788. * If you define a list of actions, the first column of the table will be
  789. * converted into checkboxes.
  790. * @param array $actions A list of actions. The key is the name of the
  791. * action. The value is the label to show in the select-box
  792. * @param string $checkbox_name The name of the generated checkboxes. The
  793. * value of the checkbox will be the value of the first column.
  794. */
  795. public function set_form_actions($actions, $checkbox_name = 'id')
  796. {
  797. $this->form_actions = $actions;
  798. $this->checkbox_name = $checkbox_name;
  799. }
  800. /**
  801. * Define a list of additional parameters to use in the generated URLs
  802. * <code>$parameters['action'] = 'test'; will be convert in <input type="hidden" name="action" value="test"></code>
  803. * @param array $parameters
  804. */
  805. public function set_additional_parameters($parameters)
  806. {
  807. $this->additional_parameters = $parameters;
  808. }
  809. /**
  810. * Set other tables on the same page.
  811. * If you have other sortable tables on the page displaying this sortable
  812. * tables, you can define those other tables with this function. If you
  813. * don't define the other tables, there sorting and pagination will return
  814. * to their default state when sorting this table.
  815. * @param array $tablenames An array of table names.
  816. */
  817. public function set_other_tables($tablenames)
  818. {
  819. $this->other_tables = $tablenames;
  820. }
  821. /**
  822. * Transform all data in a table-row, using the filters defined by the
  823. * function set_column_filter(...) defined elsewhere in this class.
  824. * If you've defined actions, the first element of the given row will be
  825. * converted into a checkbox
  826. * @param array $row A row from the table.
  827. */
  828. public function filter_data($row)
  829. {
  830. $url_params = $this->get_sortable_table_param_string().'&'.$this->get_additional_url_paramstring();
  831. foreach ($this->column_filters as $column => & $function) {
  832. $firstParam = isset($row[$column]) ? $row[$column] : 0;
  833. $row[$column] = call_user_func($function, $firstParam, $url_params, $row);
  834. }
  835. if (count($this->form_actions) > 0) {
  836. if (strlen($row[0]) > 0) {
  837. $row[0] = '<input type="checkbox" name="'.$this->checkbox_name.'[]" value="'.$row[0].'"';
  838. if (isset($_GET[$this->param_prefix.'selectall'])) {
  839. $row[0] .= ' checked="checked"';
  840. }
  841. $row[0] .= '/>';
  842. }
  843. }
  844. if (is_array($row)) {
  845. foreach ($row as & $value) {
  846. if (empty($value)) {
  847. $value = '-';
  848. }
  849. }
  850. }
  851. return $row;
  852. }
  853. /**
  854. * Get the total number of items. This function calls the function given as
  855. * 2nd argument in the constructor of a SortableTable. Make sure your
  856. * function has the same parameters as defined here.
  857. */
  858. public function get_total_number_of_items()
  859. {
  860. if ($this->total_number_of_items == -1 && !is_null($this->get_total_number_function)) {
  861. $this->total_number_of_items = call_user_func($this->get_total_number_function);
  862. }
  863. return $this->total_number_of_items;
  864. }
  865. /**
  866. * Get the data to display. This function calls the function given as
  867. * 2nd argument in the constructor of a SortableTable. Make sure your
  868. * function has the same parameters as defined here.
  869. * @param int $from Index of the first item to return.
  870. * @param int $per_page The number of items to return
  871. * @param int $column The number of the column on which the data should be
  872. * @param bool $sort Whether to sort or not
  873. * sorted
  874. * @param string $direction In which order should the data be sorted (ASC
  875. * or DESC)
  876. */
  877. public function get_table_data($from = null, $per_page = null, $column = null, $direction = null, $sort = null)
  878. {
  879. $data = [];
  880. if (!is_null($this->get_data_function)) {
  881. $data = call_user_func(
  882. $this->get_data_function,
  883. $from,
  884. $this->per_page,
  885. $this->column,
  886. $this->direction
  887. );
  888. }
  889. return $data;
  890. }
  891. }
  892. /**
  893. * Sortable table which can be used for data available in an array
  894. * @package chamilo.library
  895. */
  896. class SortableTableFromArray extends SortableTable
  897. {
  898. /**
  899. * The array containing all data for this table
  900. */
  901. public $table_data;
  902. /**
  903. * Constructor
  904. * @param array $table_data
  905. * @param int $default_column
  906. * @param int $default_items_per_page
  907. */
  908. public function __construct(
  909. $table_data,
  910. $default_column = 1,
  911. $default_items_per_page = 20,
  912. $tablename = 'tablename',
  913. $get_total_number_function = null
  914. ) {
  915. parent:: __construct(
  916. $tablename,
  917. $get_total_number_function,
  918. null,
  919. $default_column,
  920. $default_items_per_page
  921. );
  922. $this->table_data = $table_data;
  923. }
  924. /**
  925. * Get table data to show on current page
  926. * @see SortableTable#get_table_data
  927. */
  928. public function get_table_data($from = 1, $per_page = null, $column = null, $direction = null, $sort = true)
  929. {
  930. if ($sort) {
  931. $content = TableSort::sort_table($this->table_data, $this->column, $this->direction == 'ASC' ? SORT_ASC : SORT_DESC);
  932. } else {
  933. $content = $this->table_data;
  934. }
  935. return array_slice($content, $from, $this->per_page);
  936. }
  937. /**
  938. * Get total number of items
  939. * @see SortableTable#get_total_number_of_items
  940. */
  941. public function get_total_number_of_items()
  942. {
  943. if (isset($this->total_number_of_items) && !empty($this->total_number_of_items)) {
  944. return $this->total_number_of_items;
  945. } else {
  946. return count($this->table_data);
  947. }
  948. }
  949. }
  950. /**
  951. * Sortable table which can be used for data available in an array
  952. *
  953. * Is a variation of SortableTableFromArray because we add 2 new arrays $column_show and $column_order
  954. * $column_show is an array that lets us decide which are going to be the columns to show
  955. * $column_order is an array that lets us decide the ordering of the columns
  956. * i.e: $column_header=array('a','b','c','d','e'); $column_order=array(1,2,5,4,5);
  957. * These means that the 3th column (letter "c") will be sort like the order we use in the 5th column
  958. * @package chamilo.library
  959. */
  960. class SortableTableFromArrayConfig extends SortableTable
  961. {
  962. /**
  963. * The array containing the columns that will be show i.e $column_show=array('1','0','0'); we will show only the 1st column
  964. */
  965. private $column_show;
  966. /**
  967. *The array containing the real sort column $column_order=array('1''4','3','4'); The 2nd column will be order like the 4th column
  968. */
  969. private $column_order;
  970. /**
  971. * The array containing all data for this table
  972. */
  973. private $table_data;
  974. private $doc_filter;
  975. /**
  976. * Constructor
  977. * @param array $table_data All the information of the table
  978. * @param int $default_column Default column that will be use in the sorts functions
  979. * @param int $default_items_per_page quantity of pages that we are going to see
  980. * @param int $tablename Name of the table
  981. * @param array $column_show An array with binary values 1: we show the column 2: we don't show it
  982. * @param array $column_order An array of integers that let us decide how the columns are going to be sort.
  983. * @param string $direction
  984. * @param bool $doc_filter special modification to fix the document name order
  985. */
  986. public function __construct(
  987. $table_data,
  988. $default_column = 1,
  989. $default_items_per_page = 20,
  990. $tablename = 'tablename',
  991. $column_show = array(),
  992. $column_order = array(),
  993. $direction = 'ASC',
  994. $doc_filter = false
  995. ) {
  996. $this->column_show = $column_show;
  997. $this->column_order = $column_order;
  998. $this->doc_filter = $doc_filter;
  999. parent::__construct($tablename, null, null, $default_column, $default_items_per_page, $direction);
  1000. $this->table_data = $table_data;
  1001. }
  1002. /**
  1003. * Get table data to show on current page
  1004. * @see SortableTable#get_table_data
  1005. */
  1006. public function get_table_data(
  1007. $from = 1,
  1008. $per_page = null,
  1009. $column = null,
  1010. $direction = null,
  1011. $sort = true
  1012. ) {
  1013. $content = TableSort::sort_table_config(
  1014. $this->table_data,
  1015. $this->column,
  1016. $this->direction == 'ASC' ? SORT_ASC : SORT_DESC,
  1017. $this->column_show,
  1018. $this->column_order,
  1019. SORT_REGULAR,
  1020. $this->doc_filter
  1021. );
  1022. return array_slice($content, $from, $this->per_page);
  1023. }
  1024. /**
  1025. * Get total number of items
  1026. * @see SortableTable#get_total_number_of_items
  1027. */
  1028. public function get_total_number_of_items()
  1029. {
  1030. return count($this->table_data);
  1031. }
  1032. }