You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

538 lines
12 KiB

7 years ago
  1. <?php
  2. /**
  3. * CodeIgniter
  4. *
  5. * An open source application development framework for PHP
  6. *
  7. * This content is released under the MIT License (MIT)
  8. *
  9. * Copyright (c) 2014 - 2017, British Columbia Institute of Technology
  10. *
  11. * Permission is hereby granted, free of charge, to any person obtaining a copy
  12. * of this software and associated documentation files (the "Software"), to deal
  13. * in the Software without restriction, including without limitation the rights
  14. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  15. * copies of the Software, and to permit persons to whom the Software is
  16. * furnished to do so, subject to the following conditions:
  17. *
  18. * The above copyright notice and this permission notice shall be included in
  19. * all copies or substantial portions of the Software.
  20. *
  21. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  22. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  23. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  24. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  25. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  26. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  27. * THE SOFTWARE.
  28. *
  29. * @package CodeIgniter
  30. * @author EllisLab Dev Team
  31. * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
  32. * @copyright Copyright (c) 2014 - 2017, British Columbia Institute of Technology (http://bcit.ca/)
  33. * @license http://opensource.org/licenses/MIT MIT License
  34. * @link https://codeigniter.com
  35. * @since Version 1.3.1
  36. * @filesource
  37. */
  38. defined('BASEPATH') OR exit('No direct script access allowed');
  39. /**
  40. * HTML Table Generating Class
  41. *
  42. * Lets you create tables manually or from database result objects, or arrays.
  43. *
  44. * @package CodeIgniter
  45. * @subpackage Libraries
  46. * @category HTML Tables
  47. * @author EllisLab Dev Team
  48. * @link https://codeigniter.com/user_guide/libraries/table.html
  49. */
  50. class CI_Table {
  51. /**
  52. * Data for table rows
  53. *
  54. * @var array
  55. */
  56. public $rows = array();
  57. /**
  58. * Data for table heading
  59. *
  60. * @var array
  61. */
  62. public $heading = array();
  63. /**
  64. * Whether or not to automatically create the table header
  65. *
  66. * @var bool
  67. */
  68. public $auto_heading = TRUE;
  69. /**
  70. * Table caption
  71. *
  72. * @var string
  73. */
  74. public $caption = NULL;
  75. /**
  76. * Table layout template
  77. *
  78. * @var array
  79. */
  80. public $template = NULL;
  81. /**
  82. * Newline setting
  83. *
  84. * @var string
  85. */
  86. public $newline = "\n";
  87. /**
  88. * Contents of empty cells
  89. *
  90. * @var string
  91. */
  92. public $empty_cells = '';
  93. /**
  94. * Callback for custom table layout
  95. *
  96. * @var function
  97. */
  98. public $function = NULL;
  99. /**
  100. * Set the template from the table config file if it exists
  101. *
  102. * @param array $config (default: array())
  103. * @return void
  104. */
  105. public function __construct($config = array())
  106. {
  107. // initialize config
  108. foreach ($config as $key => $val)
  109. {
  110. $this->template[$key] = $val;
  111. }
  112. log_message('info', 'Table Class Initialized');
  113. }
  114. // --------------------------------------------------------------------
  115. /**
  116. * Set the template
  117. *
  118. * @param array $template
  119. * @return bool
  120. */
  121. public function set_template($template)
  122. {
  123. if ( ! is_array($template))
  124. {
  125. return FALSE;
  126. }
  127. $this->template = $template;
  128. return TRUE;
  129. }
  130. // --------------------------------------------------------------------
  131. /**
  132. * Set the table heading
  133. *
  134. * Can be passed as an array or discreet params
  135. *
  136. * @param mixed
  137. * @return CI_Table
  138. */
  139. public function set_heading($args = array())
  140. {
  141. $this->heading = $this->_prep_args(func_get_args());
  142. return $this;
  143. }
  144. // --------------------------------------------------------------------
  145. /**
  146. * Set columns. Takes a one-dimensional array as input and creates
  147. * a multi-dimensional array with a depth equal to the number of
  148. * columns. This allows a single array with many elements to be
  149. * displayed in a table that has a fixed column count.
  150. *
  151. * @param array $array
  152. * @param int $col_limit
  153. * @return array
  154. */
  155. public function make_columns($array = array(), $col_limit = 0)
  156. {
  157. if ( ! is_array($array) OR count($array) === 0 OR ! is_int($col_limit))
  158. {
  159. return FALSE;
  160. }
  161. // Turn off the auto-heading feature since it's doubtful we
  162. // will want headings from a one-dimensional array
  163. $this->auto_heading = FALSE;
  164. if ($col_limit === 0)
  165. {
  166. return $array;
  167. }
  168. $new = array();
  169. do
  170. {
  171. $temp = array_splice($array, 0, $col_limit);
  172. if (count($temp) < $col_limit)
  173. {
  174. for ($i = count($temp); $i < $col_limit; $i++)
  175. {
  176. $temp[] = '&nbsp;';
  177. }
  178. }
  179. $new[] = $temp;
  180. }
  181. while (count($array) > 0);
  182. return $new;
  183. }
  184. // --------------------------------------------------------------------
  185. /**
  186. * Set "empty" cells
  187. *
  188. * Can be passed as an array or discreet params
  189. *
  190. * @param mixed $value
  191. * @return CI_Table
  192. */
  193. public function set_empty($value)
  194. {
  195. $this->empty_cells = $value;
  196. return $this;
  197. }
  198. // --------------------------------------------------------------------
  199. /**
  200. * Add a table row
  201. *
  202. * Can be passed as an array or discreet params
  203. *
  204. * @param mixed
  205. * @return CI_Table
  206. */
  207. public function add_row($args = array())
  208. {
  209. $this->rows[] = $this->_prep_args(func_get_args());
  210. return $this;
  211. }
  212. // --------------------------------------------------------------------
  213. /**
  214. * Prep Args
  215. *
  216. * Ensures a standard associative array format for all cell data
  217. *
  218. * @param array
  219. * @return array
  220. */
  221. protected function _prep_args($args)
  222. {
  223. // If there is no $args[0], skip this and treat as an associative array
  224. // This can happen if there is only a single key, for example this is passed to table->generate
  225. // array(array('foo'=>'bar'))
  226. if (isset($args[0]) && count($args) === 1 && is_array($args[0]) && ! isset($args[0]['data']))
  227. {
  228. $args = $args[0];
  229. }
  230. foreach ($args as $key => $val)
  231. {
  232. is_array($val) OR $args[$key] = array('data' => $val);
  233. }
  234. return $args;
  235. }
  236. // --------------------------------------------------------------------
  237. /**
  238. * Add a table caption
  239. *
  240. * @param string $caption
  241. * @return CI_Table
  242. */
  243. public function set_caption($caption)
  244. {
  245. $this->caption = $caption;
  246. return $this;
  247. }
  248. // --------------------------------------------------------------------
  249. /**
  250. * Generate the table
  251. *
  252. * @param mixed $table_data
  253. * @return string
  254. */
  255. public function generate($table_data = NULL)
  256. {
  257. // The table data can optionally be passed to this function
  258. // either as a database result object or an array
  259. if ( ! empty($table_data))
  260. {
  261. if ($table_data instanceof CI_DB_result)
  262. {
  263. $this->_set_from_db_result($table_data);
  264. }
  265. elseif (is_array($table_data))
  266. {
  267. $this->_set_from_array($table_data);
  268. }
  269. }
  270. // Is there anything to display? No? Smite them!
  271. if (empty($this->heading) && empty($this->rows))
  272. {
  273. return 'Undefined table data';
  274. }
  275. // Compile and validate the template date
  276. $this->_compile_template();
  277. // Validate a possibly existing custom cell manipulation function
  278. if (isset($this->function) && ! is_callable($this->function))
  279. {
  280. $this->function = NULL;
  281. }
  282. // Build the table!
  283. $out = $this->template['table_open'].$this->newline;
  284. // Add any caption here
  285. if ($this->caption)
  286. {
  287. $out .= '<caption>'.$this->caption.'</caption>'.$this->newline;
  288. }
  289. // Is there a table heading to display?
  290. if ( ! empty($this->heading))
  291. {
  292. $out .= $this->template['thead_open'].$this->newline.$this->template['heading_row_start'].$this->newline;
  293. foreach ($this->heading as $heading)
  294. {
  295. $temp = $this->template['heading_cell_start'];
  296. foreach ($heading as $key => $val)
  297. {
  298. if ($key !== 'data')
  299. {
  300. $temp = str_replace('<th', '<th '.$key.'="'.$val.'"', $temp);
  301. }
  302. }
  303. $out .= $temp.(isset($heading['data']) ? $heading['data'] : '').$this->template['heading_cell_end'];
  304. }
  305. $out .= $this->template['heading_row_end'].$this->newline.$this->template['thead_close'].$this->newline;
  306. }
  307. // Build the table rows
  308. if ( ! empty($this->rows))
  309. {
  310. $out .= $this->template['tbody_open'].$this->newline;
  311. $i = 1;
  312. foreach ($this->rows as $row)
  313. {
  314. if ( ! is_array($row))
  315. {
  316. break;
  317. }
  318. // We use modulus to alternate the row colors
  319. $name = fmod($i++, 2) ? '' : 'alt_';
  320. $out .= $this->template['row_'.$name.'start'].$this->newline;
  321. foreach ($row as $cell)
  322. {
  323. $temp = $this->template['cell_'.$name.'start'];
  324. foreach ($cell as $key => $val)
  325. {
  326. if ($key !== 'data')
  327. {
  328. $temp = str_replace('<td', '<td '.$key.'="'.$val.'"', $temp);
  329. }
  330. }
  331. $cell = isset($cell['data']) ? $cell['data'] : '';
  332. $out .= $temp;
  333. if ($cell === '' OR $cell === NULL)
  334. {
  335. $out .= $this->empty_cells;
  336. }
  337. elseif (isset($this->function))
  338. {
  339. $out .= call_user_func($this->function, $cell);
  340. }
  341. else
  342. {
  343. $out .= $cell;
  344. }
  345. $out .= $this->template['cell_'.$name.'end'];
  346. }
  347. $out .= $this->template['row_'.$name.'end'].$this->newline;
  348. }
  349. $out .= $this->template['tbody_close'].$this->newline;
  350. }
  351. $out .= $this->template['table_close'];
  352. // Clear table class properties before generating the table
  353. $this->clear();
  354. return $out;
  355. }
  356. // --------------------------------------------------------------------
  357. /**
  358. * Clears the table arrays. Useful if multiple tables are being generated
  359. *
  360. * @return CI_Table
  361. */
  362. public function clear()
  363. {
  364. $this->rows = array();
  365. $this->heading = array();
  366. $this->auto_heading = TRUE;
  367. return $this;
  368. }
  369. // --------------------------------------------------------------------
  370. /**
  371. * Set table data from a database result object
  372. *
  373. * @param CI_DB_result $object Database result object
  374. * @return void
  375. */
  376. protected function _set_from_db_result($object)
  377. {
  378. // First generate the headings from the table column names
  379. if ($this->auto_heading === TRUE && empty($this->heading))
  380. {
  381. $this->heading = $this->_prep_args($object->list_fields());
  382. }
  383. foreach ($object->result_array() as $row)
  384. {
  385. $this->rows[] = $this->_prep_args($row);
  386. }
  387. }
  388. // --------------------------------------------------------------------
  389. /**
  390. * Set table data from an array
  391. *
  392. * @param array $data
  393. * @return void
  394. */
  395. protected function _set_from_array($data)
  396. {
  397. if ($this->auto_heading === TRUE && empty($this->heading))
  398. {
  399. $this->heading = $this->_prep_args(array_shift($data));
  400. }
  401. foreach ($data as &$row)
  402. {
  403. $this->rows[] = $this->_prep_args($row);
  404. }
  405. }
  406. // --------------------------------------------------------------------
  407. /**
  408. * Compile Template
  409. *
  410. * @return void
  411. */
  412. protected function _compile_template()
  413. {
  414. if ($this->template === NULL)
  415. {
  416. $this->template = $this->_default_template();
  417. return;
  418. }
  419. $this->temp = $this->_default_template();
  420. foreach (array('table_open', 'thead_open', 'thead_close', 'heading_row_start', 'heading_row_end', 'heading_cell_start', 'heading_cell_end', 'tbody_open', 'tbody_close', 'row_start', 'row_end', 'cell_start', 'cell_end', 'row_alt_start', 'row_alt_end', 'cell_alt_start', 'cell_alt_end', 'table_close') as $val)
  421. {
  422. if ( ! isset($this->template[$val]))
  423. {
  424. $this->template[$val] = $this->temp[$val];
  425. }
  426. }
  427. }
  428. // --------------------------------------------------------------------
  429. /**
  430. * Default Template
  431. *
  432. * @return array
  433. */
  434. protected function _default_template()
  435. {
  436. return array(
  437. 'table_open' => '<table border="0" cellpadding="4" cellspacing="0">',
  438. 'thead_open' => '<thead>',
  439. 'thead_close' => '</thead>',
  440. 'heading_row_start' => '<tr>',
  441. 'heading_row_end' => '</tr>',
  442. 'heading_cell_start' => '<th>',
  443. 'heading_cell_end' => '</th>',
  444. 'tbody_open' => '<tbody>',
  445. 'tbody_close' => '</tbody>',
  446. 'row_start' => '<tr>',
  447. 'row_end' => '</tr>',
  448. 'cell_start' => '<td>',
  449. 'cell_end' => '</td>',
  450. 'row_alt_start' => '<tr>',
  451. 'row_alt_end' => '</tr>',
  452. 'cell_alt_start' => '<td>',
  453. 'cell_alt_end' => '</td>',
  454. 'table_close' => '</table>'
  455. );
  456. }
  457. }