sortable_table.class.php 41 KB

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