1 |
efrain |
1 |
<?php
|
|
|
2 |
|
|
|
3 |
// This file is part of Moodle - http://moodle.org/
|
|
|
4 |
//
|
|
|
5 |
// Moodle is free software: you can redistribute it and/or modify
|
|
|
6 |
// it under the terms of the GNU General Public License as published by
|
|
|
7 |
// the Free Software Foundation, either version 3 of the License, or
|
|
|
8 |
// (at your option) any later version.
|
|
|
9 |
//
|
|
|
10 |
// Moodle is distributed in the hope that it will be useful,
|
|
|
11 |
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
12 |
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
13 |
// GNU General Public License for more details.
|
|
|
14 |
//
|
|
|
15 |
// You should have received a copy of the GNU General Public License
|
|
|
16 |
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
|
|
17 |
|
|
|
18 |
/**
|
|
|
19 |
* @package core
|
|
|
20 |
* @subpackage lib
|
|
|
21 |
* @copyright 1999 onwards Martin Dougiamas {@link http://moodle.com}
|
|
|
22 |
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
|
|
23 |
*/
|
|
|
24 |
|
|
|
25 |
|
|
|
26 |
defined('MOODLE_INTERNAL') || die();
|
|
|
27 |
|
|
|
28 |
/**#@+
|
|
|
29 |
* These constants relate to the table's handling of URL parameters.
|
|
|
30 |
*/
|
|
|
31 |
define('TABLE_VAR_SORT', 1);
|
|
|
32 |
define('TABLE_VAR_HIDE', 2);
|
|
|
33 |
define('TABLE_VAR_SHOW', 3);
|
|
|
34 |
define('TABLE_VAR_IFIRST', 4);
|
|
|
35 |
define('TABLE_VAR_ILAST', 5);
|
|
|
36 |
define('TABLE_VAR_PAGE', 6);
|
|
|
37 |
define('TABLE_VAR_RESET', 7);
|
|
|
38 |
define('TABLE_VAR_DIR', 8);
|
|
|
39 |
/**#@-*/
|
|
|
40 |
|
|
|
41 |
/**#@+
|
|
|
42 |
* Constants that indicate whether the paging bar for the table
|
|
|
43 |
* appears above or below the table.
|
|
|
44 |
*/
|
|
|
45 |
define('TABLE_P_TOP', 1);
|
|
|
46 |
define('TABLE_P_BOTTOM', 2);
|
|
|
47 |
/**#@-*/
|
|
|
48 |
|
|
|
49 |
/**
|
|
|
50 |
* Constant that defines the 'Show all' page size.
|
|
|
51 |
*/
|
|
|
52 |
define('TABLE_SHOW_ALL_PAGE_SIZE', 5000);
|
|
|
53 |
|
11 |
efrain |
54 |
use core\dataformat;
|
1 |
efrain |
55 |
use core_table\local\filter\filterset;
|
|
|
56 |
|
|
|
57 |
/**
|
|
|
58 |
* @package moodlecore
|
|
|
59 |
* @copyright 1999 onwards Martin Dougiamas {@link http://moodle.com}
|
|
|
60 |
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
|
|
61 |
*/
|
|
|
62 |
class flexible_table {
|
|
|
63 |
|
|
|
64 |
var $uniqueid = NULL;
|
|
|
65 |
var $attributes = array();
|
|
|
66 |
var $headers = array();
|
|
|
67 |
|
|
|
68 |
/**
|
|
|
69 |
* @var string A column which should be considered as a header column.
|
|
|
70 |
*/
|
|
|
71 |
protected $headercolumn = null;
|
|
|
72 |
|
|
|
73 |
/**
|
|
|
74 |
* @var string For create header with help icon.
|
|
|
75 |
*/
|
|
|
76 |
private $helpforheaders = array();
|
|
|
77 |
var $columns = array();
|
|
|
78 |
var $column_style = array();
|
|
|
79 |
var $column_class = array();
|
|
|
80 |
var $column_suppress = array();
|
|
|
81 |
var $column_nosort = array('userpic');
|
|
|
82 |
private $column_textsort = array();
|
|
|
83 |
|
|
|
84 |
/**
|
|
|
85 |
* @var array The sticky attribute of each table column.
|
|
|
86 |
*/
|
|
|
87 |
protected $columnsticky = [];
|
|
|
88 |
|
|
|
89 |
/** @var boolean Stores if setup has already been called on this flixible table. */
|
|
|
90 |
var $setup = false;
|
|
|
91 |
var $baseurl = NULL;
|
|
|
92 |
var $request = array();
|
|
|
93 |
|
|
|
94 |
/** @var string[] Columns that are expected to contain a users fullname. */
|
|
|
95 |
protected $userfullnamecolumns = ['fullname'];
|
|
|
96 |
|
|
|
97 |
/** @var array[] Attributes for each column */
|
|
|
98 |
private $columnsattributes = [];
|
|
|
99 |
|
|
|
100 |
/**
|
|
|
101 |
* @var bool Whether or not to store table properties in the user_preferences table.
|
|
|
102 |
*/
|
|
|
103 |
private $persistent = false;
|
|
|
104 |
var $is_collapsible = false;
|
|
|
105 |
var $is_sortable = false;
|
|
|
106 |
|
|
|
107 |
/**
|
|
|
108 |
* @var array The fields to sort.
|
|
|
109 |
*/
|
|
|
110 |
protected $sortdata;
|
|
|
111 |
|
|
|
112 |
/** @var string The manually set first name initial preference */
|
|
|
113 |
protected $ifirst;
|
|
|
114 |
|
|
|
115 |
/** @var string The manually set last name initial preference */
|
|
|
116 |
protected $ilast;
|
|
|
117 |
|
|
|
118 |
var $use_pages = false;
|
|
|
119 |
var $use_initials = false;
|
|
|
120 |
|
|
|
121 |
var $maxsortkeys = 2;
|
|
|
122 |
var $pagesize = 30;
|
|
|
123 |
var $currpage = 0;
|
|
|
124 |
var $totalrows = 0;
|
|
|
125 |
var $currentrow = 0;
|
|
|
126 |
var $sort_default_column = NULL;
|
|
|
127 |
var $sort_default_order = SORT_ASC;
|
|
|
128 |
|
|
|
129 |
/** @var integer The defeult per page size for the table. */
|
|
|
130 |
private $defaultperpage = 30;
|
|
|
131 |
|
|
|
132 |
/**
|
|
|
133 |
* Array of positions in which to display download controls.
|
|
|
134 |
*/
|
|
|
135 |
var $showdownloadbuttonsat= array(TABLE_P_TOP);
|
|
|
136 |
|
|
|
137 |
/**
|
|
|
138 |
* @var string Key of field returned by db query that is the id field of the
|
|
|
139 |
* user table or equivalent.
|
|
|
140 |
*/
|
|
|
141 |
public $useridfield = 'id';
|
|
|
142 |
|
|
|
143 |
/**
|
|
|
144 |
* @var string which download plugin to use. Default '' means none - print
|
|
|
145 |
* html table with paging. Property set by is_downloading which typically
|
|
|
146 |
* passes in cleaned data from $
|
|
|
147 |
*/
|
|
|
148 |
var $download = '';
|
|
|
149 |
|
|
|
150 |
/**
|
|
|
151 |
* @var bool whether data is downloadable from table. Determines whether
|
|
|
152 |
* to display download buttons. Set by method downloadable().
|
|
|
153 |
*/
|
|
|
154 |
var $downloadable = false;
|
|
|
155 |
|
|
|
156 |
/**
|
|
|
157 |
* @var bool Has start output been called yet?
|
|
|
158 |
*/
|
|
|
159 |
var $started_output = false;
|
|
|
160 |
|
|
|
161 |
/** @var table_dataformat_export_format */
|
|
|
162 |
var $exportclass = null;
|
|
|
163 |
|
|
|
164 |
/**
|
|
|
165 |
* @var array For storing user-customised table properties in the user_preferences db table.
|
|
|
166 |
*/
|
|
|
167 |
private $prefs = array();
|
|
|
168 |
|
|
|
169 |
/** @var string $sheettitle */
|
|
|
170 |
protected $sheettitle;
|
|
|
171 |
|
|
|
172 |
/** @var string $filename */
|
|
|
173 |
protected $filename;
|
|
|
174 |
|
|
|
175 |
/** @var array $hiddencolumns List of hidden columns. */
|
|
|
176 |
protected $hiddencolumns;
|
|
|
177 |
|
|
|
178 |
/** @var bool $resetting Whether the table preferences is resetting. */
|
|
|
179 |
protected $resetting;
|
|
|
180 |
|
|
|
181 |
/**
|
|
|
182 |
* @var string $caption The caption of table
|
|
|
183 |
*/
|
|
|
184 |
public $caption;
|
|
|
185 |
|
|
|
186 |
/**
|
|
|
187 |
* @var array $captionattributes The caption attributes of table
|
|
|
188 |
*/
|
|
|
189 |
public $captionattributes;
|
|
|
190 |
|
|
|
191 |
/**
|
|
|
192 |
* @var filterset The currently applied filerset
|
|
|
193 |
* This is required for dynamic tables, but can be used by other tables too if desired.
|
|
|
194 |
*/
|
|
|
195 |
protected $filterset = null;
|
|
|
196 |
|
|
|
197 |
/**
|
|
|
198 |
* Constructor
|
|
|
199 |
* @param string $uniqueid all tables have to have a unique id, this is used
|
|
|
200 |
* as a key when storing table properties like sort order in the session.
|
|
|
201 |
*/
|
|
|
202 |
function __construct($uniqueid) {
|
|
|
203 |
$this->uniqueid = $uniqueid;
|
|
|
204 |
$this->request = array(
|
|
|
205 |
TABLE_VAR_SORT => 'tsort',
|
|
|
206 |
TABLE_VAR_HIDE => 'thide',
|
|
|
207 |
TABLE_VAR_SHOW => 'tshow',
|
|
|
208 |
TABLE_VAR_IFIRST => 'tifirst',
|
|
|
209 |
TABLE_VAR_ILAST => 'tilast',
|
|
|
210 |
TABLE_VAR_PAGE => 'page',
|
|
|
211 |
TABLE_VAR_RESET => 'treset',
|
|
|
212 |
TABLE_VAR_DIR => 'tdir',
|
|
|
213 |
);
|
|
|
214 |
}
|
|
|
215 |
|
|
|
216 |
/**
|
|
|
217 |
* Call this to pass the download type. Use :
|
|
|
218 |
* $download = optional_param('download', '', PARAM_ALPHA);
|
|
|
219 |
* To get the download type. We assume that if you call this function with
|
|
|
220 |
* params that this table's data is downloadable, so we call is_downloadable
|
|
|
221 |
* for you (even if the param is '', which means no download this time.
|
|
|
222 |
* Also you can call this method with no params to get the current set
|
|
|
223 |
* download type.
|
|
|
224 |
* @param string|null $download type of dataformat for export.
|
|
|
225 |
* @param string $filename filename for downloads without file extension.
|
|
|
226 |
* @param string $sheettitle title for downloaded data.
|
|
|
227 |
* @return string download dataformat type.
|
|
|
228 |
*/
|
|
|
229 |
function is_downloading($download = null, $filename='', $sheettitle='') {
|
|
|
230 |
if ($download!==null) {
|
|
|
231 |
$this->sheettitle = $sheettitle;
|
|
|
232 |
$this->is_downloadable(true);
|
|
|
233 |
$this->download = $download;
|
|
|
234 |
$this->filename = clean_filename($filename);
|
|
|
235 |
$this->export_class_instance();
|
|
|
236 |
}
|
|
|
237 |
return $this->download;
|
|
|
238 |
}
|
|
|
239 |
|
|
|
240 |
/**
|
|
|
241 |
* Get, and optionally set, the export class.
|
|
|
242 |
* @param table_dataformat_export_format $exportclass (optional) if passed, set the table to use this export class.
|
|
|
243 |
* @return table_dataformat_export_format the export class in use (after any set).
|
|
|
244 |
*/
|
|
|
245 |
function export_class_instance($exportclass = null) {
|
|
|
246 |
if (!is_null($exportclass)) {
|
|
|
247 |
$this->started_output = true;
|
|
|
248 |
$this->exportclass = $exportclass;
|
|
|
249 |
$this->exportclass->table = $this;
|
|
|
250 |
} else if (is_null($this->exportclass) && !empty($this->download)) {
|
|
|
251 |
$this->exportclass = new table_dataformat_export_format($this, $this->download);
|
|
|
252 |
if (!$this->exportclass->document_started()) {
|
|
|
253 |
$this->exportclass->start_document($this->filename, $this->sheettitle);
|
|
|
254 |
}
|
|
|
255 |
}
|
|
|
256 |
return $this->exportclass;
|
|
|
257 |
}
|
|
|
258 |
|
|
|
259 |
/**
|
|
|
260 |
* Probably don't need to call this directly. Calling is_downloading with a
|
|
|
261 |
* param automatically sets table as downloadable.
|
|
|
262 |
*
|
|
|
263 |
* @param bool $downloadable optional param to set whether data from
|
|
|
264 |
* table is downloadable. If ommitted this function can be used to get
|
|
|
265 |
* current state of table.
|
|
|
266 |
* @return bool whether table data is set to be downloadable.
|
|
|
267 |
*/
|
|
|
268 |
function is_downloadable($downloadable = null) {
|
|
|
269 |
if ($downloadable !== null) {
|
|
|
270 |
$this->downloadable = $downloadable;
|
|
|
271 |
}
|
|
|
272 |
return $this->downloadable;
|
|
|
273 |
}
|
|
|
274 |
|
|
|
275 |
/**
|
|
|
276 |
* Call with boolean true to store table layout changes in the user_preferences table.
|
|
|
277 |
* Note: user_preferences.value has a maximum length of 1333 characters.
|
|
|
278 |
* Call with no parameter to get current state of table persistence.
|
|
|
279 |
*
|
|
|
280 |
* @param bool $persistent Optional parameter to set table layout persistence.
|
|
|
281 |
* @return bool Whether or not the table layout preferences will persist.
|
|
|
282 |
*/
|
|
|
283 |
public function is_persistent($persistent = null) {
|
|
|
284 |
if ($persistent == true) {
|
|
|
285 |
$this->persistent = true;
|
|
|
286 |
}
|
|
|
287 |
return $this->persistent;
|
|
|
288 |
}
|
|
|
289 |
|
|
|
290 |
/**
|
|
|
291 |
* Where to show download buttons.
|
|
|
292 |
* @param array $showat array of postions in which to show download buttons.
|
|
|
293 |
* Containing TABLE_P_TOP and/or TABLE_P_BOTTOM
|
|
|
294 |
*/
|
|
|
295 |
function show_download_buttons_at($showat) {
|
|
|
296 |
$this->showdownloadbuttonsat = $showat;
|
|
|
297 |
}
|
|
|
298 |
|
|
|
299 |
/**
|
|
|
300 |
* Sets the is_sortable variable to the given boolean, sort_default_column to
|
|
|
301 |
* the given string, and the sort_default_order to the given integer.
|
|
|
302 |
* @param bool $bool
|
|
|
303 |
* @param string $defaultcolumn
|
|
|
304 |
* @param int $defaultorder
|
|
|
305 |
* @return void
|
|
|
306 |
*/
|
|
|
307 |
function sortable($bool, $defaultcolumn = NULL, $defaultorder = SORT_ASC) {
|
|
|
308 |
$this->is_sortable = $bool;
|
|
|
309 |
$this->sort_default_column = $defaultcolumn;
|
|
|
310 |
$this->sort_default_order = $defaultorder;
|
|
|
311 |
}
|
|
|
312 |
|
|
|
313 |
/**
|
|
|
314 |
* Use text sorting functions for this column (required for text columns with Oracle).
|
|
|
315 |
* Be warned that you cannot use this with column aliases. You can only do this
|
|
|
316 |
* with real columns. See MDL-40481 for an example.
|
|
|
317 |
* @param string column name
|
|
|
318 |
*/
|
|
|
319 |
function text_sorting($column) {
|
|
|
320 |
$this->column_textsort[] = $column;
|
|
|
321 |
}
|
|
|
322 |
|
|
|
323 |
/**
|
|
|
324 |
* Do not sort using this column
|
|
|
325 |
* @param string column name
|
|
|
326 |
*/
|
|
|
327 |
function no_sorting($column) {
|
|
|
328 |
$this->column_nosort[] = $column;
|
|
|
329 |
}
|
|
|
330 |
|
|
|
331 |
/**
|
|
|
332 |
* Is the column sortable?
|
|
|
333 |
* @param string column name, null means table
|
|
|
334 |
* @return bool
|
|
|
335 |
*/
|
|
|
336 |
function is_sortable($column = null) {
|
|
|
337 |
if (empty($column)) {
|
|
|
338 |
return $this->is_sortable;
|
|
|
339 |
}
|
|
|
340 |
if (!$this->is_sortable) {
|
|
|
341 |
return false;
|
|
|
342 |
}
|
|
|
343 |
return !in_array($column, $this->column_nosort);
|
|
|
344 |
}
|
|
|
345 |
|
|
|
346 |
/**
|
|
|
347 |
* Sets the is_collapsible variable to the given boolean.
|
|
|
348 |
* @param bool $bool
|
|
|
349 |
* @return void
|
|
|
350 |
*/
|
|
|
351 |
function collapsible($bool) {
|
|
|
352 |
$this->is_collapsible = $bool;
|
|
|
353 |
}
|
|
|
354 |
|
|
|
355 |
/**
|
|
|
356 |
* Sets the use_pages variable to the given boolean.
|
|
|
357 |
* @param bool $bool
|
|
|
358 |
* @return void
|
|
|
359 |
*/
|
|
|
360 |
function pageable($bool) {
|
|
|
361 |
$this->use_pages = $bool;
|
|
|
362 |
}
|
|
|
363 |
|
|
|
364 |
/**
|
|
|
365 |
* Sets the use_initials variable to the given boolean.
|
|
|
366 |
* @param bool $bool
|
|
|
367 |
* @return void
|
|
|
368 |
*/
|
|
|
369 |
function initialbars($bool) {
|
|
|
370 |
$this->use_initials = $bool;
|
|
|
371 |
}
|
|
|
372 |
|
|
|
373 |
/**
|
|
|
374 |
* Sets the pagesize variable to the given integer, the totalrows variable
|
|
|
375 |
* to the given integer, and the use_pages variable to true.
|
|
|
376 |
* @param int $perpage
|
|
|
377 |
* @param int $total
|
|
|
378 |
* @return void
|
|
|
379 |
*/
|
|
|
380 |
function pagesize($perpage, $total) {
|
|
|
381 |
$this->pagesize = $perpage;
|
|
|
382 |
$this->totalrows = $total;
|
|
|
383 |
$this->use_pages = true;
|
|
|
384 |
}
|
|
|
385 |
|
|
|
386 |
/**
|
|
|
387 |
* Assigns each given variable in the array to the corresponding index
|
|
|
388 |
* in the request class variable.
|
|
|
389 |
* @param array $variables
|
|
|
390 |
* @return void
|
|
|
391 |
*/
|
|
|
392 |
function set_control_variables($variables) {
|
|
|
393 |
foreach ($variables as $what => $variable) {
|
|
|
394 |
if (isset($this->request[$what])) {
|
|
|
395 |
$this->request[$what] = $variable;
|
|
|
396 |
}
|
|
|
397 |
}
|
|
|
398 |
}
|
|
|
399 |
|
|
|
400 |
/**
|
|
|
401 |
* Gives the given $value to the $attribute index of $this->attributes.
|
|
|
402 |
* @param string $attribute
|
|
|
403 |
* @param mixed $value
|
|
|
404 |
* @return void
|
|
|
405 |
*/
|
|
|
406 |
function set_attribute($attribute, $value) {
|
|
|
407 |
$this->attributes[$attribute] = $value;
|
|
|
408 |
}
|
|
|
409 |
|
|
|
410 |
/**
|
|
|
411 |
* What this method does is set the column so that if the same data appears in
|
|
|
412 |
* consecutive rows, then it is not repeated.
|
|
|
413 |
*
|
|
|
414 |
* For example, in the quiz overview report, the fullname column is set to be suppressed, so
|
|
|
415 |
* that when one student has made multiple attempts, their name is only printed in the row
|
|
|
416 |
* for their first attempt.
|
|
|
417 |
* @param int $column the index of a column.
|
|
|
418 |
*/
|
|
|
419 |
function column_suppress($column) {
|
|
|
420 |
if (isset($this->column_suppress[$column])) {
|
|
|
421 |
$this->column_suppress[$column] = true;
|
|
|
422 |
}
|
|
|
423 |
}
|
|
|
424 |
|
|
|
425 |
/**
|
|
|
426 |
* Sets the given $column index to the given $classname in $this->column_class.
|
|
|
427 |
* @param int $column
|
|
|
428 |
* @param string $classname
|
|
|
429 |
* @return void
|
|
|
430 |
*/
|
|
|
431 |
function column_class($column, $classname) {
|
|
|
432 |
if (isset($this->column_class[$column])) {
|
|
|
433 |
$this->column_class[$column] = ' '.$classname; // This space needed so that classnames don't run together in the HTML
|
|
|
434 |
}
|
|
|
435 |
}
|
|
|
436 |
|
|
|
437 |
/**
|
|
|
438 |
* Sets the given $column index and $property index to the given $value in $this->column_style.
|
|
|
439 |
* @param int $column
|
|
|
440 |
* @param string $property
|
|
|
441 |
* @param mixed $value
|
|
|
442 |
* @return void
|
|
|
443 |
*/
|
|
|
444 |
function column_style($column, $property, $value) {
|
|
|
445 |
if (isset($this->column_style[$column])) {
|
|
|
446 |
$this->column_style[$column][$property] = $value;
|
|
|
447 |
}
|
|
|
448 |
}
|
|
|
449 |
|
|
|
450 |
/**
|
|
|
451 |
* Sets a sticky attribute to a column.
|
|
|
452 |
* @param string $column Column name
|
|
|
453 |
* @param bool $sticky
|
|
|
454 |
*/
|
|
|
455 |
public function column_sticky(string $column, bool $sticky = true): void {
|
|
|
456 |
if (isset($this->columnsticky[$column])) {
|
|
|
457 |
$this->columnsticky[$column] = $sticky == true ? ' sticky-column' : '';
|
|
|
458 |
}
|
|
|
459 |
}
|
|
|
460 |
|
|
|
461 |
/**
|
|
|
462 |
* Sets the given $attributes to $this->columnsattributes.
|
|
|
463 |
* Column attributes will be added to every cell in the column.
|
|
|
464 |
*
|
|
|
465 |
* @param array[] $attributes e.g. ['c0_firstname' => ['data-foo' => 'bar']]
|
|
|
466 |
*/
|
|
|
467 |
public function set_columnsattributes(array $attributes): void {
|
|
|
468 |
$this->columnsattributes = $attributes;
|
|
|
469 |
}
|
|
|
470 |
|
|
|
471 |
/**
|
|
|
472 |
* Sets all columns' $propertys to the given $value in $this->column_style.
|
|
|
473 |
* @param int $property
|
|
|
474 |
* @param string $value
|
|
|
475 |
* @return void
|
|
|
476 |
*/
|
|
|
477 |
function column_style_all($property, $value) {
|
|
|
478 |
foreach (array_keys($this->columns) as $column) {
|
|
|
479 |
$this->column_style[$column][$property] = $value;
|
|
|
480 |
}
|
|
|
481 |
}
|
|
|
482 |
|
|
|
483 |
/**
|
|
|
484 |
* Sets $this->baseurl.
|
|
|
485 |
* @param moodle_url|string $url the url with params needed to call up this page
|
|
|
486 |
*/
|
|
|
487 |
function define_baseurl($url) {
|
|
|
488 |
$this->baseurl = new moodle_url($url);
|
|
|
489 |
}
|
|
|
490 |
|
|
|
491 |
/**
|
|
|
492 |
* @param array $columns an array of identifying names for columns. If
|
|
|
493 |
* columns are sorted then column names must correspond to a field in sql.
|
|
|
494 |
*/
|
|
|
495 |
function define_columns($columns) {
|
|
|
496 |
$this->columns = array();
|
|
|
497 |
$this->column_style = array();
|
|
|
498 |
$this->column_class = array();
|
|
|
499 |
$this->columnsticky = [];
|
|
|
500 |
$this->columnsattributes = [];
|
|
|
501 |
$colnum = 0;
|
|
|
502 |
|
|
|
503 |
foreach ($columns as $column) {
|
|
|
504 |
$this->columns[$column] = $colnum++;
|
|
|
505 |
$this->column_style[$column] = array();
|
|
|
506 |
$this->column_class[$column] = '';
|
|
|
507 |
$this->columnsticky[$column] = '';
|
|
|
508 |
$this->columnsattributes[$column] = [];
|
|
|
509 |
$this->column_suppress[$column] = false;
|
|
|
510 |
}
|
|
|
511 |
}
|
|
|
512 |
|
|
|
513 |
/**
|
|
|
514 |
* @param array $headers numerical keyed array of displayed string titles
|
|
|
515 |
* for each column.
|
|
|
516 |
*/
|
|
|
517 |
function define_headers($headers) {
|
|
|
518 |
$this->headers = $headers;
|
|
|
519 |
}
|
|
|
520 |
|
|
|
521 |
/**
|
|
|
522 |
* Mark a specific column as being a table header using the column name defined in define_columns.
|
|
|
523 |
*
|
|
|
524 |
* Note: Only one column can be a header, and it will be rendered using a th tag.
|
|
|
525 |
*
|
|
|
526 |
* @param string $column
|
|
|
527 |
*/
|
|
|
528 |
public function define_header_column(string $column) {
|
|
|
529 |
$this->headercolumn = $column;
|
|
|
530 |
}
|
|
|
531 |
|
|
|
532 |
/**
|
|
|
533 |
* Defines a help icon for the header
|
|
|
534 |
*
|
|
|
535 |
* Always use this function if you need to create header with sorting and help icon.
|
|
|
536 |
*
|
|
|
537 |
* @param renderable[] $helpicons An array of renderable objects to be used as help icons
|
|
|
538 |
*/
|
|
|
539 |
public function define_help_for_headers($helpicons) {
|
|
|
540 |
$this->helpforheaders = $helpicons;
|
|
|
541 |
}
|
|
|
542 |
|
|
|
543 |
/**
|
|
|
544 |
* Mark the table preferences to be reset.
|
|
|
545 |
*/
|
|
|
546 |
public function mark_table_to_reset(): void {
|
|
|
547 |
$this->resetting = true;
|
|
|
548 |
}
|
|
|
549 |
|
|
|
550 |
/**
|
|
|
551 |
* Is the table marked for reset preferences?
|
|
|
552 |
*
|
|
|
553 |
* @return bool True if the table is marked to reset, false otherwise.
|
|
|
554 |
*/
|
|
|
555 |
protected function is_resetting_preferences(): bool {
|
|
|
556 |
if ($this->resetting === null) {
|
|
|
557 |
$this->resetting = optional_param($this->request[TABLE_VAR_RESET], false, PARAM_BOOL);
|
|
|
558 |
}
|
|
|
559 |
|
|
|
560 |
return $this->resetting;
|
|
|
561 |
}
|
|
|
562 |
|
|
|
563 |
/**
|
|
|
564 |
* Must be called after table is defined. Use methods above first. Cannot
|
|
|
565 |
* use functions below till after calling this method.
|
|
|
566 |
*/
|
|
|
567 |
function setup() {
|
|
|
568 |
|
|
|
569 |
if (empty($this->columns) || empty($this->uniqueid)) {
|
|
|
570 |
return false;
|
|
|
571 |
}
|
|
|
572 |
|
|
|
573 |
$this->initialise_table_preferences();
|
|
|
574 |
|
|
|
575 |
if (empty($this->baseurl)) {
|
|
|
576 |
debugging('You should set baseurl when using flexible_table.');
|
|
|
577 |
global $PAGE;
|
|
|
578 |
$this->baseurl = $PAGE->url;
|
|
|
579 |
}
|
|
|
580 |
|
|
|
581 |
if ($this->currpage == null) {
|
|
|
582 |
$this->currpage = optional_param($this->request[TABLE_VAR_PAGE], 0, PARAM_INT);
|
|
|
583 |
}
|
|
|
584 |
|
|
|
585 |
$this->setup = true;
|
|
|
586 |
|
|
|
587 |
// Always introduce the "flexible" class for the table if not specified
|
|
|
588 |
if (empty($this->attributes)) {
|
|
|
589 |
$this->attributes['class'] = 'flexible table table-striped table-hover';
|
|
|
590 |
} else if (!isset($this->attributes['class'])) {
|
|
|
591 |
$this->attributes['class'] = 'flexible table table-striped table-hover';
|
|
|
592 |
} else if (!in_array('flexible', explode(' ', $this->attributes['class']))) {
|
|
|
593 |
$this->attributes['class'] = trim('flexible table table-striped table-hover ' . $this->attributes['class']);
|
|
|
594 |
}
|
|
|
595 |
}
|
|
|
596 |
|
|
|
597 |
/**
|
|
|
598 |
* Get the order by clause from the session or user preferences, for the table with id $uniqueid.
|
|
|
599 |
* @param string $uniqueid the identifier for a table.
|
|
|
600 |
* @return string SQL fragment that can be used in an ORDER BY clause.
|
|
|
601 |
*/
|
|
|
602 |
public static function get_sort_for_table($uniqueid) {
|
|
|
603 |
global $SESSION;
|
|
|
604 |
if (isset($SESSION->flextable[$uniqueid])) {
|
|
|
605 |
$prefs = $SESSION->flextable[$uniqueid];
|
|
|
606 |
} else if (!$prefs = json_decode(get_user_preferences("flextable_{$uniqueid}", ''), true)) {
|
|
|
607 |
return '';
|
|
|
608 |
}
|
|
|
609 |
|
|
|
610 |
if (empty($prefs['sortby'])) {
|
|
|
611 |
return '';
|
|
|
612 |
}
|
|
|
613 |
if (empty($prefs['textsort'])) {
|
|
|
614 |
$prefs['textsort'] = array();
|
|
|
615 |
}
|
|
|
616 |
|
|
|
617 |
return self::construct_order_by($prefs['sortby'], $prefs['textsort']);
|
|
|
618 |
}
|
|
|
619 |
|
|
|
620 |
/**
|
|
|
621 |
* Prepare an an order by clause from the list of columns to be sorted.
|
|
|
622 |
* @param array $cols column name => SORT_ASC or SORT_DESC
|
|
|
623 |
* @return string SQL fragment that can be used in an ORDER BY clause.
|
|
|
624 |
*/
|
|
|
625 |
public static function construct_order_by($cols, $textsortcols=array()) {
|
|
|
626 |
global $DB;
|
|
|
627 |
$bits = array();
|
|
|
628 |
|
|
|
629 |
foreach ($cols as $column => $order) {
|
|
|
630 |
if (in_array($column, $textsortcols)) {
|
|
|
631 |
$column = $DB->sql_order_by_text($column);
|
|
|
632 |
}
|
|
|
633 |
if ($order == SORT_ASC) {
|
|
|
634 |
$bits[] = $DB->sql_order_by_null($column);
|
|
|
635 |
} else {
|
|
|
636 |
$bits[] = $DB->sql_order_by_null($column, SORT_DESC);
|
|
|
637 |
}
|
|
|
638 |
}
|
|
|
639 |
|
|
|
640 |
return implode(', ', $bits);
|
|
|
641 |
}
|
|
|
642 |
|
|
|
643 |
/**
|
|
|
644 |
* @return string SQL fragment that can be used in an ORDER BY clause.
|
|
|
645 |
*/
|
|
|
646 |
public function get_sql_sort() {
|
|
|
647 |
return self::construct_order_by($this->get_sort_columns(), $this->column_textsort);
|
|
|
648 |
}
|
|
|
649 |
|
|
|
650 |
/**
|
|
|
651 |
* Whether the current table contains any fullname columns
|
|
|
652 |
*
|
|
|
653 |
* @return bool
|
|
|
654 |
*/
|
|
|
655 |
private function contains_fullname_columns(): bool {
|
|
|
656 |
$fullnamecolumns = array_intersect_key($this->columns, array_flip($this->userfullnamecolumns));
|
|
|
657 |
|
|
|
658 |
return !empty($fullnamecolumns);
|
|
|
659 |
}
|
|
|
660 |
|
|
|
661 |
/**
|
|
|
662 |
* Get the columns to sort by, in the form required by {@link construct_order_by()}.
|
|
|
663 |
* @return array column name => SORT_... constant.
|
|
|
664 |
*/
|
|
|
665 |
public function get_sort_columns() {
|
|
|
666 |
if (!$this->setup) {
|
|
|
667 |
throw new coding_exception('Cannot call get_sort_columns until you have called setup.');
|
|
|
668 |
}
|
|
|
669 |
|
|
|
670 |
if (empty($this->prefs['sortby'])) {
|
|
|
671 |
return array();
|
|
|
672 |
}
|
|
|
673 |
foreach ($this->prefs['sortby'] as $column => $notused) {
|
|
|
674 |
if (isset($this->columns[$column])) {
|
|
|
675 |
continue; // This column is OK.
|
|
|
676 |
}
|
|
|
677 |
if (in_array($column, \core_user\fields::get_name_fields()) && $this->contains_fullname_columns()) {
|
|
|
678 |
continue; // This column is OK.
|
|
|
679 |
}
|
|
|
680 |
// This column is not OK.
|
|
|
681 |
unset($this->prefs['sortby'][$column]);
|
|
|
682 |
}
|
|
|
683 |
|
|
|
684 |
return $this->prefs['sortby'];
|
|
|
685 |
}
|
|
|
686 |
|
|
|
687 |
/**
|
|
|
688 |
* @return int the offset for LIMIT clause of SQL
|
|
|
689 |
*/
|
|
|
690 |
function get_page_start() {
|
|
|
691 |
if (!$this->use_pages) {
|
|
|
692 |
return '';
|
|
|
693 |
}
|
|
|
694 |
return $this->currpage * $this->pagesize;
|
|
|
695 |
}
|
|
|
696 |
|
|
|
697 |
/**
|
|
|
698 |
* @return int the pagesize for LIMIT clause of SQL
|
|
|
699 |
*/
|
|
|
700 |
function get_page_size() {
|
|
|
701 |
if (!$this->use_pages) {
|
|
|
702 |
return '';
|
|
|
703 |
}
|
|
|
704 |
return $this->pagesize;
|
|
|
705 |
}
|
|
|
706 |
|
|
|
707 |
/**
|
|
|
708 |
* @return array sql to add to where statement.
|
|
|
709 |
*/
|
|
|
710 |
function get_sql_where() {
|
|
|
711 |
global $DB;
|
|
|
712 |
|
|
|
713 |
$conditions = array();
|
|
|
714 |
$params = array();
|
|
|
715 |
|
|
|
716 |
if ($this->contains_fullname_columns()) {
|
|
|
717 |
static $i = 0;
|
|
|
718 |
$i++;
|
|
|
719 |
|
|
|
720 |
if (!empty($this->prefs['i_first'])) {
|
|
|
721 |
$conditions[] = $DB->sql_like('firstname', ':ifirstc'.$i, false, false);
|
|
|
722 |
$params['ifirstc'.$i] = $this->prefs['i_first'].'%';
|
|
|
723 |
}
|
|
|
724 |
if (!empty($this->prefs['i_last'])) {
|
|
|
725 |
$conditions[] = $DB->sql_like('lastname', ':ilastc'.$i, false, false);
|
|
|
726 |
$params['ilastc'.$i] = $this->prefs['i_last'].'%';
|
|
|
727 |
}
|
|
|
728 |
}
|
|
|
729 |
|
|
|
730 |
return array(implode(" AND ", $conditions), $params);
|
|
|
731 |
}
|
|
|
732 |
|
|
|
733 |
/**
|
|
|
734 |
* Add a row of data to the table. This function takes an array or object with
|
|
|
735 |
* column names as keys or property names.
|
|
|
736 |
*
|
|
|
737 |
* It ignores any elements with keys that are not defined as columns. It
|
|
|
738 |
* puts in empty strings into the row when there is no element in the passed
|
|
|
739 |
* array corresponding to a column in the table. It puts the row elements in
|
|
|
740 |
* the proper order (internally row table data is stored by in arrays with
|
|
|
741 |
* a numerical index corresponding to the column number).
|
|
|
742 |
*
|
|
|
743 |
* @param object|array $rowwithkeys array keys or object property names are column names,
|
|
|
744 |
* as defined in call to define_columns.
|
|
|
745 |
* @param string $classname CSS class name to add to this row's tr tag.
|
|
|
746 |
*/
|
|
|
747 |
function add_data_keyed($rowwithkeys, $classname = '') {
|
|
|
748 |
$this->add_data($this->get_row_from_keyed($rowwithkeys), $classname);
|
|
|
749 |
}
|
|
|
750 |
|
|
|
751 |
/**
|
|
|
752 |
* Add a number of rows to the table at once. And optionally finish output after they have been added.
|
|
|
753 |
*
|
|
|
754 |
* @param (object|array|null)[] $rowstoadd Array of rows to add to table, a null value in array adds a separator row. Or a
|
|
|
755 |
* object or array is added to table. We expect properties for the row array as would be
|
|
|
756 |
* passed to add_data_keyed.
|
|
|
757 |
* @param bool $finish
|
|
|
758 |
*/
|
|
|
759 |
public function format_and_add_array_of_rows($rowstoadd, $finish = true) {
|
|
|
760 |
foreach ($rowstoadd as $row) {
|
|
|
761 |
if (is_null($row)) {
|
|
|
762 |
$this->add_separator();
|
|
|
763 |
} else {
|
|
|
764 |
$this->add_data_keyed($this->format_row($row));
|
|
|
765 |
}
|
|
|
766 |
}
|
|
|
767 |
if ($finish) {
|
|
|
768 |
$this->finish_output(!$this->is_downloading());
|
|
|
769 |
}
|
|
|
770 |
}
|
|
|
771 |
|
|
|
772 |
/**
|
|
|
773 |
* Add a seperator line to table.
|
|
|
774 |
*/
|
|
|
775 |
function add_separator() {
|
|
|
776 |
if (!$this->setup) {
|
|
|
777 |
return false;
|
|
|
778 |
}
|
|
|
779 |
$this->add_data(NULL);
|
|
|
780 |
}
|
|
|
781 |
|
|
|
782 |
/**
|
|
|
783 |
* This method actually directly echoes the row passed to it now or adds it
|
|
|
784 |
* to the download. If this is the first row and start_output has not
|
|
|
785 |
* already been called this method also calls start_output to open the table
|
|
|
786 |
* or send headers for the downloaded.
|
|
|
787 |
* Can be used as before. print_html now calls finish_html to close table.
|
|
|
788 |
*
|
|
|
789 |
* @param array $row a numerically keyed row of data to add to the table.
|
|
|
790 |
* @param string $classname CSS class name to add to this row's tr tag.
|
|
|
791 |
* @return bool success.
|
|
|
792 |
*/
|
|
|
793 |
function add_data($row, $classname = '') {
|
|
|
794 |
if (!$this->setup) {
|
|
|
795 |
return false;
|
|
|
796 |
}
|
|
|
797 |
if (!$this->started_output) {
|
|
|
798 |
$this->start_output();
|
|
|
799 |
}
|
|
|
800 |
if ($this->exportclass!==null) {
|
|
|
801 |
if ($row === null) {
|
|
|
802 |
$this->exportclass->add_seperator();
|
|
|
803 |
} else {
|
|
|
804 |
$this->exportclass->add_data($row);
|
|
|
805 |
}
|
|
|
806 |
} else {
|
|
|
807 |
$this->print_row($row, $classname);
|
|
|
808 |
}
|
|
|
809 |
return true;
|
|
|
810 |
}
|
|
|
811 |
|
|
|
812 |
/**
|
|
|
813 |
* You should call this to finish outputting the table data after adding
|
|
|
814 |
* data to the table with add_data or add_data_keyed.
|
|
|
815 |
*
|
|
|
816 |
*/
|
|
|
817 |
function finish_output($closeexportclassdoc = true) {
|
|
|
818 |
if ($this->exportclass!==null) {
|
|
|
819 |
$this->exportclass->finish_table();
|
|
|
820 |
if ($closeexportclassdoc) {
|
|
|
821 |
$this->exportclass->finish_document();
|
|
|
822 |
}
|
|
|
823 |
} else {
|
|
|
824 |
$this->finish_html();
|
|
|
825 |
}
|
|
|
826 |
}
|
|
|
827 |
|
|
|
828 |
/**
|
|
|
829 |
* Hook that can be overridden in child classes to wrap a table in a form
|
|
|
830 |
* for example. Called only when there is data to display and not
|
|
|
831 |
* downloading.
|
|
|
832 |
*/
|
|
|
833 |
function wrap_html_start() {
|
|
|
834 |
}
|
|
|
835 |
|
|
|
836 |
/**
|
|
|
837 |
* Hook that can be overridden in child classes to wrap a table in a form
|
|
|
838 |
* for example. Called only when there is data to display and not
|
|
|
839 |
* downloading.
|
|
|
840 |
*/
|
|
|
841 |
function wrap_html_finish() {
|
|
|
842 |
}
|
|
|
843 |
|
|
|
844 |
/**
|
|
|
845 |
* Call appropriate methods on this table class to perform any processing on values before displaying in table.
|
|
|
846 |
* Takes raw data from the database and process it into human readable format, perhaps also adding html linking when
|
|
|
847 |
* displaying table as html, adding a div wrap, etc.
|
|
|
848 |
*
|
|
|
849 |
* See for example col_fullname below which will be called for a column whose name is 'fullname'.
|
|
|
850 |
*
|
|
|
851 |
* @param array|object $row row of data from db used to make one row of the table.
|
|
|
852 |
* @return array one row for the table, added using add_data_keyed method.
|
|
|
853 |
*/
|
|
|
854 |
function format_row($row) {
|
|
|
855 |
if (is_array($row)) {
|
|
|
856 |
$row = (object)$row;
|
|
|
857 |
}
|
|
|
858 |
$formattedrow = array();
|
|
|
859 |
foreach (array_keys($this->columns) as $column) {
|
|
|
860 |
$colmethodname = 'col_'.$column;
|
|
|
861 |
if (method_exists($this, $colmethodname)) {
|
|
|
862 |
$formattedcolumn = $this->$colmethodname($row);
|
|
|
863 |
} else {
|
|
|
864 |
$formattedcolumn = $this->other_cols($column, $row);
|
|
|
865 |
if ($formattedcolumn===NULL) {
|
|
|
866 |
$formattedcolumn = $row->$column;
|
|
|
867 |
}
|
|
|
868 |
}
|
|
|
869 |
$formattedrow[$column] = $formattedcolumn;
|
|
|
870 |
}
|
|
|
871 |
return $formattedrow;
|
|
|
872 |
}
|
|
|
873 |
|
|
|
874 |
/**
|
|
|
875 |
* Fullname is treated as a special columname in tablelib and should always
|
|
|
876 |
* be treated the same as the fullname of a user.
|
|
|
877 |
* @uses $this->useridfield if the userid field is not expected to be id
|
|
|
878 |
* then you need to override $this->useridfield to point at the correct
|
|
|
879 |
* field for the user id.
|
|
|
880 |
*
|
|
|
881 |
* @param object $row the data from the db containing all fields from the
|
|
|
882 |
* users table necessary to construct the full name of the user in
|
|
|
883 |
* current language.
|
|
|
884 |
* @return string contents of cell in column 'fullname', for this row.
|
|
|
885 |
*/
|
|
|
886 |
function col_fullname($row) {
|
|
|
887 |
global $COURSE;
|
|
|
888 |
|
|
|
889 |
$name = fullname($row, has_capability('moodle/site:viewfullnames', $this->get_context()));
|
|
|
890 |
if ($this->download) {
|
|
|
891 |
return $name;
|
|
|
892 |
}
|
|
|
893 |
|
|
|
894 |
$userid = $row->{$this->useridfield};
|
|
|
895 |
if ($COURSE->id == SITEID) {
|
|
|
896 |
$profileurl = new moodle_url('/user/profile.php', array('id' => $userid));
|
|
|
897 |
} else {
|
|
|
898 |
$profileurl = new moodle_url('/user/view.php',
|
|
|
899 |
array('id' => $userid, 'course' => $COURSE->id));
|
|
|
900 |
}
|
|
|
901 |
return html_writer::link($profileurl, $name);
|
|
|
902 |
}
|
|
|
903 |
|
|
|
904 |
/**
|
|
|
905 |
* You can override this method in a child class. See the description of
|
|
|
906 |
* build_table which calls this method.
|
|
|
907 |
*/
|
|
|
908 |
function other_cols($column, $row) {
|
|
|
909 |
if (isset($row->$column) && ($column === 'email' || $column === 'idnumber') &&
|
|
|
910 |
(!$this->is_downloading() || $this->export_class_instance()->supports_html())) {
|
|
|
911 |
// Columns email and idnumber may potentially contain malicious characters, escape them by default.
|
|
|
912 |
// This function will not be executed if the child class implements col_email() or col_idnumber().
|
|
|
913 |
return s($row->$column);
|
|
|
914 |
}
|
|
|
915 |
return NULL;
|
|
|
916 |
}
|
|
|
917 |
|
|
|
918 |
/**
|
|
|
919 |
* Used from col_* functions when text is to be displayed. Does the
|
|
|
920 |
* right thing - either converts text to html or strips any html tags
|
|
|
921 |
* depending on if we are downloading and what is the download type. Params
|
|
|
922 |
* are the same as format_text function in weblib.php but some default
|
|
|
923 |
* options are changed.
|
|
|
924 |
*/
|
|
|
925 |
function format_text($text, $format=FORMAT_MOODLE, $options=NULL, $courseid=NULL) {
|
|
|
926 |
if (!$this->is_downloading()) {
|
|
|
927 |
if (is_null($options)) {
|
|
|
928 |
$options = new stdClass;
|
|
|
929 |
}
|
|
|
930 |
//some sensible defaults
|
|
|
931 |
if (!isset($options->para)) {
|
|
|
932 |
$options->para = false;
|
|
|
933 |
}
|
|
|
934 |
if (!isset($options->newlines)) {
|
|
|
935 |
$options->newlines = false;
|
|
|
936 |
}
|
|
|
937 |
if (!isset($options->filter)) {
|
|
|
938 |
$options->filter = false;
|
|
|
939 |
}
|
|
|
940 |
return format_text($text, $format, $options);
|
|
|
941 |
} else {
|
|
|
942 |
$eci = $this->export_class_instance();
|
|
|
943 |
return $eci->format_text($text, $format, $options, $courseid);
|
|
|
944 |
}
|
|
|
945 |
}
|
|
|
946 |
/**
|
|
|
947 |
* This method is deprecated although the old api is still supported.
|
|
|
948 |
* @deprecated 1.9.2 - Jun 2, 2008
|
|
|
949 |
*/
|
|
|
950 |
function print_html() {
|
|
|
951 |
if (!$this->setup) {
|
|
|
952 |
return false;
|
|
|
953 |
}
|
|
|
954 |
$this->finish_html();
|
|
|
955 |
}
|
|
|
956 |
|
|
|
957 |
/**
|
|
|
958 |
* This function is not part of the public api.
|
|
|
959 |
* @return string initial of first name we are currently filtering by
|
|
|
960 |
*/
|
|
|
961 |
function get_initial_first() {
|
|
|
962 |
if (!$this->use_initials) {
|
|
|
963 |
return NULL;
|
|
|
964 |
}
|
|
|
965 |
|
|
|
966 |
return $this->prefs['i_first'];
|
|
|
967 |
}
|
|
|
968 |
|
|
|
969 |
/**
|
|
|
970 |
* This function is not part of the public api.
|
|
|
971 |
* @return string initial of last name we are currently filtering by
|
|
|
972 |
*/
|
|
|
973 |
function get_initial_last() {
|
|
|
974 |
if (!$this->use_initials) {
|
|
|
975 |
return NULL;
|
|
|
976 |
}
|
|
|
977 |
|
|
|
978 |
return $this->prefs['i_last'];
|
|
|
979 |
}
|
|
|
980 |
|
|
|
981 |
/**
|
|
|
982 |
* Helper function, used by {@link print_initials_bar()} to output one initial bar.
|
|
|
983 |
* @param array $alpha of letters in the alphabet.
|
|
|
984 |
* @param string $current the currently selected letter.
|
|
|
985 |
* @param string $class class name to add to this initial bar.
|
|
|
986 |
* @param string $title the name to put in front of this initial bar.
|
|
|
987 |
* @param string $urlvar URL parameter name for this initial.
|
|
|
988 |
*
|
|
|
989 |
* @deprecated since Moodle 3.3
|
|
|
990 |
*/
|
|
|
991 |
protected function print_one_initials_bar($alpha, $current, $class, $title, $urlvar) {
|
|
|
992 |
|
|
|
993 |
debugging('Method print_one_initials_bar() is no longer used and has been deprecated, ' .
|
|
|
994 |
'to print initials bar call print_initials_bar()', DEBUG_DEVELOPER);
|
|
|
995 |
|
|
|
996 |
echo html_writer::start_tag('div', array('class' => 'initialbar ' . $class)) .
|
|
|
997 |
$title . ' : ';
|
|
|
998 |
if ($current) {
|
|
|
999 |
echo html_writer::link($this->baseurl->out(false, array($urlvar => '')), get_string('all'));
|
|
|
1000 |
} else {
|
|
|
1001 |
echo html_writer::tag('strong', get_string('all'));
|
|
|
1002 |
}
|
|
|
1003 |
|
|
|
1004 |
foreach ($alpha as $letter) {
|
|
|
1005 |
if ($letter === $current) {
|
|
|
1006 |
echo html_writer::tag('strong', $letter);
|
|
|
1007 |
} else {
|
|
|
1008 |
echo html_writer::link($this->baseurl->out(false, array($urlvar => $letter)), $letter);
|
|
|
1009 |
}
|
|
|
1010 |
}
|
|
|
1011 |
|
|
|
1012 |
echo html_writer::end_tag('div');
|
|
|
1013 |
}
|
|
|
1014 |
|
|
|
1015 |
/**
|
|
|
1016 |
* This function is not part of the public api.
|
|
|
1017 |
*/
|
|
|
1018 |
function print_initials_bar() {
|
|
|
1019 |
global $OUTPUT;
|
|
|
1020 |
|
|
|
1021 |
$ifirst = $this->get_initial_first();
|
|
|
1022 |
$ilast = $this->get_initial_last();
|
|
|
1023 |
if (is_null($ifirst)) {
|
|
|
1024 |
$ifirst = '';
|
|
|
1025 |
}
|
|
|
1026 |
if (is_null($ilast)) {
|
|
|
1027 |
$ilast = '';
|
|
|
1028 |
}
|
|
|
1029 |
|
|
|
1030 |
if ((!empty($ifirst) || !empty($ilast) || $this->use_initials) && $this->contains_fullname_columns()) {
|
|
|
1031 |
$prefixfirst = $this->request[TABLE_VAR_IFIRST];
|
|
|
1032 |
$prefixlast = $this->request[TABLE_VAR_ILAST];
|
|
|
1033 |
echo $OUTPUT->initials_bar($ifirst, 'firstinitial', get_string('firstname'), $prefixfirst, $this->baseurl);
|
|
|
1034 |
echo $OUTPUT->initials_bar($ilast, 'lastinitial', get_string('lastname'), $prefixlast, $this->baseurl);
|
|
|
1035 |
}
|
|
|
1036 |
|
|
|
1037 |
}
|
|
|
1038 |
|
|
|
1039 |
/**
|
|
|
1040 |
* This function is not part of the public api.
|
|
|
1041 |
*/
|
|
|
1042 |
function print_nothing_to_display() {
|
|
|
1043 |
global $OUTPUT;
|
|
|
1044 |
|
|
|
1045 |
// Render the dynamic table header.
|
|
|
1046 |
echo $this->get_dynamic_table_html_start();
|
|
|
1047 |
|
|
|
1048 |
// Render button to allow user to reset table preferences.
|
|
|
1049 |
echo $this->render_reset_button();
|
|
|
1050 |
|
|
|
1051 |
$this->print_initials_bar();
|
|
|
1052 |
|
|
|
1053 |
echo $OUTPUT->notification(get_string('nothingtodisplay'), 'info', false);
|
|
|
1054 |
|
|
|
1055 |
// Render the dynamic table footer.
|
|
|
1056 |
echo $this->get_dynamic_table_html_end();
|
|
|
1057 |
}
|
|
|
1058 |
|
|
|
1059 |
/**
|
|
|
1060 |
* This function is not part of the public api.
|
|
|
1061 |
*/
|
|
|
1062 |
function get_row_from_keyed($rowwithkeys) {
|
|
|
1063 |
if (is_object($rowwithkeys)) {
|
|
|
1064 |
$rowwithkeys = (array)$rowwithkeys;
|
|
|
1065 |
}
|
|
|
1066 |
$row = array();
|
|
|
1067 |
foreach (array_keys($this->columns) as $column) {
|
|
|
1068 |
if (isset($rowwithkeys[$column])) {
|
|
|
1069 |
$row [] = $rowwithkeys[$column];
|
|
|
1070 |
} else {
|
|
|
1071 |
$row[] ='';
|
|
|
1072 |
}
|
|
|
1073 |
}
|
|
|
1074 |
return $row;
|
|
|
1075 |
}
|
|
|
1076 |
|
|
|
1077 |
/**
|
|
|
1078 |
* Get the html for the download buttons
|
|
|
1079 |
*
|
|
|
1080 |
* Usually only use internally
|
|
|
1081 |
*/
|
|
|
1082 |
public function download_buttons() {
|
|
|
1083 |
global $OUTPUT;
|
|
|
1084 |
|
|
|
1085 |
if ($this->is_downloadable() && !$this->is_downloading()) {
|
|
|
1086 |
return $OUTPUT->download_dataformat_selector(get_string('downloadas', 'table'),
|
|
|
1087 |
$this->baseurl->out_omit_querystring(), 'download', $this->baseurl->params());
|
|
|
1088 |
} else {
|
|
|
1089 |
return '';
|
|
|
1090 |
}
|
|
|
1091 |
}
|
|
|
1092 |
|
|
|
1093 |
/**
|
|
|
1094 |
* This function is not part of the public api.
|
|
|
1095 |
* You don't normally need to call this. It is called automatically when
|
|
|
1096 |
* needed when you start adding data to the table.
|
|
|
1097 |
*
|
|
|
1098 |
*/
|
|
|
1099 |
function start_output() {
|
|
|
1100 |
$this->started_output = true;
|
|
|
1101 |
if ($this->exportclass!==null) {
|
|
|
1102 |
$this->exportclass->start_table($this->sheettitle);
|
|
|
1103 |
$this->exportclass->output_headers($this->headers);
|
|
|
1104 |
} else {
|
|
|
1105 |
$this->start_html();
|
|
|
1106 |
$this->print_headers();
|
|
|
1107 |
echo html_writer::start_tag('tbody');
|
|
|
1108 |
}
|
|
|
1109 |
}
|
|
|
1110 |
|
|
|
1111 |
/**
|
|
|
1112 |
* This function is not part of the public api.
|
|
|
1113 |
*/
|
|
|
1114 |
function print_row($row, $classname = '') {
|
|
|
1115 |
echo $this->get_row_html($row, $classname);
|
|
|
1116 |
}
|
|
|
1117 |
|
|
|
1118 |
/**
|
|
|
1119 |
* Generate html code for the passed row.
|
|
|
1120 |
*
|
|
|
1121 |
* @param array $row Row data.
|
|
|
1122 |
* @param string $classname classes to add.
|
|
|
1123 |
*
|
|
|
1124 |
* @return string $html html code for the row passed.
|
|
|
1125 |
*/
|
|
|
1126 |
public function get_row_html($row, $classname = '') {
|
|
|
1127 |
static $suppress_lastrow = NULL;
|
|
|
1128 |
$rowclasses = array();
|
|
|
1129 |
|
|
|
1130 |
if ($classname) {
|
|
|
1131 |
$rowclasses[] = $classname;
|
|
|
1132 |
}
|
|
|
1133 |
|
|
|
1134 |
$rowid = $this->uniqueid . '_r' . $this->currentrow;
|
|
|
1135 |
$html = '';
|
|
|
1136 |
|
|
|
1137 |
$html .= html_writer::start_tag('tr', array('class' => implode(' ', $rowclasses), 'id' => $rowid));
|
|
|
1138 |
|
|
|
1139 |
// If we have a separator, print it
|
|
|
1140 |
if ($row === NULL) {
|
|
|
1141 |
$colcount = count($this->columns);
|
|
|
1142 |
$html .= html_writer::tag('td', html_writer::tag('div', '',
|
|
|
1143 |
array('class' => 'tabledivider')), array('colspan' => $colcount));
|
|
|
1144 |
|
|
|
1145 |
} else {
|
|
|
1146 |
$html .= $this->get_row_cells_html($rowid, $row, $suppress_lastrow);
|
|
|
1147 |
}
|
|
|
1148 |
|
|
|
1149 |
$html .= html_writer::end_tag('tr');
|
|
|
1150 |
|
|
|
1151 |
$suppress_enabled = array_sum($this->column_suppress);
|
|
|
1152 |
if ($suppress_enabled) {
|
|
|
1153 |
$suppress_lastrow = $row;
|
|
|
1154 |
}
|
|
|
1155 |
$this->currentrow++;
|
|
|
1156 |
return $html;
|
|
|
1157 |
}
|
|
|
1158 |
|
|
|
1159 |
/**
|
|
|
1160 |
* Generate html code for the row cells.
|
|
|
1161 |
*
|
|
|
1162 |
* @param string $rowid
|
|
|
1163 |
* @param array $row
|
|
|
1164 |
* @param array|null $suppresslastrow
|
|
|
1165 |
* @return string
|
|
|
1166 |
*/
|
|
|
1167 |
public function get_row_cells_html(string $rowid, array $row, ?array $suppresslastrow): string {
|
|
|
1168 |
$html = '';
|
|
|
1169 |
$colbyindex = array_flip($this->columns);
|
|
|
1170 |
foreach ($row as $index => $data) {
|
|
|
1171 |
$column = $colbyindex[$index];
|
|
|
1172 |
|
|
|
1173 |
$columnattributes = $this->columnsattributes[$column] ?? [];
|
|
|
1174 |
if (isset($columnattributes['class'])) {
|
|
|
1175 |
$this->column_class($column, $columnattributes['class']);
|
|
|
1176 |
unset($columnattributes['class']);
|
|
|
1177 |
}
|
|
|
1178 |
|
|
|
1179 |
$attributes = [
|
|
|
1180 |
'class' => "cell c{$index}" . $this->column_class[$column] . $this->columnsticky[$column],
|
|
|
1181 |
'id' => "{$rowid}_c{$index}",
|
|
|
1182 |
'style' => $this->make_styles_string($this->column_style[$column]),
|
|
|
1183 |
];
|
|
|
1184 |
|
|
|
1185 |
$celltype = 'td';
|
|
|
1186 |
if ($this->headercolumn && $column == $this->headercolumn) {
|
|
|
1187 |
$celltype = 'th';
|
|
|
1188 |
$attributes['scope'] = 'row';
|
|
|
1189 |
}
|
|
|
1190 |
|
|
|
1191 |
$attributes += $columnattributes;
|
|
|
1192 |
|
|
|
1193 |
if (empty($this->prefs['collapse'][$column])) {
|
|
|
1194 |
if ($this->column_suppress[$column] && $suppresslastrow !== null && $suppresslastrow[$index] === $data) {
|
|
|
1195 |
$content = ' ';
|
|
|
1196 |
} else {
|
|
|
1197 |
$content = $data;
|
|
|
1198 |
}
|
|
|
1199 |
} else {
|
|
|
1200 |
$content = ' ';
|
|
|
1201 |
}
|
|
|
1202 |
|
|
|
1203 |
$html .= html_writer::tag($celltype, $content, $attributes);
|
|
|
1204 |
}
|
|
|
1205 |
return $html;
|
|
|
1206 |
}
|
|
|
1207 |
|
|
|
1208 |
/**
|
|
|
1209 |
* This function is not part of the public api.
|
|
|
1210 |
*/
|
|
|
1211 |
function finish_html() {
|
|
|
1212 |
global $OUTPUT, $PAGE;
|
|
|
1213 |
|
|
|
1214 |
if (!$this->started_output) {
|
|
|
1215 |
//no data has been added to the table.
|
|
|
1216 |
$this->print_nothing_to_display();
|
|
|
1217 |
|
|
|
1218 |
} else {
|
|
|
1219 |
// Print empty rows to fill the table to the current pagesize.
|
|
|
1220 |
// This is done so the header aria-controls attributes do not point to
|
|
|
1221 |
// non existant elements.
|
|
|
1222 |
$emptyrow = array_fill(0, count($this->columns), '');
|
|
|
1223 |
while ($this->currentrow < $this->pagesize) {
|
|
|
1224 |
$this->print_row($emptyrow, 'emptyrow');
|
|
|
1225 |
}
|
|
|
1226 |
|
|
|
1227 |
echo html_writer::end_tag('tbody');
|
|
|
1228 |
echo html_writer::end_tag('table');
|
|
|
1229 |
echo html_writer::end_tag('div');
|
|
|
1230 |
$this->wrap_html_finish();
|
|
|
1231 |
|
|
|
1232 |
// Paging bar
|
|
|
1233 |
if(in_array(TABLE_P_BOTTOM, $this->showdownloadbuttonsat)) {
|
|
|
1234 |
echo $this->download_buttons();
|
|
|
1235 |
}
|
|
|
1236 |
|
|
|
1237 |
if($this->use_pages) {
|
|
|
1238 |
$pagingbar = new paging_bar($this->totalrows, $this->currpage, $this->pagesize, $this->baseurl);
|
|
|
1239 |
$pagingbar->pagevar = $this->request[TABLE_VAR_PAGE];
|
|
|
1240 |
echo $OUTPUT->render($pagingbar);
|
|
|
1241 |
}
|
|
|
1242 |
|
|
|
1243 |
// Render the dynamic table footer.
|
|
|
1244 |
echo $this->get_dynamic_table_html_end();
|
|
|
1245 |
}
|
|
|
1246 |
}
|
|
|
1247 |
|
|
|
1248 |
/**
|
|
|
1249 |
* Generate the HTML for the collapse/uncollapse icon. This is a helper method
|
|
|
1250 |
* used by {@link print_headers()}.
|
|
|
1251 |
* @param string $column the column name, index into various names.
|
|
|
1252 |
* @param int $index numerical index of the column.
|
|
|
1253 |
* @return string HTML fragment.
|
|
|
1254 |
*/
|
|
|
1255 |
protected function show_hide_link($column, $index) {
|
|
|
1256 |
global $OUTPUT;
|
|
|
1257 |
// Some headers contain <br /> tags, do not include in title, hence the
|
|
|
1258 |
// strip tags.
|
|
|
1259 |
|
|
|
1260 |
$ariacontrols = '';
|
|
|
1261 |
for ($i = 0; $i < $this->pagesize; $i++) {
|
|
|
1262 |
$ariacontrols .= $this->uniqueid . '_r' . $i . '_c' . $index . ' ';
|
|
|
1263 |
}
|
|
|
1264 |
|
|
|
1265 |
$ariacontrols = trim($ariacontrols);
|
|
|
1266 |
|
|
|
1267 |
if (!empty($this->prefs['collapse'][$column])) {
|
|
|
1268 |
$linkattributes = [
|
|
|
1269 |
'title' => get_string('show') . ' ' . strip_tags($this->headers[$index]),
|
|
|
1270 |
'aria-expanded' => 'false',
|
|
|
1271 |
'aria-controls' => $ariacontrols,
|
|
|
1272 |
'data-action' => 'show',
|
|
|
1273 |
'data-column' => $column,
|
|
|
1274 |
'role' => 'button',
|
|
|
1275 |
];
|
|
|
1276 |
return html_writer::link($this->baseurl->out(false, array($this->request[TABLE_VAR_SHOW] => $column)),
|
|
|
1277 |
$OUTPUT->pix_icon('t/switch_plus', null), $linkattributes);
|
|
|
1278 |
|
|
|
1279 |
} else if ($this->headers[$index] !== NULL) {
|
|
|
1280 |
$linkattributes = [
|
|
|
1281 |
'title' => get_string('hide') . ' ' . strip_tags($this->headers[$index]),
|
|
|
1282 |
'aria-expanded' => 'true',
|
|
|
1283 |
'aria-controls' => $ariacontrols,
|
|
|
1284 |
'data-action' => 'hide',
|
|
|
1285 |
'data-column' => $column,
|
|
|
1286 |
'role' => 'button',
|
|
|
1287 |
];
|
|
|
1288 |
return html_writer::link($this->baseurl->out(false, array($this->request[TABLE_VAR_HIDE] => $column)),
|
|
|
1289 |
$OUTPUT->pix_icon('t/switch_minus', null), $linkattributes);
|
|
|
1290 |
}
|
|
|
1291 |
}
|
|
|
1292 |
|
|
|
1293 |
/**
|
|
|
1294 |
* This function is not part of the public api.
|
|
|
1295 |
*/
|
|
|
1296 |
function print_headers() {
|
|
|
1297 |
global $CFG, $OUTPUT;
|
|
|
1298 |
|
|
|
1299 |
// Set the primary sort column/order where possible, so that sort links/icons are correct.
|
|
|
1300 |
[
|
|
|
1301 |
'sortby' => $primarysortcolumn,
|
|
|
1302 |
'sortorder' => $primarysortorder,
|
|
|
1303 |
] = $this->get_primary_sort_order();
|
|
|
1304 |
|
|
|
1305 |
echo html_writer::start_tag('thead');
|
|
|
1306 |
echo html_writer::start_tag('tr');
|
|
|
1307 |
foreach ($this->columns as $column => $index) {
|
|
|
1308 |
|
|
|
1309 |
$icon_hide = '';
|
|
|
1310 |
if ($this->is_collapsible) {
|
|
|
1311 |
$icon_hide = $this->show_hide_link($column, $index);
|
|
|
1312 |
}
|
|
|
1313 |
switch ($column) {
|
|
|
1314 |
|
|
|
1315 |
case 'userpic':
|
|
|
1316 |
// do nothing, do not display sortable links
|
|
|
1317 |
break;
|
|
|
1318 |
|
|
|
1319 |
default:
|
|
|
1320 |
|
|
|
1321 |
if (array_search($column, $this->userfullnamecolumns) !== false) {
|
|
|
1322 |
// Check the full name display for sortable fields.
|
|
|
1323 |
if (has_capability('moodle/site:viewfullnames', $this->get_context())) {
|
|
|
1324 |
$nameformat = $CFG->alternativefullnameformat;
|
|
|
1325 |
} else {
|
|
|
1326 |
$nameformat = $CFG->fullnamedisplay;
|
|
|
1327 |
}
|
|
|
1328 |
|
|
|
1329 |
if ($nameformat == 'language') {
|
|
|
1330 |
$nameformat = get_string('fullnamedisplay');
|
|
|
1331 |
}
|
|
|
1332 |
|
|
|
1333 |
$requirednames = order_in_string(\core_user\fields::get_name_fields(), $nameformat);
|
|
|
1334 |
|
|
|
1335 |
if (!empty($requirednames)) {
|
|
|
1336 |
if ($this->is_sortable($column)) {
|
|
|
1337 |
// Done this way for the possibility of more than two sortable full name display fields.
|
|
|
1338 |
$this->headers[$index] = '';
|
|
|
1339 |
foreach ($requirednames as $name) {
|
|
|
1340 |
$sortname = $this->sort_link(get_string($name),
|
|
|
1341 |
$name, $primarysortcolumn === $name, $primarysortorder);
|
|
|
1342 |
$this->headers[$index] .= $sortname . ' / ';
|
|
|
1343 |
}
|
|
|
1344 |
$helpicon = '';
|
|
|
1345 |
if (isset($this->helpforheaders[$index])) {
|
|
|
1346 |
$helpicon = $OUTPUT->render($this->helpforheaders[$index]);
|
|
|
1347 |
}
|
|
|
1348 |
$this->headers[$index] = substr($this->headers[$index], 0, -3) . $helpicon;
|
|
|
1349 |
}
|
|
|
1350 |
}
|
|
|
1351 |
} else if ($this->is_sortable($column)) {
|
|
|
1352 |
$helpicon = '';
|
|
|
1353 |
if (isset($this->helpforheaders[$index])) {
|
|
|
1354 |
$helpicon = $OUTPUT->render($this->helpforheaders[$index]);
|
|
|
1355 |
}
|
|
|
1356 |
$this->headers[$index] = $this->sort_link($this->headers[$index],
|
|
|
1357 |
$column, $primarysortcolumn == $column, $primarysortorder) . $helpicon;
|
|
|
1358 |
}
|
|
|
1359 |
}
|
|
|
1360 |
|
|
|
1361 |
$attributes = array(
|
|
|
1362 |
'class' => 'header c' . $index . $this->column_class[$column] . $this->columnsticky[$column],
|
|
|
1363 |
'scope' => 'col',
|
|
|
1364 |
);
|
|
|
1365 |
if ($this->headers[$index] === NULL) {
|
|
|
1366 |
$content = ' ';
|
|
|
1367 |
} else if (!empty($this->prefs['collapse'][$column])) {
|
|
|
1368 |
$content = $icon_hide;
|
|
|
1369 |
} else {
|
|
|
1370 |
if (is_array($this->column_style[$column])) {
|
|
|
1371 |
$attributes['style'] = $this->make_styles_string($this->column_style[$column]);
|
|
|
1372 |
}
|
|
|
1373 |
$helpicon = '';
|
|
|
1374 |
if (isset($this->helpforheaders[$index]) && !$this->is_sortable($column)) {
|
|
|
1375 |
$helpicon = $OUTPUT->render($this->helpforheaders[$index]);
|
|
|
1376 |
}
|
|
|
1377 |
$content = $this->headers[$index] . $helpicon . html_writer::tag('div',
|
|
|
1378 |
$icon_hide, array('class' => 'commands'));
|
|
|
1379 |
}
|
|
|
1380 |
echo html_writer::tag('th', $content, $attributes);
|
|
|
1381 |
}
|
|
|
1382 |
|
|
|
1383 |
echo html_writer::end_tag('tr');
|
|
|
1384 |
echo html_writer::end_tag('thead');
|
|
|
1385 |
}
|
|
|
1386 |
|
|
|
1387 |
/**
|
|
|
1388 |
* Calculate the preferences for sort order based on user-supplied values and get params.
|
|
|
1389 |
*/
|
|
|
1390 |
protected function set_sorting_preferences(): void {
|
|
|
1391 |
$sortdata = $this->sortdata;
|
|
|
1392 |
|
|
|
1393 |
if ($sortdata === null) {
|
|
|
1394 |
$sortdata = $this->prefs['sortby'];
|
|
|
1395 |
|
|
|
1396 |
$sortorder = optional_param($this->request[TABLE_VAR_DIR], $this->sort_default_order, PARAM_INT);
|
|
|
1397 |
$sortby = optional_param($this->request[TABLE_VAR_SORT], '', PARAM_ALPHANUMEXT);
|
|
|
1398 |
|
|
|
1399 |
if (array_key_exists($sortby, $sortdata)) {
|
|
|
1400 |
// This key already exists somewhere. Change its sortorder and bring it to the top.
|
|
|
1401 |
unset($sortdata[$sortby]);
|
|
|
1402 |
}
|
|
|
1403 |
$sortdata = array_merge([$sortby => $sortorder], $sortdata);
|
|
|
1404 |
}
|
|
|
1405 |
|
|
|
1406 |
$usernamefields = \core_user\fields::get_name_fields();
|
|
|
1407 |
$sortdata = array_filter($sortdata, function($sortby) use ($usernamefields) {
|
|
|
1408 |
$isvalidsort = $sortby && $this->is_sortable($sortby);
|
|
|
1409 |
$isvalidsort = $isvalidsort && empty($this->prefs['collapse'][$sortby]);
|
|
|
1410 |
$isrealcolumn = isset($this->columns[$sortby]);
|
|
|
1411 |
$isfullnamefield = $this->contains_fullname_columns() && in_array($sortby, $usernamefields);
|
|
|
1412 |
|
|
|
1413 |
return $isvalidsort && ($isrealcolumn || $isfullnamefield);
|
|
|
1414 |
}, ARRAY_FILTER_USE_KEY);
|
|
|
1415 |
|
|
|
1416 |
// Finally, make sure that no more than $this->maxsortkeys are present into the array.
|
|
|
1417 |
$sortdata = array_slice($sortdata, 0, $this->maxsortkeys);
|
|
|
1418 |
|
|
|
1419 |
// If a default order is defined and it is not in the current list of order by columns, add it at the end.
|
|
|
1420 |
// This prevents results from being returned in a random order if the only order by column contains equal values.
|
|
|
1421 |
if (!empty($this->sort_default_column) && !array_key_exists($this->sort_default_column, $sortdata)) {
|
|
|
1422 |
$sortdata = array_merge($sortdata, [$this->sort_default_column => $this->sort_default_order]);
|
|
|
1423 |
}
|
|
|
1424 |
|
|
|
1425 |
// Apply the sortdata to the preference.
|
|
|
1426 |
$this->prefs['sortby'] = $sortdata;
|
|
|
1427 |
}
|
|
|
1428 |
|
|
|
1429 |
/**
|
|
|
1430 |
* Fill in the preferences for the initials bar.
|
|
|
1431 |
*/
|
|
|
1432 |
protected function set_initials_preferences(): void {
|
|
|
1433 |
$ifirst = $this->ifirst;
|
|
|
1434 |
$ilast = $this->ilast;
|
|
|
1435 |
|
|
|
1436 |
if ($ifirst === null) {
|
|
|
1437 |
$ifirst = optional_param($this->request[TABLE_VAR_IFIRST], null, PARAM_RAW);
|
|
|
1438 |
}
|
|
|
1439 |
|
|
|
1440 |
if ($ilast === null) {
|
|
|
1441 |
$ilast = optional_param($this->request[TABLE_VAR_ILAST], null, PARAM_RAW);
|
|
|
1442 |
}
|
|
|
1443 |
|
|
|
1444 |
if (!is_null($ifirst) && ($ifirst === '' || strpos(get_string('alphabet', 'langconfig'), $ifirst) !== false)) {
|
|
|
1445 |
$this->prefs['i_first'] = $ifirst;
|
|
|
1446 |
}
|
|
|
1447 |
|
|
|
1448 |
if (!is_null($ilast) && ($ilast === '' || strpos(get_string('alphabet', 'langconfig'), $ilast) !== false)) {
|
|
|
1449 |
$this->prefs['i_last'] = $ilast;
|
|
|
1450 |
}
|
|
|
1451 |
|
|
|
1452 |
}
|
|
|
1453 |
|
|
|
1454 |
/**
|
|
|
1455 |
* Set hide and show preferences.
|
|
|
1456 |
*/
|
|
|
1457 |
protected function set_hide_show_preferences(): void {
|
|
|
1458 |
|
|
|
1459 |
if ($this->hiddencolumns !== null) {
|
|
|
1460 |
$this->prefs['collapse'] = array_fill_keys(array_filter($this->hiddencolumns, function($column) {
|
|
|
1461 |
return array_key_exists($column, $this->columns);
|
|
|
1462 |
}), true);
|
|
|
1463 |
} else {
|
|
|
1464 |
if ($column = optional_param($this->request[TABLE_VAR_HIDE], '', PARAM_ALPHANUMEXT)) {
|
|
|
1465 |
if (isset($this->columns[$column])) {
|
|
|
1466 |
$this->prefs['collapse'][$column] = true;
|
|
|
1467 |
}
|
|
|
1468 |
}
|
|
|
1469 |
}
|
|
|
1470 |
|
|
|
1471 |
if ($column = optional_param($this->request[TABLE_VAR_SHOW], '', PARAM_ALPHANUMEXT)) {
|
|
|
1472 |
unset($this->prefs['collapse'][$column]);
|
|
|
1473 |
}
|
|
|
1474 |
|
|
|
1475 |
foreach (array_keys($this->prefs['collapse']) as $column) {
|
|
|
1476 |
if (array_key_exists($column, $this->prefs['sortby'])) {
|
|
|
1477 |
unset($this->prefs['sortby'][$column]);
|
|
|
1478 |
}
|
|
|
1479 |
}
|
|
|
1480 |
}
|
|
|
1481 |
|
|
|
1482 |
/**
|
|
|
1483 |
* Set the list of hidden columns.
|
|
|
1484 |
*
|
|
|
1485 |
* @param array $columns The list of hidden columns.
|
|
|
1486 |
*/
|
|
|
1487 |
public function set_hidden_columns(array $columns): void {
|
|
|
1488 |
$this->hiddencolumns = $columns;
|
|
|
1489 |
}
|
|
|
1490 |
|
|
|
1491 |
/**
|
|
|
1492 |
* Initialise table preferences.
|
|
|
1493 |
*/
|
|
|
1494 |
protected function initialise_table_preferences(): void {
|
|
|
1495 |
global $SESSION;
|
|
|
1496 |
|
|
|
1497 |
// Load any existing user preferences.
|
|
|
1498 |
if ($this->persistent) {
|
|
|
1499 |
$this->prefs = json_decode(get_user_preferences("flextable_{$this->uniqueid}", ''), true);
|
|
|
1500 |
$oldprefs = $this->prefs;
|
|
|
1501 |
} else if (isset($SESSION->flextable[$this->uniqueid])) {
|
|
|
1502 |
$this->prefs = $SESSION->flextable[$this->uniqueid];
|
|
|
1503 |
$oldprefs = $this->prefs;
|
|
|
1504 |
}
|
|
|
1505 |
|
|
|
1506 |
// Set up default preferences if needed.
|
|
|
1507 |
if (!$this->prefs || $this->is_resetting_preferences()) {
|
|
|
1508 |
$this->prefs = [
|
|
|
1509 |
'collapse' => [],
|
|
|
1510 |
'sortby' => [],
|
|
|
1511 |
'i_first' => '',
|
|
|
1512 |
'i_last' => '',
|
|
|
1513 |
'textsort' => $this->column_textsort,
|
|
|
1514 |
];
|
|
|
1515 |
}
|
|
|
1516 |
|
|
|
1517 |
if (!isset($oldprefs)) {
|
|
|
1518 |
$oldprefs = $this->prefs;
|
|
|
1519 |
}
|
|
|
1520 |
|
|
|
1521 |
// Save user preferences if they have changed.
|
|
|
1522 |
if ($this->is_resetting_preferences()) {
|
|
|
1523 |
$this->sortdata = null;
|
|
|
1524 |
$this->ifirst = null;
|
|
|
1525 |
$this->ilast = null;
|
|
|
1526 |
}
|
|
|
1527 |
|
|
|
1528 |
if (($showcol = optional_param($this->request[TABLE_VAR_SHOW], '', PARAM_ALPHANUMEXT)) &&
|
|
|
1529 |
isset($this->columns[$showcol])) {
|
|
|
1530 |
$this->prefs['collapse'][$showcol] = false;
|
|
|
1531 |
} else if (($hidecol = optional_param($this->request[TABLE_VAR_HIDE], '', PARAM_ALPHANUMEXT)) &&
|
|
|
1532 |
isset($this->columns[$hidecol])) {
|
|
|
1533 |
$this->prefs['collapse'][$hidecol] = true;
|
|
|
1534 |
if (array_key_exists($hidecol, $this->prefs['sortby'])) {
|
|
|
1535 |
unset($this->prefs['sortby'][$hidecol]);
|
|
|
1536 |
}
|
|
|
1537 |
}
|
|
|
1538 |
|
|
|
1539 |
$this->set_hide_show_preferences();
|
|
|
1540 |
$this->set_sorting_preferences();
|
|
|
1541 |
$this->set_initials_preferences();
|
|
|
1542 |
|
|
|
1543 |
// Now, reduce the width of collapsed columns and remove the width from columns that should be expanded.
|
|
|
1544 |
foreach (array_keys($this->columns) as $column) {
|
|
|
1545 |
if (!empty($this->prefs['collapse'][$column])) {
|
|
|
1546 |
$this->column_style[$column]['width'] = '10px';
|
|
|
1547 |
} else {
|
|
|
1548 |
unset($this->column_style[$column]['width']);
|
|
|
1549 |
}
|
|
|
1550 |
}
|
|
|
1551 |
|
|
|
1552 |
if (empty($this->baseurl)) {
|
|
|
1553 |
debugging('You should set baseurl when using flexible_table.');
|
|
|
1554 |
global $PAGE;
|
|
|
1555 |
$this->baseurl = $PAGE->url;
|
|
|
1556 |
}
|
|
|
1557 |
|
|
|
1558 |
if ($this->currpage == null) {
|
|
|
1559 |
$this->currpage = optional_param($this->request[TABLE_VAR_PAGE], 0, PARAM_INT);
|
|
|
1560 |
}
|
|
|
1561 |
|
|
|
1562 |
$this->save_preferences($oldprefs);
|
|
|
1563 |
}
|
|
|
1564 |
|
|
|
1565 |
/**
|
|
|
1566 |
* Save preferences.
|
|
|
1567 |
*
|
|
|
1568 |
* @param array $oldprefs Old preferences to compare against.
|
|
|
1569 |
*/
|
|
|
1570 |
protected function save_preferences($oldprefs): void {
|
|
|
1571 |
global $SESSION;
|
|
|
1572 |
|
|
|
1573 |
if ($this->prefs != $oldprefs) {
|
|
|
1574 |
if ($this->persistent) {
|
|
|
1575 |
set_user_preference('flextable_' . $this->uniqueid, json_encode($this->prefs));
|
|
|
1576 |
} else {
|
|
|
1577 |
$SESSION->flextable[$this->uniqueid] = $this->prefs;
|
|
|
1578 |
}
|
|
|
1579 |
}
|
|
|
1580 |
unset($oldprefs);
|
|
|
1581 |
}
|
|
|
1582 |
|
|
|
1583 |
/**
|
|
|
1584 |
* Set the preferred table sorting attributes.
|
|
|
1585 |
*
|
|
|
1586 |
* @param string $sortby The field to sort by.
|
|
|
1587 |
* @param int $sortorder The sort order.
|
|
|
1588 |
*/
|
|
|
1589 |
public function set_sortdata(array $sortdata): void {
|
|
|
1590 |
$this->sortdata = [];
|
|
|
1591 |
foreach ($sortdata as $sortitem) {
|
|
|
1592 |
if (!array_key_exists($sortitem['sortby'], $this->sortdata)) {
|
|
|
1593 |
$this->sortdata[$sortitem['sortby']] = (int) $sortitem['sortorder'];
|
|
|
1594 |
}
|
|
|
1595 |
}
|
|
|
1596 |
}
|
|
|
1597 |
|
|
|
1598 |
/**
|
|
|
1599 |
* Get the default per page.
|
|
|
1600 |
*
|
|
|
1601 |
* @return int
|
|
|
1602 |
*/
|
|
|
1603 |
public function get_default_per_page(): int {
|
|
|
1604 |
return $this->defaultperpage;
|
|
|
1605 |
}
|
|
|
1606 |
|
|
|
1607 |
/**
|
|
|
1608 |
* Set the default per page.
|
|
|
1609 |
*
|
|
|
1610 |
* @param int $defaultperpage
|
|
|
1611 |
*/
|
|
|
1612 |
public function set_default_per_page(int $defaultperpage): void {
|
|
|
1613 |
$this->defaultperpage = $defaultperpage;
|
|
|
1614 |
}
|
|
|
1615 |
|
|
|
1616 |
/**
|
|
|
1617 |
* Set the preferred first name initial in an initials bar.
|
|
|
1618 |
*
|
|
|
1619 |
* @param string $initial The character to set
|
|
|
1620 |
*/
|
|
|
1621 |
public function set_first_initial(string $initial): void {
|
|
|
1622 |
$this->ifirst = $initial;
|
|
|
1623 |
}
|
|
|
1624 |
|
|
|
1625 |
/**
|
|
|
1626 |
* Set the preferred last name initial in an initials bar.
|
|
|
1627 |
*
|
|
|
1628 |
* @param string $initial The character to set
|
|
|
1629 |
*/
|
|
|
1630 |
public function set_last_initial(string $initial): void {
|
|
|
1631 |
$this->ilast = $initial;
|
|
|
1632 |
}
|
|
|
1633 |
|
|
|
1634 |
/**
|
|
|
1635 |
* Set the page number.
|
|
|
1636 |
*
|
|
|
1637 |
* @param int $pagenumber The page number.
|
|
|
1638 |
*/
|
|
|
1639 |
public function set_page_number(int $pagenumber): void {
|
|
|
1640 |
$this->currpage = $pagenumber - 1;
|
|
|
1641 |
}
|
|
|
1642 |
|
|
|
1643 |
/**
|
|
|
1644 |
* Generate the HTML for the sort icon. This is a helper method used by {@link sort_link()}.
|
|
|
1645 |
* @param bool $isprimary whether an icon is needed (it is only needed for the primary sort column.)
|
|
|
1646 |
* @param int $order SORT_ASC or SORT_DESC
|
|
|
1647 |
* @return string HTML fragment.
|
|
|
1648 |
*/
|
|
|
1649 |
protected function sort_icon($isprimary, $order) {
|
|
|
1650 |
global $OUTPUT;
|
|
|
1651 |
|
|
|
1652 |
if (!$isprimary) {
|
|
|
1653 |
return '';
|
|
|
1654 |
}
|
|
|
1655 |
|
|
|
1656 |
if ($order == SORT_ASC) {
|
|
|
1657 |
return $OUTPUT->pix_icon('t/sort_asc', get_string('asc'));
|
|
|
1658 |
} else {
|
|
|
1659 |
return $OUTPUT->pix_icon('t/sort_desc', get_string('desc'));
|
|
|
1660 |
}
|
|
|
1661 |
}
|
|
|
1662 |
|
|
|
1663 |
/**
|
|
|
1664 |
* Generate the correct tool tip for changing the sort order. This is a
|
|
|
1665 |
* helper method used by {@link sort_link()}.
|
|
|
1666 |
* @param bool $isprimary whether the is column is the current primary sort column.
|
|
|
1667 |
* @param int $order SORT_ASC or SORT_DESC
|
|
|
1668 |
* @return string the correct title.
|
|
|
1669 |
*/
|
|
|
1670 |
protected function sort_order_name($isprimary, $order) {
|
|
|
1671 |
if ($isprimary && $order != SORT_ASC) {
|
|
|
1672 |
return get_string('desc');
|
|
|
1673 |
} else {
|
|
|
1674 |
return get_string('asc');
|
|
|
1675 |
}
|
|
|
1676 |
}
|
|
|
1677 |
|
|
|
1678 |
/**
|
|
|
1679 |
* Generate the HTML for the sort link. This is a helper method used by {@link print_headers()}.
|
|
|
1680 |
* @param string $text the text for the link.
|
|
|
1681 |
* @param string $column the column name, may be a fake column like 'firstname' or a real one.
|
|
|
1682 |
* @param bool $isprimary whether the is column is the current primary sort column.
|
|
|
1683 |
* @param int $order SORT_ASC or SORT_DESC
|
|
|
1684 |
* @return string HTML fragment.
|
|
|
1685 |
*/
|
|
|
1686 |
protected function sort_link($text, $column, $isprimary, $order) {
|
|
|
1687 |
// If we are already sorting by this column, switch direction.
|
|
|
1688 |
if (array_key_exists($column, $this->prefs['sortby'])) {
|
|
|
1689 |
$sortorder = $this->prefs['sortby'][$column] == SORT_ASC ? SORT_DESC : SORT_ASC;
|
|
|
1690 |
} else {
|
|
|
1691 |
$sortorder = $order;
|
|
|
1692 |
}
|
|
|
1693 |
|
|
|
1694 |
$params = [
|
|
|
1695 |
$this->request[TABLE_VAR_SORT] => $column,
|
|
|
1696 |
$this->request[TABLE_VAR_DIR] => $sortorder,
|
|
|
1697 |
];
|
|
|
1698 |
|
|
|
1699 |
return html_writer::link($this->baseurl->out(false, $params),
|
|
|
1700 |
$text . get_accesshide(get_string('sortby') . ' ' .
|
|
|
1701 |
$text . ' ' . $this->sort_order_name($isprimary, $order)),
|
|
|
1702 |
[
|
|
|
1703 |
'data-sortable' => $this->is_sortable($column),
|
|
|
1704 |
'data-sortby' => $column,
|
|
|
1705 |
'data-sortorder' => $sortorder,
|
|
|
1706 |
'role' => 'button',
|
|
|
1707 |
]) . ' ' . $this->sort_icon($isprimary, $order);
|
|
|
1708 |
}
|
|
|
1709 |
|
|
|
1710 |
/**
|
|
|
1711 |
* Return primary sorting column/order, either the first preferred "sortby" value or defaults defined for the table
|
|
|
1712 |
*
|
|
|
1713 |
* @return array
|
|
|
1714 |
*/
|
|
|
1715 |
protected function get_primary_sort_order(): array {
|
|
|
1716 |
if (reset($this->prefs['sortby'])) {
|
|
|
1717 |
return $this->get_sort_order();
|
|
|
1718 |
}
|
|
|
1719 |
|
|
|
1720 |
return [
|
|
|
1721 |
'sortby' => $this->sort_default_column,
|
|
|
1722 |
'sortorder' => $this->sort_default_order,
|
|
|
1723 |
];
|
|
|
1724 |
}
|
|
|
1725 |
|
|
|
1726 |
/**
|
|
|
1727 |
* Return sorting attributes values.
|
|
|
1728 |
*
|
|
|
1729 |
* @return array
|
|
|
1730 |
*/
|
|
|
1731 |
protected function get_sort_order(): array {
|
|
|
1732 |
$sortbys = $this->prefs['sortby'];
|
|
|
1733 |
$sortby = key($sortbys);
|
|
|
1734 |
|
|
|
1735 |
return [
|
|
|
1736 |
'sortby' => $sortby,
|
|
|
1737 |
'sortorder' => $sortbys[$sortby],
|
|
|
1738 |
];
|
|
|
1739 |
}
|
|
|
1740 |
|
|
|
1741 |
/**
|
|
|
1742 |
* Get dynamic class component.
|
|
|
1743 |
*
|
|
|
1744 |
* @return string
|
|
|
1745 |
*/
|
|
|
1746 |
protected function get_component() {
|
|
|
1747 |
$tableclass = explode("\\", get_class($this));
|
|
|
1748 |
return reset($tableclass);
|
|
|
1749 |
}
|
|
|
1750 |
|
|
|
1751 |
/**
|
|
|
1752 |
* Get dynamic class handler.
|
|
|
1753 |
*
|
|
|
1754 |
* @return string
|
|
|
1755 |
*/
|
|
|
1756 |
protected function get_handler() {
|
|
|
1757 |
$tableclass = explode("\\", get_class($this));
|
|
|
1758 |
return end($tableclass);
|
|
|
1759 |
}
|
|
|
1760 |
|
|
|
1761 |
/**
|
|
|
1762 |
* Get the dynamic table start wrapper.
|
|
|
1763 |
* If this is not a dynamic table, then an empty string is returned making this safe to blindly call.
|
|
|
1764 |
*
|
|
|
1765 |
* @return string
|
|
|
1766 |
*/
|
|
|
1767 |
protected function get_dynamic_table_html_start(): string {
|
|
|
1768 |
if (is_a($this, \core_table\dynamic::class)) {
|
|
|
1769 |
$sortdata = array_map(function($sortby, $sortorder) {
|
|
|
1770 |
return [
|
|
|
1771 |
'sortby' => $sortby,
|
|
|
1772 |
'sortorder' => $sortorder,
|
|
|
1773 |
];
|
|
|
1774 |
}, array_keys($this->prefs['sortby']), array_values($this->prefs['sortby']));;
|
|
|
1775 |
|
|
|
1776 |
return html_writer::start_tag('div', [
|
|
|
1777 |
'class' => 'table-dynamic position-relative',
|
|
|
1778 |
'data-region' => 'core_table/dynamic',
|
|
|
1779 |
'data-table-handler' => $this->get_handler(),
|
|
|
1780 |
'data-table-component' => $this->get_component(),
|
|
|
1781 |
'data-table-uniqueid' => $this->uniqueid,
|
|
|
1782 |
'data-table-filters' => json_encode($this->get_filterset()),
|
|
|
1783 |
'data-table-sort-data' => json_encode($sortdata),
|
|
|
1784 |
'data-table-first-initial' => $this->prefs['i_first'],
|
|
|
1785 |
'data-table-last-initial' => $this->prefs['i_last'],
|
|
|
1786 |
'data-table-page-number' => $this->currpage + 1,
|
|
|
1787 |
'data-table-page-size' => $this->pagesize,
|
|
|
1788 |
'data-table-default-per-page' => $this->get_default_per_page(),
|
|
|
1789 |
'data-table-hidden-columns' => json_encode(array_keys($this->prefs['collapse'])),
|
|
|
1790 |
'data-table-total-rows' => $this->totalrows,
|
|
|
1791 |
]);
|
|
|
1792 |
}
|
|
|
1793 |
|
|
|
1794 |
return '';
|
|
|
1795 |
}
|
|
|
1796 |
|
|
|
1797 |
/**
|
|
|
1798 |
* Get the dynamic table end wrapper.
|
|
|
1799 |
* If this is not a dynamic table, then an empty string is returned making this safe to blindly call.
|
|
|
1800 |
*
|
|
|
1801 |
* @return string
|
|
|
1802 |
*/
|
|
|
1803 |
protected function get_dynamic_table_html_end(): string {
|
|
|
1804 |
global $PAGE;
|
|
|
1805 |
|
|
|
1806 |
if (is_a($this, \core_table\dynamic::class)) {
|
|
|
1807 |
$output = '';
|
|
|
1808 |
|
|
|
1809 |
$perpageurl = new moodle_url($PAGE->url);
|
|
|
1810 |
|
|
|
1811 |
// Generate "Show all/Show per page" link.
|
|
|
1812 |
if ($this->pagesize == TABLE_SHOW_ALL_PAGE_SIZE && $this->totalrows > $this->get_default_per_page()) {
|
|
|
1813 |
$perpagesize = $this->get_default_per_page();
|
|
|
1814 |
$perpagestring = get_string('showperpage', '', $this->get_default_per_page());
|
|
|
1815 |
} else if ($this->pagesize < $this->totalrows) {
|
|
|
1816 |
$perpagesize = TABLE_SHOW_ALL_PAGE_SIZE;
|
|
|
1817 |
$perpagestring = get_string('showall', '', $this->totalrows);
|
|
|
1818 |
}
|
|
|
1819 |
if (isset($perpagesize) && isset($perpagestring)) {
|
|
|
1820 |
$perpageurl->param('perpage', $perpagesize);
|
|
|
1821 |
$output .= html_writer::link(
|
|
|
1822 |
$perpageurl,
|
|
|
1823 |
$perpagestring,
|
|
|
1824 |
[
|
|
|
1825 |
'data-action' => 'showcount',
|
|
|
1826 |
'data-target-page-size' => $perpagesize,
|
|
|
1827 |
]
|
|
|
1828 |
);
|
|
|
1829 |
}
|
|
|
1830 |
|
|
|
1831 |
$PAGE->requires->js_call_amd('core_table/dynamic', 'init');
|
|
|
1832 |
$output .= html_writer::end_tag('div');
|
|
|
1833 |
return $output;
|
|
|
1834 |
}
|
|
|
1835 |
|
|
|
1836 |
return '';
|
|
|
1837 |
}
|
|
|
1838 |
|
|
|
1839 |
/**
|
|
|
1840 |
* This function is not part of the public api.
|
|
|
1841 |
*/
|
|
|
1842 |
function start_html() {
|
|
|
1843 |
global $OUTPUT;
|
|
|
1844 |
|
|
|
1845 |
// Render the dynamic table header.
|
|
|
1846 |
echo $this->get_dynamic_table_html_start();
|
|
|
1847 |
|
|
|
1848 |
// Render button to allow user to reset table preferences.
|
|
|
1849 |
echo $this->render_reset_button();
|
|
|
1850 |
|
|
|
1851 |
// Do we need to print initial bars?
|
|
|
1852 |
$this->print_initials_bar();
|
|
|
1853 |
|
|
|
1854 |
// Paging bar
|
|
|
1855 |
if ($this->use_pages) {
|
|
|
1856 |
$pagingbar = new paging_bar($this->totalrows, $this->currpage, $this->pagesize, $this->baseurl);
|
|
|
1857 |
$pagingbar->pagevar = $this->request[TABLE_VAR_PAGE];
|
|
|
1858 |
echo $OUTPUT->render($pagingbar);
|
|
|
1859 |
}
|
|
|
1860 |
|
|
|
1861 |
if (in_array(TABLE_P_TOP, $this->showdownloadbuttonsat)) {
|
|
|
1862 |
echo $this->download_buttons();
|
|
|
1863 |
}
|
|
|
1864 |
|
|
|
1865 |
$this->wrap_html_start();
|
|
|
1866 |
// Start of main data table
|
|
|
1867 |
|
|
|
1868 |
echo html_writer::start_tag('div', array('class' => 'no-overflow'));
|
|
|
1869 |
echo html_writer::start_tag('table', $this->attributes) . $this->render_caption();
|
|
|
1870 |
}
|
|
|
1871 |
|
|
|
1872 |
/**
|
|
|
1873 |
* This function set caption for table.
|
|
|
1874 |
*
|
|
|
1875 |
* @param string $caption Caption of table.
|
|
|
1876 |
* @param array|null $captionattributes Caption attributes of table.
|
|
|
1877 |
*/
|
|
|
1878 |
public function set_caption(string $caption, ?array $captionattributes): void {
|
|
|
1879 |
$this->caption = $caption;
|
|
|
1880 |
$this->captionattributes = $captionattributes;
|
|
|
1881 |
}
|
|
|
1882 |
|
|
|
1883 |
/**
|
|
|
1884 |
* This function renders a table caption.
|
|
|
1885 |
*
|
|
|
1886 |
* @return string $output Caption of table.
|
|
|
1887 |
*/
|
|
|
1888 |
public function render_caption(): string {
|
|
|
1889 |
if ($this->caption === null) {
|
|
|
1890 |
return '';
|
|
|
1891 |
}
|
|
|
1892 |
|
|
|
1893 |
return html_writer::tag(
|
|
|
1894 |
'caption',
|
|
|
1895 |
$this->caption,
|
|
|
1896 |
$this->captionattributes,
|
|
|
1897 |
);
|
|
|
1898 |
}
|
|
|
1899 |
|
|
|
1900 |
/**
|
|
|
1901 |
* This function is not part of the public api.
|
|
|
1902 |
* @param array $styles CSS-property => value
|
|
|
1903 |
* @return string values suitably to go in a style="" attribute in HTML.
|
|
|
1904 |
*/
|
|
|
1905 |
function make_styles_string($styles) {
|
|
|
1906 |
if (empty($styles)) {
|
|
|
1907 |
return null;
|
|
|
1908 |
}
|
|
|
1909 |
|
|
|
1910 |
$string = '';
|
|
|
1911 |
foreach($styles as $property => $value) {
|
|
|
1912 |
$string .= $property . ':' . $value . ';';
|
|
|
1913 |
}
|
|
|
1914 |
return $string;
|
|
|
1915 |
}
|
|
|
1916 |
|
|
|
1917 |
/**
|
|
|
1918 |
* Generate the HTML for the table preferences reset button.
|
|
|
1919 |
*
|
|
|
1920 |
* @return string HTML fragment, empty string if no need to reset
|
|
|
1921 |
*/
|
|
|
1922 |
protected function render_reset_button() {
|
|
|
1923 |
|
|
|
1924 |
if (!$this->can_be_reset()) {
|
|
|
1925 |
return '';
|
|
|
1926 |
}
|
|
|
1927 |
|
|
|
1928 |
$url = $this->baseurl->out(false, array($this->request[TABLE_VAR_RESET] => 1));
|
|
|
1929 |
|
|
|
1930 |
$html = html_writer::start_div('resettable mdl-right');
|
|
|
1931 |
$html .= html_writer::link($url, get_string('resettable'), ['role' => 'button']);
|
|
|
1932 |
$html .= html_writer::end_div();
|
|
|
1933 |
|
|
|
1934 |
return $html;
|
|
|
1935 |
}
|
|
|
1936 |
|
|
|
1937 |
/**
|
|
|
1938 |
* Are there some table preferences that can be reset?
|
|
|
1939 |
*
|
|
|
1940 |
* If true, then the "reset table preferences" widget should be displayed.
|
|
|
1941 |
*
|
|
|
1942 |
* @return bool
|
|
|
1943 |
*/
|
|
|
1944 |
protected function can_be_reset() {
|
|
|
1945 |
// Loop through preferences and make sure they are empty or set to the default value.
|
|
|
1946 |
foreach ($this->prefs as $prefname => $prefval) {
|
|
|
1947 |
if ($prefname === 'sortby' and !empty($this->sort_default_column)) {
|
|
|
1948 |
// Check if the actual sorting differs from the default one.
|
|
|
1949 |
if (empty($prefval) or $prefval !== array($this->sort_default_column => $this->sort_default_order)) {
|
|
|
1950 |
return true;
|
|
|
1951 |
}
|
|
|
1952 |
|
|
|
1953 |
} else if ($prefname === 'collapse' and !empty($prefval)) {
|
|
|
1954 |
// Check if there are some collapsed columns (all are expanded by default).
|
|
|
1955 |
foreach ($prefval as $columnname => $iscollapsed) {
|
|
|
1956 |
if ($iscollapsed) {
|
|
|
1957 |
return true;
|
|
|
1958 |
}
|
|
|
1959 |
}
|
|
|
1960 |
|
|
|
1961 |
} else if (!empty($prefval)) {
|
|
|
1962 |
// For all other cases, we just check if some preference is set.
|
|
|
1963 |
return true;
|
|
|
1964 |
}
|
|
|
1965 |
}
|
|
|
1966 |
|
|
|
1967 |
return false;
|
|
|
1968 |
}
|
|
|
1969 |
|
|
|
1970 |
/**
|
|
|
1971 |
* Get the context for the table.
|
|
|
1972 |
*
|
|
|
1973 |
* Note: This function _must_ be overridden by dynamic tables to ensure that the context is correctly determined
|
|
|
1974 |
* from the filterset parameters.
|
|
|
1975 |
*
|
|
|
1976 |
* @return context
|
|
|
1977 |
*/
|
|
|
1978 |
public function get_context(): context {
|
|
|
1979 |
global $PAGE;
|
|
|
1980 |
|
|
|
1981 |
if (is_a($this, \core_table\dynamic::class)) {
|
|
|
1982 |
throw new coding_exception('The get_context function must be defined for a dynamic table');
|
|
|
1983 |
}
|
|
|
1984 |
|
|
|
1985 |
return $PAGE->context;
|
|
|
1986 |
}
|
|
|
1987 |
|
|
|
1988 |
/**
|
|
|
1989 |
* Set the filterset in the table class.
|
|
|
1990 |
*
|
|
|
1991 |
* The use of filtersets is a requirement for dynamic tables, but can be used by other tables too if desired.
|
|
|
1992 |
*
|
|
|
1993 |
* @param filterset $filterset The filterset object to get filters and table parameters from
|
|
|
1994 |
*/
|
|
|
1995 |
public function set_filterset(filterset $filterset): void {
|
|
|
1996 |
$this->filterset = $filterset;
|
|
|
1997 |
|
|
|
1998 |
$this->guess_base_url();
|
|
|
1999 |
}
|
|
|
2000 |
|
|
|
2001 |
/**
|
|
|
2002 |
* Get the currently defined filterset.
|
|
|
2003 |
*
|
|
|
2004 |
* @return filterset
|
|
|
2005 |
*/
|
|
|
2006 |
public function get_filterset(): ?filterset {
|
|
|
2007 |
return $this->filterset;
|
|
|
2008 |
}
|
|
|
2009 |
|
|
|
2010 |
/**
|
|
|
2011 |
* Get the class used as a filterset.
|
|
|
2012 |
*
|
|
|
2013 |
* @return string
|
|
|
2014 |
*/
|
|
|
2015 |
public static function get_filterset_class(): string {
|
|
|
2016 |
return static::class . '_filterset';
|
|
|
2017 |
}
|
|
|
2018 |
|
|
|
2019 |
/**
|
|
|
2020 |
* Attempt to guess the base URL.
|
|
|
2021 |
*/
|
|
|
2022 |
public function guess_base_url(): void {
|
|
|
2023 |
if (is_a($this, \core_table\dynamic::class)) {
|
|
|
2024 |
throw new coding_exception('The guess_base_url function must be defined for a dynamic table');
|
|
|
2025 |
}
|
|
|
2026 |
}
|
|
|
2027 |
}
|
|
|
2028 |
|
|
|
2029 |
|
|
|
2030 |
/**
|
|
|
2031 |
* @package moodlecore
|
|
|
2032 |
* @copyright 1999 onwards Martin Dougiamas {@link http://moodle.com}
|
|
|
2033 |
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
|
|
2034 |
*/
|
|
|
2035 |
class table_sql extends flexible_table {
|
|
|
2036 |
|
|
|
2037 |
public $countsql = NULL;
|
|
|
2038 |
public $countparams = NULL;
|
|
|
2039 |
/**
|
|
|
2040 |
* @var object sql for querying db. Has fields 'fields', 'from', 'where', 'params'.
|
|
|
2041 |
*/
|
|
|
2042 |
public $sql = NULL;
|
|
|
2043 |
/**
|
|
|
2044 |
* @var array|\Traversable Data fetched from the db.
|
|
|
2045 |
*/
|
|
|
2046 |
public $rawdata = NULL;
|
|
|
2047 |
|
|
|
2048 |
/**
|
|
|
2049 |
* @var bool Overriding default for this.
|
|
|
2050 |
*/
|
|
|
2051 |
public $is_sortable = true;
|
|
|
2052 |
/**
|
|
|
2053 |
* @var bool Overriding default for this.
|
|
|
2054 |
*/
|
|
|
2055 |
public $is_collapsible = true;
|
|
|
2056 |
|
|
|
2057 |
/**
|
|
|
2058 |
* @param string $uniqueid a string identifying this table.Used as a key in
|
|
|
2059 |
* session vars.
|
|
|
2060 |
*/
|
|
|
2061 |
function __construct($uniqueid) {
|
|
|
2062 |
parent::__construct($uniqueid);
|
|
|
2063 |
// some sensible defaults
|
|
|
2064 |
$this->set_attribute('class', 'generaltable generalbox');
|
|
|
2065 |
}
|
|
|
2066 |
|
|
|
2067 |
/**
|
|
|
2068 |
* Build the table from the fetched data.
|
|
|
2069 |
*
|
|
|
2070 |
* Take the data returned from the db_query and go through all the rows
|
|
|
2071 |
* processing each col using either col_{columnname} method or other_cols
|
|
|
2072 |
* method or if other_cols returns NULL then put the data straight into the
|
|
|
2073 |
* table.
|
|
|
2074 |
*
|
|
|
2075 |
* After calling this function, don't forget to call close_recordset.
|
|
|
2076 |
*/
|
|
|
2077 |
public function build_table() {
|
|
|
2078 |
if (!$this->rawdata) {
|
|
|
2079 |
return;
|
|
|
2080 |
}
|
|
|
2081 |
|
|
|
2082 |
foreach ($this->rawdata as $row) {
|
|
|
2083 |
$formattedrow = $this->format_row($row);
|
|
|
2084 |
$this->add_data_keyed($formattedrow, $this->get_row_class($row));
|
|
|
2085 |
}
|
|
|
2086 |
}
|
|
|
2087 |
|
|
|
2088 |
/**
|
|
|
2089 |
* Closes recordset (for use after building the table).
|
|
|
2090 |
*/
|
|
|
2091 |
public function close_recordset() {
|
|
|
2092 |
if ($this->rawdata && ($this->rawdata instanceof \core\dml\recordset_walk ||
|
|
|
2093 |
$this->rawdata instanceof moodle_recordset)) {
|
|
|
2094 |
$this->rawdata->close();
|
|
|
2095 |
$this->rawdata = null;
|
|
|
2096 |
}
|
|
|
2097 |
}
|
|
|
2098 |
|
|
|
2099 |
/**
|
|
|
2100 |
* Get any extra classes names to add to this row in the HTML.
|
|
|
2101 |
* @param $row array the data for this row.
|
|
|
2102 |
* @return string added to the class="" attribute of the tr.
|
|
|
2103 |
*/
|
|
|
2104 |
function get_row_class($row) {
|
|
|
2105 |
return '';
|
|
|
2106 |
}
|
|
|
2107 |
|
|
|
2108 |
/**
|
|
|
2109 |
* This is only needed if you want to use different sql to count rows.
|
|
|
2110 |
* Used for example when perhaps all db JOINS are not needed when counting
|
|
|
2111 |
* records. You don't need to call this function the count_sql
|
|
|
2112 |
* will be generated automatically.
|
|
|
2113 |
*
|
|
|
2114 |
* We need to count rows returned by the db seperately to the query itself
|
|
|
2115 |
* as we need to know how many pages of data we have to display.
|
|
|
2116 |
*/
|
|
|
2117 |
function set_count_sql($sql, array $params = NULL) {
|
|
|
2118 |
$this->countsql = $sql;
|
|
|
2119 |
$this->countparams = $params;
|
|
|
2120 |
}
|
|
|
2121 |
|
|
|
2122 |
/**
|
|
|
2123 |
* Set the sql to query the db. Query will be :
|
|
|
2124 |
* SELECT $fields FROM $from WHERE $where
|
|
|
2125 |
* Of course you can use sub-queries, JOINS etc. by putting them in the
|
|
|
2126 |
* appropriate clause of the query.
|
|
|
2127 |
*/
|
|
|
2128 |
function set_sql($fields, $from, $where, array $params = array()) {
|
|
|
2129 |
$this->sql = new stdClass();
|
|
|
2130 |
$this->sql->fields = $fields;
|
|
|
2131 |
$this->sql->from = $from;
|
|
|
2132 |
$this->sql->where = $where;
|
|
|
2133 |
$this->sql->params = $params;
|
|
|
2134 |
}
|
|
|
2135 |
|
|
|
2136 |
/**
|
|
|
2137 |
* Query the db. Store results in the table object for use by build_table.
|
|
|
2138 |
*
|
|
|
2139 |
* @param int $pagesize size of page for paginated displayed table.
|
|
|
2140 |
* @param bool $useinitialsbar do you want to use the initials bar. Bar
|
|
|
2141 |
* will only be used if there is a fullname column defined for the table.
|
|
|
2142 |
*/
|
|
|
2143 |
function query_db($pagesize, $useinitialsbar=true) {
|
|
|
2144 |
global $DB;
|
|
|
2145 |
if (!$this->is_downloading()) {
|
|
|
2146 |
if ($this->countsql === NULL) {
|
|
|
2147 |
$this->countsql = 'SELECT COUNT(1) FROM '.$this->sql->from.' WHERE '.$this->sql->where;
|
|
|
2148 |
$this->countparams = $this->sql->params;
|
|
|
2149 |
}
|
|
|
2150 |
$grandtotal = $DB->count_records_sql($this->countsql, $this->countparams);
|
|
|
2151 |
if ($useinitialsbar && !$this->is_downloading()) {
|
|
|
2152 |
$this->initialbars(true);
|
|
|
2153 |
}
|
|
|
2154 |
|
|
|
2155 |
list($wsql, $wparams) = $this->get_sql_where();
|
|
|
2156 |
if ($wsql) {
|
|
|
2157 |
$this->countsql .= ' AND '.$wsql;
|
|
|
2158 |
$this->countparams = array_merge($this->countparams, $wparams);
|
|
|
2159 |
|
|
|
2160 |
$this->sql->where .= ' AND '.$wsql;
|
|
|
2161 |
$this->sql->params = array_merge($this->sql->params, $wparams);
|
|
|
2162 |
|
|
|
2163 |
$total = $DB->count_records_sql($this->countsql, $this->countparams);
|
|
|
2164 |
} else {
|
|
|
2165 |
$total = $grandtotal;
|
|
|
2166 |
}
|
|
|
2167 |
|
|
|
2168 |
$this->pagesize($pagesize, $total);
|
|
|
2169 |
}
|
|
|
2170 |
|
|
|
2171 |
// Fetch the attempts
|
|
|
2172 |
$sort = $this->get_sql_sort();
|
|
|
2173 |
if ($sort) {
|
|
|
2174 |
$sort = "ORDER BY $sort";
|
|
|
2175 |
}
|
|
|
2176 |
$sql = "SELECT
|
|
|
2177 |
{$this->sql->fields}
|
|
|
2178 |
FROM {$this->sql->from}
|
|
|
2179 |
WHERE {$this->sql->where}
|
|
|
2180 |
{$sort}";
|
|
|
2181 |
|
|
|
2182 |
if (!$this->is_downloading()) {
|
|
|
2183 |
$this->rawdata = $DB->get_records_sql($sql, $this->sql->params, $this->get_page_start(), $this->get_page_size());
|
|
|
2184 |
} else {
|
|
|
2185 |
$this->rawdata = $DB->get_records_sql($sql, $this->sql->params);
|
|
|
2186 |
}
|
|
|
2187 |
}
|
|
|
2188 |
|
|
|
2189 |
/**
|
|
|
2190 |
* Convenience method to call a number of methods for you to display the
|
|
|
2191 |
* table.
|
|
|
2192 |
*/
|
|
|
2193 |
function out($pagesize, $useinitialsbar, $downloadhelpbutton='') {
|
|
|
2194 |
global $DB;
|
|
|
2195 |
if (!$this->columns) {
|
|
|
2196 |
$onerow = $DB->get_record_sql("SELECT {$this->sql->fields} FROM {$this->sql->from} WHERE {$this->sql->where}",
|
|
|
2197 |
$this->sql->params, IGNORE_MULTIPLE);
|
|
|
2198 |
//if columns is not set then define columns as the keys of the rows returned
|
|
|
2199 |
//from the db.
|
|
|
2200 |
$this->define_columns(array_keys((array)$onerow));
|
|
|
2201 |
$this->define_headers(array_keys((array)$onerow));
|
|
|
2202 |
}
|
|
|
2203 |
$this->pagesize = $pagesize;
|
|
|
2204 |
$this->setup();
|
|
|
2205 |
$this->query_db($pagesize, $useinitialsbar);
|
|
|
2206 |
$this->build_table();
|
|
|
2207 |
$this->close_recordset();
|
|
|
2208 |
$this->finish_output();
|
|
|
2209 |
}
|
|
|
2210 |
}
|
|
|
2211 |
|
|
|
2212 |
|
|
|
2213 |
/**
|
|
|
2214 |
* @package moodlecore
|
|
|
2215 |
* @copyright 1999 onwards Martin Dougiamas {@link http://moodle.com}
|
|
|
2216 |
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
|
|
2217 |
*/
|
|
|
2218 |
class table_default_export_format_parent {
|
|
|
2219 |
/**
|
|
|
2220 |
* @var flexible_table or child class reference pointing to table class
|
|
|
2221 |
* object from which to export data.
|
|
|
2222 |
*/
|
|
|
2223 |
var $table;
|
|
|
2224 |
|
|
|
2225 |
/**
|
|
|
2226 |
* @var bool output started. Keeps track of whether any output has been
|
|
|
2227 |
* started yet.
|
|
|
2228 |
*/
|
|
|
2229 |
var $documentstarted = false;
|
|
|
2230 |
|
|
|
2231 |
/**
|
|
|
2232 |
* Constructor
|
|
|
2233 |
*
|
|
|
2234 |
* @param flexible_table $table
|
|
|
2235 |
*/
|
|
|
2236 |
public function __construct(&$table) {
|
|
|
2237 |
$this->table =& $table;
|
|
|
2238 |
}
|
|
|
2239 |
|
|
|
2240 |
/**
|
|
|
2241 |
* Old syntax of class constructor. Deprecated in PHP7.
|
|
|
2242 |
*
|
|
|
2243 |
* @deprecated since Moodle 3.1
|
|
|
2244 |
*/
|
|
|
2245 |
public function table_default_export_format_parent(&$table) {
|
|
|
2246 |
debugging('Use of class name as constructor is deprecated', DEBUG_DEVELOPER);
|
|
|
2247 |
self::__construct($table);
|
|
|
2248 |
}
|
|
|
2249 |
|
|
|
2250 |
function set_table(&$table) {
|
|
|
2251 |
$this->table =& $table;
|
|
|
2252 |
}
|
|
|
2253 |
|
|
|
2254 |
function add_data($row) {
|
|
|
2255 |
return false;
|
|
|
2256 |
}
|
|
|
2257 |
|
|
|
2258 |
function add_seperator() {
|
|
|
2259 |
return false;
|
|
|
2260 |
}
|
|
|
2261 |
|
|
|
2262 |
function document_started() {
|
|
|
2263 |
return $this->documentstarted;
|
|
|
2264 |
}
|
|
|
2265 |
/**
|
|
|
2266 |
* Given text in a variety of format codings, this function returns
|
|
|
2267 |
* the text as safe HTML or as plain text dependent on what is appropriate
|
|
|
2268 |
* for the download format. The default removes all tags.
|
|
|
2269 |
*/
|
|
|
2270 |
function format_text($text, $format=FORMAT_MOODLE, $options=NULL, $courseid=NULL) {
|
|
|
2271 |
//use some whitespace to indicate where there was some line spacing.
|
|
|
2272 |
$text = str_replace(array('</p>', "\n", "\r"), ' ', $text);
|
|
|
2273 |
return html_entity_decode(strip_tags($text), ENT_COMPAT);
|
|
|
2274 |
}
|
|
|
2275 |
|
|
|
2276 |
/**
|
|
|
2277 |
* Format a row of data, removing HTML tags and entities from each of the cells
|
|
|
2278 |
*
|
|
|
2279 |
* @param array $row
|
|
|
2280 |
* @return array
|
|
|
2281 |
*/
|
|
|
2282 |
public function format_data(array $row): array {
|
|
|
2283 |
return array_map([$this, 'format_text'], $row);
|
|
|
2284 |
}
|
|
|
2285 |
}
|
|
|
2286 |
|
|
|
2287 |
/**
|
|
|
2288 |
* Dataformat exporter
|
|
|
2289 |
*
|
|
|
2290 |
* @package core
|
|
|
2291 |
* @subpackage tablelib
|
|
|
2292 |
* @copyright 2016 Brendan Heywood (brendan@catalyst-au.net)
|
|
|
2293 |
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
|
|
2294 |
*/
|
|
|
2295 |
class table_dataformat_export_format extends table_default_export_format_parent {
|
|
|
2296 |
|
|
|
2297 |
/** @var \core\dataformat\base $dataformat */
|
|
|
2298 |
protected $dataformat;
|
|
|
2299 |
|
|
|
2300 |
/** @var int $rownum */
|
|
|
2301 |
protected $rownum = 0;
|
|
|
2302 |
|
|
|
2303 |
/** @var array $columns */
|
|
|
2304 |
protected $columns;
|
|
|
2305 |
|
|
|
2306 |
/**
|
|
|
2307 |
* Constructor
|
|
|
2308 |
*
|
|
|
2309 |
* @param string $table An sql table
|
|
|
2310 |
* @param string $dataformat type of dataformat for export
|
|
|
2311 |
*/
|
|
|
2312 |
public function __construct(&$table, $dataformat) {
|
|
|
2313 |
parent::__construct($table);
|
|
|
2314 |
|
|
|
2315 |
if (ob_get_length()) {
|
|
|
2316 |
throw new coding_exception("Output can not be buffered before instantiating table_dataformat_export_format");
|
|
|
2317 |
}
|
|
|
2318 |
|
11 |
efrain |
2319 |
$this->dataformat = dataformat::get_format_instance($dataformat);
|
1 |
efrain |
2320 |
|
|
|
2321 |
// The dataformat export time to first byte could take a while to generate...
|
|
|
2322 |
set_time_limit(0);
|
|
|
2323 |
|
|
|
2324 |
// Close the session so that the users other tabs in the same session are not blocked.
|
|
|
2325 |
\core\session\manager::write_close();
|
|
|
2326 |
}
|
|
|
2327 |
|
|
|
2328 |
/**
|
|
|
2329 |
* Whether the current dataformat supports export of HTML
|
|
|
2330 |
*
|
|
|
2331 |
* @return bool
|
|
|
2332 |
*/
|
|
|
2333 |
public function supports_html(): bool {
|
|
|
2334 |
return $this->dataformat->supports_html();
|
|
|
2335 |
}
|
|
|
2336 |
|
|
|
2337 |
/**
|
|
|
2338 |
* Start document
|
|
|
2339 |
*
|
|
|
2340 |
* @param string $filename
|
|
|
2341 |
* @param string $sheettitle
|
|
|
2342 |
*/
|
|
|
2343 |
public function start_document($filename, $sheettitle) {
|
|
|
2344 |
$this->documentstarted = true;
|
|
|
2345 |
$this->dataformat->set_filename($filename);
|
|
|
2346 |
$this->dataformat->send_http_headers();
|
|
|
2347 |
$this->dataformat->set_sheettitle($sheettitle);
|
|
|
2348 |
$this->dataformat->start_output();
|
|
|
2349 |
}
|
|
|
2350 |
|
|
|
2351 |
/**
|
|
|
2352 |
* Start export
|
|
|
2353 |
*
|
|
|
2354 |
* @param string $sheettitle optional spreadsheet worksheet title
|
|
|
2355 |
*/
|
|
|
2356 |
public function start_table($sheettitle) {
|
|
|
2357 |
$this->dataformat->set_sheettitle($sheettitle);
|
|
|
2358 |
}
|
|
|
2359 |
|
|
|
2360 |
/**
|
|
|
2361 |
* Output headers
|
|
|
2362 |
*
|
|
|
2363 |
* @param array $headers
|
|
|
2364 |
*/
|
|
|
2365 |
public function output_headers($headers) {
|
|
|
2366 |
$this->columns = $this->format_data($headers);
|
|
|
2367 |
if (method_exists($this->dataformat, 'write_header')) {
|
|
|
2368 |
error_log('The function write_header() does not support multiple sheets. In order to support multiple sheets you ' .
|
|
|
2369 |
'must implement start_output() and start_sheet() and remove write_header() in your dataformat.');
|
|
|
2370 |
$this->dataformat->write_header($this->columns);
|
|
|
2371 |
} else {
|
|
|
2372 |
$this->dataformat->start_sheet($this->columns);
|
|
|
2373 |
}
|
|
|
2374 |
}
|
|
|
2375 |
|
|
|
2376 |
/**
|
|
|
2377 |
* Add a row of data
|
|
|
2378 |
*
|
|
|
2379 |
* @param array $row One record of data
|
|
|
2380 |
*/
|
|
|
2381 |
public function add_data($row) {
|
|
|
2382 |
if (!$this->supports_html()) {
|
|
|
2383 |
$row = $this->format_data($row);
|
|
|
2384 |
}
|
|
|
2385 |
|
|
|
2386 |
$this->dataformat->write_record($row, $this->rownum++);
|
|
|
2387 |
return true;
|
|
|
2388 |
}
|
|
|
2389 |
|
|
|
2390 |
/**
|
|
|
2391 |
* Finish export
|
|
|
2392 |
*/
|
|
|
2393 |
public function finish_table() {
|
|
|
2394 |
if (method_exists($this->dataformat, 'write_footer')) {
|
|
|
2395 |
error_log('The function write_footer() does not support multiple sheets. In order to support multiple sheets you ' .
|
|
|
2396 |
'must implement close_sheet() and close_output() and remove write_footer() in your dataformat.');
|
|
|
2397 |
$this->dataformat->write_footer($this->columns);
|
|
|
2398 |
} else {
|
|
|
2399 |
$this->dataformat->close_sheet($this->columns);
|
|
|
2400 |
}
|
|
|
2401 |
}
|
|
|
2402 |
|
|
|
2403 |
/**
|
|
|
2404 |
* Finish download
|
|
|
2405 |
*/
|
|
|
2406 |
public function finish_document() {
|
|
|
2407 |
$this->dataformat->close_output();
|
|
|
2408 |
exit();
|
|
|
2409 |
}
|
|
|
2410 |
}
|