1 <?php
2 /**
3 * @package Joomla.Platform
4 * @subpackage Grid
5
6 * @copyright Copyright (C) 2005 - 2017 Open Source Matters, Inc. All rights reserved.
7 * @license GNU General Public License version 2 or later; see LICENSE
8 */
9
10 defined('JPATH_PLATFORM') or die;
11
12 /**
13 * JGrid class to dynamically generate HTML tables
14 *
15 * @since 11.3
16 */
17 class JGrid
18 {
19 /**
20 * Array of columns
21 * @var array
22 * @since 11.3
23 */
24 protected $columns = array();
25
26 /**
27 * Current active row
28 * @var int
29 * @since 11.3
30 */
31 protected $activeRow = 0;
32
33 /**
34 * Rows of the table (including header and footer rows)
35 * @var array
36 * @since 11.3
37 */
38 protected $rows = array();
39
40 /**
41 * Header and Footer row-IDs
42 * @var array
43 * @since 11.3
44 */
45 protected $specialRows = array('header' => array(), 'footer' => array());
46
47 /**
48 * Associative array of attributes for the table-tag
49 * @var array
50 * @since 11.3
51 */
52 protected $options;
53
54 /**
55 * Constructor for a JGrid object
56 *
57 * @param array $options Associative array of attributes for the table-tag
58 *
59 * @since 11.3
60 */
61 public function __construct($options = array())
62 {
63 $this->setTableOptions($options, true);
64 }
65
66 /**
67 * Magic function to render this object as a table.
68 *
69 * @return string
70 *
71 * @since 11.3
72 */
73 public function __toString()
74 {
75 return $this->toString();
76 }
77
78 /**
79 * Method to set the attributes for a table-tag
80 *
81 * @param array $options Associative array of attributes for the table-tag
82 * @param bool $replace Replace possibly existing attributes
83 *
84 * @return JGrid This object for chaining
85 *
86 * @since 11.3
87 */
88 public function setTableOptions($options = array(), $replace = false)
89 {
90 if ($replace)
91 {
92 $this->options = $options;
93 }
94 else
95 {
96 $this->options = array_merge($this->options, $options);
97 }
98
99 return $this;
100 }
101
102 /**
103 * Get the Attributes of the current table
104 *
105 * @return array Associative array of attributes
106 *
107 * @since 11.3
108 */
109 public function getTableOptions()
110 {
111 return $this->options;
112 }
113
114 /**
115 * Add new column name to process
116 *
117 * @param string $name Internal column name
118 *
119 * @return JGrid This object for chaining
120 *
121 * @since 11.3
122 */
123 public function addColumn($name)
124 {
125 $this->columns[] = $name;
126
127 return $this;
128 }
129
130 /**
131 * Returns the list of internal columns
132 *
133 * @return array List of internal columns
134 *
135 * @since 11.3
136 */
137 public function getColumns()
138 {
139 return $this->columns;
140 }
141
142 /**
143 * Delete column by name
144 *
145 * @param string $name Name of the column to be deleted
146 *
147 * @return JGrid This object for chaining
148 *
149 * @since 11.3
150 */
151 public function deleteColumn($name)
152 {
153 $index = array_search($name, $this->columns);
154
155 if ($index !== false)
156 {
157 unset($this->columns[$index]);
158 $this->columns = array_values($this->columns);
159 }
160
161 return $this;
162 }
163
164 /**
165 * Method to set a whole range of columns at once
166 * This can be used to re-order the columns, too
167 *
168 * @param array $columns List of internal column names
169 *
170 * @return JGrid This object for chaining
171 *
172 * @since 11.3
173 */
174 public function setColumns($columns)
175 {
176 $this->columns = array_values($columns);
177
178 return $this;
179 }
180
181 /**
182 * Adds a row to the table and sets the currently
183 * active row to the new row
184 *
185 * @param array $options Associative array of attributes for the row
186 * @param int $special 1 for a new row in the header, 2 for a new row in the footer
187 *
188 * @return JGrid This object for chaining
189 *
190 * @since 11.3
191 */
192 public function addRow($options = array(), $special = false)
193 {
194 $this->rows[]['_row'] = $options;
195 $this->activeRow = count($this->rows) - 1;
196
197 if ($special)
198 {
199 if ($special === 1)
200 {
201 $this->specialRows['header'][] = $this->activeRow;
202 }
203 else
204 {
205 $this->specialRows['footer'][] = $this->activeRow;
206 }
207 }
208
209 return $this;
210 }
211
212 /**
213 * Method to get the attributes of the currently active row
214 *
215 * @return array Associative array of attributes
216 *
217 * @since 11.3
218 */
219 public function getRowOptions()
220 {
221 return $this->rows[$this->activeRow]['_row'];
222 }
223
224 /**
225 * Method to set the attributes of the currently active row
226 *
227 * @param array $options Associative array of attributes
228 *
229 * @return JGrid This object for chaining
230 *
231 * @since 11.3
232 */
233 public function setRowOptions($options)
234 {
235 $this->rows[$this->activeRow]['_row'] = $options;
236
237 return $this;
238 }
239
240 /**
241 * Get the currently active row ID
242 *
243 * @return int ID of the currently active row
244 *
245 * @since 11.3
246 */
247 public function getActiveRow()
248 {
249 return $this->activeRow;
250 }
251
252 /**
253 * Set the currently active row
254 *
255 * @param int $id ID of the row to be set to current
256 *
257 * @return JGrid This object for chaining
258 *
259 * @since 11.3
260 */
261 public function setActiveRow($id)
262 {
263 $this->activeRow = (int) $id;
264
265 return $this;
266 }
267
268 /**
269 * Set cell content for a specific column for the
270 * currently active row
271 *
272 * @param string $name Name of the column
273 * @param string $content Content for the cell
274 * @param array $option Associative array of attributes for the td-element
275 * @param bool $replace If false, the content is appended to the current content of the cell
276 *
277 * @return JGrid This object for chaining
278 *
279 * @since 11.3
280 */
281 public function setRowCell($name, $content, $option = array(), $replace = true)
282 {
283 if ($replace || !isset($this->rows[$this->activeRow][$name]))
284 {
285 $cell = new stdClass;
286 $cell->options = $option;
287 $cell->content = $content;
288 $this->rows[$this->activeRow][$name] = $cell;
289 }
290 else
291 {
292 $this->rows[$this->activeRow][$name]->content .= $content;
293 $this->rows[$this->activeRow][$name]->options = $option;
294 }
295
296 return $this;
297 }
298
299 /**
300 * Get all data for a row
301 *
302 * @param int $id ID of the row to return
303 *
304 * @return array Array of columns of a table row
305 *
306 * @since 11.3
307 */
308 public function getRow($id = false)
309 {
310 if ($id === false)
311 {
312 $id = $this->activeRow;
313 }
314
315 if (isset($this->rows[(int) $id]))
316 {
317 return $this->rows[(int) $id];
318 }
319 else
320 {
321 return false;
322 }
323 }
324
325 /**
326 * Get the IDs of all rows in the table
327 *
328 * @param int $special false for the standard rows, 1 for the header rows, 2 for the footer rows
329 *
330 * @return array Array of IDs
331 *
332 * @since 11.3
333 */
334 public function getRows($special = false)
335 {
336 if ($special)
337 {
338 if ($special === 1)
339 {
340 return $this->specialRows['header'];
341 }
342 else
343 {
344 return $this->specialRows['footer'];
345 }
346 }
347
348 return array_diff(array_keys($this->rows), array_merge($this->specialRows['header'], $this->specialRows['footer']));
349 }
350
351 /**
352 * Delete a row from the object
353 *
354 * @param int $id ID of the row to be deleted
355 *
356 * @return JGrid This object for chaining
357 *
358 * @since 11.3
359 */
360 public function deleteRow($id)
361 {
362 unset($this->rows[$id]);
363
364 if (in_array($id, $this->specialRows['header']))
365 {
366 unset($this->specialRows['header'][array_search($id, $this->specialRows['header'])]);
367 }
368
369 if (in_array($id, $this->specialRows['footer']))
370 {
371 unset($this->specialRows['footer'][array_search($id, $this->specialRows['footer'])]);
372 }
373
374 if ($this->activeRow == $id)
375 {
376 end($this->rows);
377 $this->activeRow = key($this->rows);
378 }
379
380 return $this;
381 }
382
383 /**
384 * Render the HTML table
385 *
386 * @return string The rendered HTML table
387 *
388 * @since 11.3
389 */
390 public function toString()
391 {
392 $output = array();
393 $output[] = '<table' . $this->renderAttributes($this->getTableOptions()) . '>';
394
395 if (count($this->specialRows['header']))
396 {
397 $output[] = $this->renderArea($this->specialRows['header'], 'thead', 'th');
398 }
399
400 if (count($this->specialRows['footer']))
401 {
402 $output[] = $this->renderArea($this->specialRows['footer'], 'tfoot');
403 }
404
405 $ids = array_diff(array_keys($this->rows), array_merge($this->specialRows['header'], $this->specialRows['footer']));
406
407 if (count($ids))
408 {
409 $output[] = $this->renderArea($ids);
410 }
411
412 $output[] = '</table>';
413
414 return implode('', $output);
415 }
416
417 /**
418 * Render an area of the table
419 *
420 * @param array $ids IDs of the rows to render
421 * @param string $area Name of the area to render. Valid: tbody, tfoot, thead
422 * @param string $cell Name of the cell to render. Valid: td, th
423 *
424 * @return string The rendered table area
425 *
426 * @since 11.3
427 */
428 protected function renderArea($ids, $area = 'tbody', $cell = 'td')
429 {
430 $output = array();
431 $output[] = '<' . $area . ">\n";
432
433 foreach ($ids as $id)
434 {
435 $output[] = "\t<tr" . $this->renderAttributes($this->rows[$id]['_row']) . ">\n";
436
437 foreach ($this->getColumns() as $name)
438 {
439 if (isset($this->rows[$id][$name]))
440 {
441 $column = $this->rows[$id][$name];
442 $output[] = "\t\t<" . $cell . $this->renderAttributes($column->options) . '>' . $column->content . '</' . $cell . ">\n";
443 }
444 }
445
446 $output[] = "\t</tr>\n";
447 }
448
449 $output[] = '</' . $area . '>';
450
451 return implode('', $output);
452 }
453
454 /**
455 * Renders an HTML attribute from an associative array
456 *
457 * @param array $attributes Associative array of attributes
458 *
459 * @return string The HTML attribute string
460 *
461 * @since 11.3
462 */
463 protected function renderAttributes($attributes)
464 {
465 if (count((array) $attributes) == 0)
466 {
467 return '';
468 }
469
470 $return = array();
471
472 foreach ($attributes as $key => $option)
473 {
474 $return[] = $key . '="' . $option . '"';
475 }
476
477 return ' ' . implode(' ', $return);
478 }
479 }
480