tablesort.lib.php 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306
  1. <?php
  2. /*
  3. ==============================================================================
  4. Dokeos - elearning and course management software
  5. Copyright (c) 2004-2008 Dokeos S.A.
  6. Copyright (c) 2003 Ghent University (UGent)
  7. Copyright (c) 2001 Universite catholique de Louvain (UCL)
  8. Copyright (c) Bart Mollet (bart.mollet@hogent.be)
  9. For a full list of contributors, see "credits.txt".
  10. The full license can be read in "license.txt".
  11. This program is free software; you can redistribute it and/or
  12. modify it under the terms of the GNU General Public License
  13. as published by the Free Software Foundation; either version 2
  14. of the License, or (at your option) any later version.
  15. See the GNU General Public License for more details.
  16. Contact address: Dokeos, 44 rue des palais, B-1030 Brussels, Belgium
  17. Mail: info@dokeos.com
  18. ==============================================================================
  19. */
  20. /**
  21. ==============================================================================
  22. * This is a library with some functions to sort tabular data
  23. *
  24. * @package dokeos.library
  25. ==============================================================================
  26. */
  27. define('SORT_DATE', 3);
  28. define('SORT_IMAGE',4);
  29. class TableSort
  30. {
  31. /**
  32. * Create a string to use in sorting.
  33. * @param string $txt The string to convert
  34. * @author René Haentjens
  35. */
  36. function orderingstring($txt)
  37. {
  38. return api_strtolower($txt);
  39. }
  40. /**
  41. * This is a method for date comparison, using hidden raw values if they are given.
  42. * Date formats vary a lot, alse they have localized values. For avoiding using
  43. * unreliable in this case date parsing routine, this method checks first whether raw
  44. * date walues have been intentionaly passed in order pricise sorting to be achieved.
  45. * Here is the format of the date value, hidden in a comment: <!--uts=1234685716-->
  46. * @param string $el1 The first element provided from the table.
  47. * @param string $el2 The second element provided from the table.
  48. * @result bool Tre comparison result.
  49. * @author Ivan Tcholakov, 2010.
  50. */
  51. public function date_compare($el1, $el2) {
  52. if (($pos1 = strpos($el1, '<!--uts=')) !== false && ($pos2 = strpos($el1, '-->', $pos1)) !== false) {
  53. $el1 = intval(substr($el1, $pos1 + 8, $pos2 - $pos1 - 8));
  54. } else {
  55. $el1 = strtotime(strip_tags($el1));
  56. }
  57. if (($pos1 = strpos($el2, '<!--uts=')) !== false && ($pos2 = strpos($el2, '-->', $pos1)) !== false) {
  58. $el2 = intval(substr($el2, $pos1 + 8, $pos2 - $pos1 - 8));
  59. } else {
  60. $el2 = strtotime(strip_tags($el2));
  61. }
  62. if ($el1 > $el2) {
  63. return 1;
  64. } elseif ($el1 > $el2) {
  65. return -1;
  66. }
  67. return 0;
  68. }
  69. /**
  70. * Sort 2-dimensional table.
  71. * @param array $data The data to be sorted.
  72. * @param int $column The column on which the data should be sorted (default = 0)
  73. * @param string $direction The direction to sort (SORT_ASC (default) or SORT_DESC)
  74. * @param constant $type How should data be sorted (SORT_REGULAR, SORT_NUMERIC,
  75. * SORT_STRING,SORT_DATE,SORT_IMAGE)
  76. * @return array The sorted dataset
  77. * @author bart.mollet@hogent.be
  78. */
  79. function sort_table($data, $column = 0, $direction = SORT_ASC, $type = SORT_REGULAR)
  80. {
  81. if(!is_array($data) or count($data)==0){return array();}
  82. if($column != strval(intval($column))){return $data;} //probably an attack
  83. if(!in_array($direction,array(SORT_ASC,SORT_DESC))){return $data;} // probably an attack
  84. $compare_function = '';
  85. if ($type == SORT_REGULAR)
  86. {
  87. if (TableSort::is_image_column($data, $column))
  88. {
  89. $type = SORT_IMAGE;
  90. }
  91. elseif (TableSort::is_date_column($data, $column))
  92. {
  93. $type = SORT_DATE;
  94. }
  95. elseif (TableSort::is_numeric_column($data, $column))
  96. {
  97. $type = SORT_NUMERIC;
  98. }
  99. else
  100. {
  101. $type = SORT_STRING;
  102. }
  103. }
  104. switch ($type)
  105. {
  106. case SORT_NUMERIC :
  107. $compare_function = 'strip_tags($el1) > strip_tags($el2)';
  108. break;
  109. case SORT_IMAGE :
  110. $compare_function = 'api_strnatcmp(TableSort::orderingstring(strip_tags($el1,"<img>")),TableSort::orderingstring(strip_tags($el2,"<img>"))) > 0';
  111. break;
  112. case SORT_DATE :
  113. $compare_function = 'TableSort::date_compare($el1, $el2) > 0';
  114. break;
  115. case SORT_STRING :
  116. default:
  117. $compare_function = 'api_strnatcmp(TableSort::orderingstring(strip_tags($el1)),TableSort::orderingstring(strip_tags($el2))) > 0';
  118. break;
  119. }
  120. $function_body = '$el1 = $a['.$column.']; $el2 = $b['.$column.']; return '.($direction == SORT_ASC ? ' ' : ' !').'('.$compare_function.');';
  121. // Sort the content
  122. usort($data, create_function('$a,$b', $function_body));
  123. return $data;
  124. }
  125. /**
  126. * Checks if a column of a 2D-array contains only numeric values
  127. * @param array $data The data-array
  128. * @param int $column The index of the column to check
  129. * @return bool true if column contains only dates, false otherwise
  130. * @todo Take locale into account (eg decimal point or comma ?)
  131. * @author bart.mollet@hogent.be
  132. */
  133. function is_numeric_column($data, $column)
  134. {
  135. $is_numeric = true;
  136. foreach ($data as $index => $row)
  137. {
  138. $is_numeric &= is_numeric(strip_tags($row[$column]));
  139. }
  140. return $is_numeric;
  141. }
  142. /**
  143. * Checks if a column of a 2D-array contains only dates (GNU date syntax)
  144. * @param array $data The data-array
  145. * @param int $column The index of the column to check
  146. * @return bool true if column contains only dates, false otherwise
  147. * @author bart.mollet@hogent.be
  148. */
  149. function is_date_column($data, $column)
  150. {
  151. $is_date = true;
  152. foreach ($data as $index => $row)
  153. {
  154. if (strpos($row[$column], '<!--uts=') !== false) {
  155. // A hidden raw date value (an integer Unix time stamp) has been detected. It is needed for precise sorting.
  156. $is_date &= true;
  157. }
  158. elseif (strlen(strip_tags($row[$column])) != 0 )
  159. {
  160. $check_date = strtotime(strip_tags($row[$column]));
  161. // strtotime Returns a timestamp on success, FALSE otherwise.
  162. // Previous to PHP 5.1.0, this function would return -1 on failure.
  163. $is_date &= ($check_date != -1 && $check_date != false);
  164. }
  165. else
  166. {
  167. $is_date &= false;
  168. }
  169. }
  170. return $is_date;
  171. }
  172. /**
  173. * Checks if a column of a 2D-array contains only images (<img src="
  174. * path/file.ext" alt=".."/>)
  175. * @param array $data The data-array
  176. * @param int $column The index of the column to check
  177. * @return bool true if column contains only images, false otherwise
  178. * @author bart.mollet@hogent.be
  179. */
  180. function is_image_column($data, $column)
  181. {
  182. $is_image = true;
  183. foreach ($data as $index => $row)
  184. {
  185. $is_image &= strlen(trim(strip_tags($row[$column],'<img>'))) > 0; // at least one img-tag
  186. $is_image &= strlen(trim(strip_tags($row[$column]))) == 0; // and no text outside attribute-values
  187. }
  188. return $is_image;
  189. }
  190. /**
  191. * Sort 2-dimensional table. It is possile of change the columns that will be show and the way that the columns are sorted.
  192. * @param array $data The data to be sorted.
  193. * @param int $column The column on which the data should be sorted (default = 0)
  194. * @param string $direction The direction to sort (SORT_ASC (default) orSORT_DESC)
  195. * @param array $column_show The columns that we will show in the table i.e: $column_show=array('1','0','1') we will show the 1st and the 3th column.
  196. * @param array $column_order Changes how the columns will be sorted ie. $column_order=array('1','4','3','4') The 2nd column will be sorted like the 4 column
  197. * @param constant $type How should data be sorted (SORT_REGULAR, SORT_NUMERIC,SORT_STRING,SORT_DATE,SORT_IMAGE) *
  198. * @return array The sorted dataset
  199. * @author bart.mollet@hogent.be
  200. */
  201. function sort_table_config($data, $column = 0, $direction = SORT_ASC, $column_show=null, $column_order=null,$type = SORT_REGULAR)
  202. {
  203. if(!is_array($data) or count($data)==0){return array();}
  204. if($column != strval(intval($column))){return $data;} //probably an attack
  205. if(!in_array($direction,array(SORT_ASC,SORT_DESC))){return $data;} // probably an attack
  206. $compare_function = '';
  207. // Change columns sort
  208. // Here we say that the real way of how the columns are going to be order is manage by the $column_order array
  209. if(is_array($column_order))
  210. {
  211. for($i=0;$i<count($column_order);$i++)
  212. {
  213. if ($column== $i+1)
  214. {
  215. $column=$column_order[$i];
  216. }
  217. }
  218. }
  219. if ($type == SORT_REGULAR)
  220. {
  221. if (TableSort::is_image_column($data, $column))
  222. {
  223. $type = SORT_IMAGE;
  224. }
  225. elseif (TableSort::is_date_column($data, $column))
  226. {
  227. $type = SORT_DATE;
  228. }
  229. elseif (TableSort::is_numeric_column($data, $column))
  230. {
  231. $type = SORT_NUMERIC;
  232. }
  233. else
  234. {
  235. $type = SORT_STRING;
  236. }
  237. }
  238. switch ($type)
  239. {
  240. case SORT_NUMERIC :
  241. $compare_function = 'strip_tags($el1) > strip_tags($el2)';
  242. break;
  243. case SORT_IMAGE :
  244. $compare_function = 'strnatcmp(TableSort::orderingstring(strip_tags($el1,"<img>")),TableSort::orderingstring(strip_tags($el2,"<img>"))) > 0';
  245. break;
  246. case SORT_DATE :
  247. $compare_function = 'TableSort::date_compare($el1, $el2) > 0';
  248. break;
  249. case SORT_STRING :
  250. default:
  251. $compare_function = 'strnatcmp(TableSort::orderingstring(strip_tags($el1)),TableSort::orderingstring(strip_tags($el2))) > 0';
  252. break;
  253. }
  254. $function_body = '$el1 = $a['.$column.']; $el2 = $b['.$column.']; return '.($direction == SORT_ASC ? ' ' : ' !').'('.$compare_function.');';
  255. // Sort the content
  256. usort($data, create_function('$a,$b', $function_body));
  257. // We show only the columns data that were set up on the $column_show array
  258. $new_order_data=array();
  259. if(is_array($column_show))
  260. {
  261. for ($j=0;$j<count($data);$j++)
  262. {
  263. $k=0;
  264. for ($i=0;$i<count($column_show);$i++)
  265. {
  266. if ($column_show[$i])
  267. {
  268. $new_order_data[$j][$k]=$data[$j][$i];
  269. }
  270. $k++;
  271. }
  272. }
  273. //replace the multi-arrays
  274. $data=$new_order_data;
  275. }
  276. else
  277. {
  278. return $data;
  279. }
  280. return $data;
  281. }
  282. }