[ Index ] |
PHP Cross Reference of moodle-2.8 |
[Summary view] [Print] [Text view]
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 /**#@-*/ 38 39 /**#@+ 40 * Constants that indicate whether the paging bar for the table 41 * appears above or below the table. 42 */ 43 define('TABLE_P_TOP', 1); 44 define('TABLE_P_BOTTOM', 2); 45 /**#@-*/ 46 47 48 /** 49 * @package moodlecore 50 * @copyright 1999 onwards Martin Dougiamas {@link http://moodle.com} 51 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 52 */ 53 class flexible_table { 54 55 var $uniqueid = NULL; 56 var $attributes = array(); 57 var $headers = array(); 58 var $columns = array(); 59 var $column_style = array(); 60 var $column_class = array(); 61 var $column_suppress = array(); 62 var $column_nosort = array('userpic'); 63 private $column_textsort = array(); 64 var $setup = false; 65 var $sess = NULL; 66 var $baseurl = NULL; 67 var $request = array(); 68 69 var $is_collapsible = false; 70 var $is_sortable = false; 71 var $use_pages = false; 72 var $use_initials = false; 73 74 var $maxsortkeys = 2; 75 var $pagesize = 30; 76 var $currpage = 0; 77 var $totalrows = 0; 78 var $currentrow = 0; 79 var $sort_default_column = NULL; 80 var $sort_default_order = SORT_ASC; 81 82 /** 83 * Array of positions in which to display download controls. 84 */ 85 var $showdownloadbuttonsat= array(TABLE_P_TOP); 86 87 /** 88 * @var string Key of field returned by db query that is the id field of the 89 * user table or equivalent. 90 */ 91 public $useridfield = 'id'; 92 93 /** 94 * @var string which download plugin to use. Default '' means none - print 95 * html table with paging. Property set by is_downloading which typically 96 * passes in cleaned data from $ 97 */ 98 var $download = ''; 99 100 /** 101 * @var bool whether data is downloadable from table. Determines whether 102 * to display download buttons. Set by method downloadable(). 103 */ 104 var $downloadable = false; 105 106 /** 107 * @var string which download plugin to use. Default '' means none - print 108 * html table with paging. 109 */ 110 var $defaultdownloadformat = 'csv'; 111 112 /** 113 * @var bool Has start output been called yet? 114 */ 115 var $started_output = false; 116 117 var $exportclass = null; 118 119 /** 120 * Constructor 121 * @param int $uniqueid all tables have to have a unique id, this is used 122 * as a key when storing table properties like sort order in the session. 123 */ 124 function __construct($uniqueid) { 125 $this->uniqueid = $uniqueid; 126 $this->request = array( 127 TABLE_VAR_SORT => 'tsort', 128 TABLE_VAR_HIDE => 'thide', 129 TABLE_VAR_SHOW => 'tshow', 130 TABLE_VAR_IFIRST => 'tifirst', 131 TABLE_VAR_ILAST => 'tilast', 132 TABLE_VAR_PAGE => 'page', 133 ); 134 } 135 136 /** 137 * Call this to pass the download type. Use : 138 * $download = optional_param('download', '', PARAM_ALPHA); 139 * To get the download type. We assume that if you call this function with 140 * params that this table's data is downloadable, so we call is_downloadable 141 * for you (even if the param is '', which means no download this time. 142 * Also you can call this method with no params to get the current set 143 * download type. 144 * @param string $download download type. One of csv, tsv, xhtml, ods, etc 145 * @param string $filename filename for downloads without file extension. 146 * @param string $sheettitle title for downloaded data. 147 * @return string download type. One of csv, tsv, xhtml, ods, etc 148 */ 149 function is_downloading($download = null, $filename='', $sheettitle='') { 150 if ($download!==null) { 151 $this->sheettitle = $sheettitle; 152 $this->is_downloadable(true); 153 $this->download = $download; 154 $this->filename = clean_filename($filename); 155 $this->export_class_instance(); 156 } 157 return $this->download; 158 } 159 160 /** 161 * Get, and optionally set, the export class. 162 * @param $exportclass (optional) if passed, set the table to use this export class. 163 * @return table_default_export_format_parent the export class in use (after any set). 164 */ 165 function export_class_instance($exportclass = null) { 166 if (!is_null($exportclass)) { 167 $this->started_output = true; 168 $this->exportclass = $exportclass; 169 $this->exportclass->table = $this; 170 } else if (is_null($this->exportclass) && !empty($this->download)) { 171 $classname = 'table_'.$this->download.'_export_format'; 172 $this->exportclass = new $classname($this); 173 if (!$this->exportclass->document_started()) { 174 $this->exportclass->start_document($this->filename); 175 } 176 } 177 return $this->exportclass; 178 } 179 180 /** 181 * Probably don't need to call this directly. Calling is_downloading with a 182 * param automatically sets table as downloadable. 183 * 184 * @param bool $downloadable optional param to set whether data from 185 * table is downloadable. If ommitted this function can be used to get 186 * current state of table. 187 * @return bool whether table data is set to be downloadable. 188 */ 189 function is_downloadable($downloadable = null) { 190 if ($downloadable !== null) { 191 $this->downloadable = $downloadable; 192 } 193 return $this->downloadable; 194 } 195 196 /** 197 * Where to show download buttons. 198 * @param array $showat array of postions in which to show download buttons. 199 * Containing TABLE_P_TOP and/or TABLE_P_BOTTOM 200 */ 201 function show_download_buttons_at($showat) { 202 $this->showdownloadbuttonsat = $showat; 203 } 204 205 /** 206 * Sets the is_sortable variable to the given boolean, sort_default_column to 207 * the given string, and the sort_default_order to the given integer. 208 * @param bool $bool 209 * @param string $defaultcolumn 210 * @param int $defaultorder 211 * @return void 212 */ 213 function sortable($bool, $defaultcolumn = NULL, $defaultorder = SORT_ASC) { 214 $this->is_sortable = $bool; 215 $this->sort_default_column = $defaultcolumn; 216 $this->sort_default_order = $defaultorder; 217 } 218 219 /** 220 * Use text sorting functions for this column (required for text columns with Oracle). 221 * Be warned that you cannot use this with column aliases. You can only do this 222 * with real columns. See MDL-40481 for an example. 223 * @param string column name 224 */ 225 function text_sorting($column) { 226 $this->column_textsort[] = $column; 227 } 228 229 /** 230 * Do not sort using this column 231 * @param string column name 232 */ 233 function no_sorting($column) { 234 $this->column_nosort[] = $column; 235 } 236 237 /** 238 * Is the column sortable? 239 * @param string column name, null means table 240 * @return bool 241 */ 242 function is_sortable($column = null) { 243 if (empty($column)) { 244 return $this->is_sortable; 245 } 246 if (!$this->is_sortable) { 247 return false; 248 } 249 return !in_array($column, $this->column_nosort); 250 } 251 252 /** 253 * Sets the is_collapsible variable to the given boolean. 254 * @param bool $bool 255 * @return void 256 */ 257 function collapsible($bool) { 258 $this->is_collapsible = $bool; 259 } 260 261 /** 262 * Sets the use_pages variable to the given boolean. 263 * @param bool $bool 264 * @return void 265 */ 266 function pageable($bool) { 267 $this->use_pages = $bool; 268 } 269 270 /** 271 * Sets the use_initials variable to the given boolean. 272 * @param bool $bool 273 * @return void 274 */ 275 function initialbars($bool) { 276 $this->use_initials = $bool; 277 } 278 279 /** 280 * Sets the pagesize variable to the given integer, the totalrows variable 281 * to the given integer, and the use_pages variable to true. 282 * @param int $perpage 283 * @param int $total 284 * @return void 285 */ 286 function pagesize($perpage, $total) { 287 $this->pagesize = $perpage; 288 $this->totalrows = $total; 289 $this->use_pages = true; 290 } 291 292 /** 293 * Assigns each given variable in the array to the corresponding index 294 * in the request class variable. 295 * @param array $variables 296 * @return void 297 */ 298 function set_control_variables($variables) { 299 foreach ($variables as $what => $variable) { 300 if (isset($this->request[$what])) { 301 $this->request[$what] = $variable; 302 } 303 } 304 } 305 306 /** 307 * Gives the given $value to the $attribute index of $this->attributes. 308 * @param string $attribute 309 * @param mixed $value 310 * @return void 311 */ 312 function set_attribute($attribute, $value) { 313 $this->attributes[$attribute] = $value; 314 } 315 316 /** 317 * What this method does is set the column so that if the same data appears in 318 * consecutive rows, then it is not repeated. 319 * 320 * For example, in the quiz overview report, the fullname column is set to be suppressed, so 321 * that when one student has made multiple attempts, their name is only printed in the row 322 * for their first attempt. 323 * @param int $column the index of a column. 324 */ 325 function column_suppress($column) { 326 if (isset($this->column_suppress[$column])) { 327 $this->column_suppress[$column] = true; 328 } 329 } 330 331 /** 332 * Sets the given $column index to the given $classname in $this->column_class. 333 * @param int $column 334 * @param string $classname 335 * @return void 336 */ 337 function column_class($column, $classname) { 338 if (isset($this->column_class[$column])) { 339 $this->column_class[$column] = ' '.$classname; // This space needed so that classnames don't run together in the HTML 340 } 341 } 342 343 /** 344 * Sets the given $column index and $property index to the given $value in $this->column_style. 345 * @param int $column 346 * @param string $property 347 * @param mixed $value 348 * @return void 349 */ 350 function column_style($column, $property, $value) { 351 if (isset($this->column_style[$column])) { 352 $this->column_style[$column][$property] = $value; 353 } 354 } 355 356 /** 357 * Sets all columns' $propertys to the given $value in $this->column_style. 358 * @param int $property 359 * @param string $value 360 * @return void 361 */ 362 function column_style_all($property, $value) { 363 foreach (array_keys($this->columns) as $column) { 364 $this->column_style[$column][$property] = $value; 365 } 366 } 367 368 /** 369 * Sets $this->baseurl. 370 * @param moodle_url|string $url the url with params needed to call up this page 371 */ 372 function define_baseurl($url) { 373 $this->baseurl = new moodle_url($url); 374 } 375 376 /** 377 * @param array $columns an array of identifying names for columns. If 378 * columns are sorted then column names must correspond to a field in sql. 379 */ 380 function define_columns($columns) { 381 $this->columns = array(); 382 $this->column_style = array(); 383 $this->column_class = array(); 384 $colnum = 0; 385 386 foreach ($columns as $column) { 387 $this->columns[$column] = $colnum++; 388 $this->column_style[$column] = array(); 389 $this->column_class[$column] = ''; 390 $this->column_suppress[$column] = false; 391 } 392 } 393 394 /** 395 * @param array $headers numerical keyed array of displayed string titles 396 * for each column. 397 */ 398 function define_headers($headers) { 399 $this->headers = $headers; 400 } 401 402 /** 403 * Must be called after table is defined. Use methods above first. Cannot 404 * use functions below till after calling this method. 405 * @return type? 406 */ 407 function setup() { 408 global $SESSION, $CFG; 409 410 if (empty($this->columns) || empty($this->uniqueid)) { 411 return false; 412 } 413 414 if (!isset($SESSION->flextable)) { 415 $SESSION->flextable = array(); 416 } 417 418 if (!isset($SESSION->flextable[$this->uniqueid])) { 419 $SESSION->flextable[$this->uniqueid] = new stdClass; 420 $SESSION->flextable[$this->uniqueid]->uniqueid = $this->uniqueid; 421 $SESSION->flextable[$this->uniqueid]->collapse = array(); 422 $SESSION->flextable[$this->uniqueid]->sortby = array(); 423 $SESSION->flextable[$this->uniqueid]->i_first = ''; 424 $SESSION->flextable[$this->uniqueid]->i_last = ''; 425 $SESSION->flextable[$this->uniqueid]->textsort = $this->column_textsort; 426 } 427 428 $this->sess = &$SESSION->flextable[$this->uniqueid]; 429 430 if (($showcol = optional_param($this->request[TABLE_VAR_SHOW], '', PARAM_ALPHANUMEXT)) && 431 isset($this->columns[$showcol])) { 432 $this->sess->collapse[$showcol] = false; 433 434 } else if (($hidecol = optional_param($this->request[TABLE_VAR_HIDE], '', PARAM_ALPHANUMEXT)) && 435 isset($this->columns[$hidecol])) { 436 $this->sess->collapse[$hidecol] = true; 437 if (array_key_exists($hidecol, $this->sess->sortby)) { 438 unset($this->sess->sortby[$hidecol]); 439 } 440 } 441 442 // Now, update the column attributes for collapsed columns 443 foreach (array_keys($this->columns) as $column) { 444 if (!empty($this->sess->collapse[$column])) { 445 $this->column_style[$column]['width'] = '10px'; 446 } 447 } 448 449 if (($sortcol = optional_param($this->request[TABLE_VAR_SORT], '', PARAM_ALPHANUMEXT)) && 450 $this->is_sortable($sortcol) && empty($this->sess->collapse[$sortcol]) && 451 (isset($this->columns[$sortcol]) || in_array($sortcol, get_all_user_name_fields()) 452 && isset($this->columns['fullname']))) { 453 454 if (array_key_exists($sortcol, $this->sess->sortby)) { 455 // This key already exists somewhere. Change its sortorder and bring it to the top. 456 $sortorder = $this->sess->sortby[$sortcol] == SORT_ASC ? SORT_DESC : SORT_ASC; 457 unset($this->sess->sortby[$sortcol]); 458 $this->sess->sortby = array_merge(array($sortcol => $sortorder), $this->sess->sortby); 459 } else { 460 // Key doesn't exist, so just add it to the beginning of the array, ascending order 461 $this->sess->sortby = array_merge(array($sortcol => SORT_ASC), $this->sess->sortby); 462 } 463 464 // Finally, make sure that no more than $this->maxsortkeys are present into the array 465 $this->sess->sortby = array_slice($this->sess->sortby, 0, $this->maxsortkeys); 466 } 467 468 // MDL-35375 - If a default order is defined and it is not in the current list of order by columns, add it at the end. 469 // This prevents results from being returned in a random order if the only order by column contains equal values. 470 if (!empty($this->sort_default_column)) { 471 if (!array_key_exists($this->sort_default_column, $this->sess->sortby)) { 472 $defaultsort = array($this->sort_default_column => $this->sort_default_order); 473 $this->sess->sortby = array_merge($this->sess->sortby, $defaultsort); 474 } 475 } 476 477 $ilast = optional_param($this->request[TABLE_VAR_ILAST], null, PARAM_RAW); 478 if (!is_null($ilast) && ($ilast ==='' || strpos(get_string('alphabet', 'langconfig'), $ilast) !== false)) { 479 $this->sess->i_last = $ilast; 480 } 481 482 $ifirst = optional_param($this->request[TABLE_VAR_IFIRST], null, PARAM_RAW); 483 if (!is_null($ifirst) && ($ifirst === '' || strpos(get_string('alphabet', 'langconfig'), $ifirst) !== false)) { 484 $this->sess->i_first = $ifirst; 485 } 486 487 if (empty($this->baseurl)) { 488 debugging('You should set baseurl when using flexible_table.'); 489 global $PAGE; 490 $this->baseurl = $PAGE->url; 491 } 492 493 $this->currpage = optional_param($this->request[TABLE_VAR_PAGE], 0, PARAM_INT); 494 $this->setup = true; 495 496 // Always introduce the "flexible" class for the table if not specified 497 if (empty($this->attributes)) { 498 $this->attributes['class'] = 'flexible'; 499 } else if (!isset($this->attributes['class'])) { 500 $this->attributes['class'] = 'flexible'; 501 } else if (!in_array('flexible', explode(' ', $this->attributes['class']))) { 502 $this->attributes['class'] = trim('flexible ' . $this->attributes['class']); 503 } 504 } 505 506 /** 507 * Get the order by clause from the session, for the table with id $uniqueid. 508 * @param string $uniqueid the identifier for a table. 509 * @return SQL fragment that can be used in an ORDER BY clause. 510 */ 511 public static function get_sort_for_table($uniqueid) { 512 global $SESSION; 513 if (empty($SESSION->flextable[$uniqueid])) { 514 return ''; 515 } 516 517 $sess = &$SESSION->flextable[$uniqueid]; 518 if (empty($sess->sortby)) { 519 return ''; 520 } 521 if (empty($sess->textsort)) { 522 $sess->textsort = array(); 523 } 524 525 return self::construct_order_by($sess->sortby, $sess->textsort); 526 } 527 528 /** 529 * Prepare an an order by clause from the list of columns to be sorted. 530 * @param array $cols column name => SORT_ASC or SORT_DESC 531 * @return SQL fragment that can be used in an ORDER BY clause. 532 */ 533 public static function construct_order_by($cols, $textsortcols=array()) { 534 global $DB; 535 $bits = array(); 536 537 foreach ($cols as $column => $order) { 538 if (in_array($column, $textsortcols)) { 539 $column = $DB->sql_order_by_text($column); 540 } 541 if ($order == SORT_ASC) { 542 $bits[] = $column . ' ASC'; 543 } else { 544 $bits[] = $column . ' DESC'; 545 } 546 } 547 548 return implode(', ', $bits); 549 } 550 551 /** 552 * @return SQL fragment that can be used in an ORDER BY clause. 553 */ 554 public function get_sql_sort() { 555 return self::construct_order_by($this->get_sort_columns(), $this->column_textsort); 556 } 557 558 /** 559 * Get the columns to sort by, in the form required by {@link construct_order_by()}. 560 * @return array column name => SORT_... constant. 561 */ 562 public function get_sort_columns() { 563 if (!$this->setup) { 564 throw new coding_exception('Cannot call get_sort_columns until you have called setup.'); 565 } 566 567 if (empty($this->sess->sortby)) { 568 return array(); 569 } 570 571 foreach ($this->sess->sortby as $column => $notused) { 572 if (isset($this->columns[$column])) { 573 continue; // This column is OK. 574 } 575 if (in_array($column, get_all_user_name_fields()) && 576 isset($this->columns['fullname'])) { 577 continue; // This column is OK. 578 } 579 // This column is not OK. 580 unset($this->sess->sortby[$column]); 581 } 582 583 return $this->sess->sortby; 584 } 585 586 /** 587 * @return int the offset for LIMIT clause of SQL 588 */ 589 function get_page_start() { 590 if (!$this->use_pages) { 591 return ''; 592 } 593 return $this->currpage * $this->pagesize; 594 } 595 596 /** 597 * @return int the pagesize for LIMIT clause of SQL 598 */ 599 function get_page_size() { 600 if (!$this->use_pages) { 601 return ''; 602 } 603 return $this->pagesize; 604 } 605 606 /** 607 * @return string sql to add to where statement. 608 */ 609 function get_sql_where() { 610 global $DB; 611 612 $conditions = array(); 613 $params = array(); 614 615 if (isset($this->columns['fullname'])) { 616 static $i = 0; 617 $i++; 618 619 if (!empty($this->sess->i_first)) { 620 $conditions[] = $DB->sql_like('firstname', ':ifirstc'.$i, false, false); 621 $params['ifirstc'.$i] = $this->sess->i_first.'%'; 622 } 623 if (!empty($this->sess->i_last)) { 624 $conditions[] = $DB->sql_like('lastname', ':ilastc'.$i, false, false); 625 $params['ilastc'.$i] = $this->sess->i_last.'%'; 626 } 627 } 628 629 return array(implode(" AND ", $conditions), $params); 630 } 631 632 /** 633 * Add a row of data to the table. This function takes an array or object with 634 * column names as keys or property names. 635 * 636 * It ignores any elements with keys that are not defined as columns. It 637 * puts in empty strings into the row when there is no element in the passed 638 * array corresponding to a column in the table. It puts the row elements in 639 * the proper order (internally row table data is stored by in arrays with 640 * a numerical index corresponding to the column number). 641 * 642 * @param object|array $rowwithkeys array keys or object property names are column names, 643 * as defined in call to define_columns. 644 * @param string $classname CSS class name to add to this row's tr tag. 645 */ 646 function add_data_keyed($rowwithkeys, $classname = '') { 647 $this->add_data($this->get_row_from_keyed($rowwithkeys), $classname); 648 } 649 650 /** 651 * Add a number of rows to the table at once. And optionally finish output after they have been added. 652 * 653 * @param (object|array|null)[] $rowstoadd Array of rows to add to table, a null value in array adds a separator row. Or a 654 * object or array is added to table. We expect properties for the row array as would be 655 * passed to add_data_keyed. 656 * @param bool $finish 657 */ 658 public function format_and_add_array_of_rows($rowstoadd, $finish = true) { 659 foreach ($rowstoadd as $row) { 660 if (is_null($row)) { 661 $this->add_separator(); 662 } else { 663 $this->add_data_keyed($this->format_row($row)); 664 } 665 } 666 if ($finish) { 667 $this->finish_output(!$this->is_downloading()); 668 } 669 } 670 671 /** 672 * Add a seperator line to table. 673 */ 674 function add_separator() { 675 if (!$this->setup) { 676 return false; 677 } 678 $this->add_data(NULL); 679 } 680 681 /** 682 * This method actually directly echoes the row passed to it now or adds it 683 * to the download. If this is the first row and start_output has not 684 * already been called this method also calls start_output to open the table 685 * or send headers for the downloaded. 686 * Can be used as before. print_html now calls finish_html to close table. 687 * 688 * @param array $row a numerically keyed row of data to add to the table. 689 * @param string $classname CSS class name to add to this row's tr tag. 690 * @return bool success. 691 */ 692 function add_data($row, $classname = '') { 693 if (!$this->setup) { 694 return false; 695 } 696 if (!$this->started_output) { 697 $this->start_output(); 698 } 699 if ($this->exportclass!==null) { 700 if ($row === null) { 701 $this->exportclass->add_seperator(); 702 } else { 703 $this->exportclass->add_data($row); 704 } 705 } else { 706 $this->print_row($row, $classname); 707 } 708 return true; 709 } 710 711 /** 712 * You should call this to finish outputting the table data after adding 713 * data to the table with add_data or add_data_keyed. 714 * 715 */ 716 function finish_output($closeexportclassdoc = true) { 717 if ($this->exportclass!==null) { 718 $this->exportclass->finish_table(); 719 if ($closeexportclassdoc) { 720 $this->exportclass->finish_document(); 721 } 722 } else { 723 $this->finish_html(); 724 } 725 } 726 727 /** 728 * Hook that can be overridden in child classes to wrap a table in a form 729 * for example. Called only when there is data to display and not 730 * downloading. 731 */ 732 function wrap_html_start() { 733 } 734 735 /** 736 * Hook that can be overridden in child classes to wrap a table in a form 737 * for example. Called only when there is data to display and not 738 * downloading. 739 */ 740 function wrap_html_finish() { 741 } 742 743 /** 744 * Call appropriate methods on this table class to perform any processing on values before displaying in table. 745 * Takes raw data from the database and process it into human readable format, perhaps also adding html linking when 746 * displaying table as html, adding a div wrap, etc. 747 * 748 * See for example col_fullname below which will be called for a column whose name is 'fullname'. 749 * 750 * @param array|object $row row of data from db used to make one row of the table. 751 * @return array one row for the table, added using add_data_keyed method. 752 */ 753 function format_row($row) { 754 if (is_array($row)) { 755 $row = (object)$row; 756 } 757 $formattedrow = array(); 758 foreach (array_keys($this->columns) as $column) { 759 $colmethodname = 'col_'.$column; 760 if (method_exists($this, $colmethodname)) { 761 $formattedcolumn = $this->$colmethodname($row); 762 } else { 763 $formattedcolumn = $this->other_cols($column, $row); 764 if ($formattedcolumn===NULL) { 765 $formattedcolumn = $row->$column; 766 } 767 } 768 $formattedrow[$column] = $formattedcolumn; 769 } 770 return $formattedrow; 771 } 772 773 /** 774 * Fullname is treated as a special columname in tablelib and should always 775 * be treated the same as the fullname of a user. 776 * @uses $this->useridfield if the userid field is not expected to be id 777 * then you need to override $this->useridfield to point at the correct 778 * field for the user id. 779 * 780 * @param object $row the data from the db containing all fields from the 781 * users table necessary to construct the full name of the user in 782 * current language. 783 * @return string contents of cell in column 'fullname', for this row. 784 */ 785 function col_fullname($row) { 786 global $COURSE; 787 788 $name = fullname($row); 789 if ($this->download) { 790 return $name; 791 } 792 793 $userid = $row->{$this->useridfield}; 794 if ($COURSE->id == SITEID) { 795 $profileurl = new moodle_url('/user/profile.php', array('id' => $userid)); 796 } else { 797 $profileurl = new moodle_url('/user/view.php', 798 array('id' => $userid, 'course' => $COURSE->id)); 799 } 800 return html_writer::link($profileurl, $name); 801 } 802 803 /** 804 * You can override this method in a child class. See the description of 805 * build_table which calls this method. 806 */ 807 function other_cols($column, $row) { 808 return NULL; 809 } 810 811 /** 812 * Used from col_* functions when text is to be displayed. Does the 813 * right thing - either converts text to html or strips any html tags 814 * depending on if we are downloading and what is the download type. Params 815 * are the same as format_text function in weblib.php but some default 816 * options are changed. 817 */ 818 function format_text($text, $format=FORMAT_MOODLE, $options=NULL, $courseid=NULL) { 819 if (!$this->is_downloading()) { 820 if (is_null($options)) { 821 $options = new stdClass; 822 } 823 //some sensible defaults 824 if (!isset($options->para)) { 825 $options->para = false; 826 } 827 if (!isset($options->newlines)) { 828 $options->newlines = false; 829 } 830 if (!isset($options->smiley)) { 831 $options->smiley = false; 832 } 833 if (!isset($options->filter)) { 834 $options->filter = false; 835 } 836 return format_text($text, $format, $options); 837 } else { 838 $eci = $this->export_class_instance(); 839 return $eci->format_text($text, $format, $options, $courseid); 840 } 841 } 842 /** 843 * This method is deprecated although the old api is still supported. 844 * @deprecated 1.9.2 - Jun 2, 2008 845 */ 846 function print_html() { 847 if (!$this->setup) { 848 return false; 849 } 850 $this->finish_html(); 851 } 852 853 /** 854 * This function is not part of the public api. 855 * @return string initial of first name we are currently filtering by 856 */ 857 function get_initial_first() { 858 if (!$this->use_initials) { 859 return NULL; 860 } 861 862 return $this->sess->i_first; 863 } 864 865 /** 866 * This function is not part of the public api. 867 * @return string initial of last name we are currently filtering by 868 */ 869 function get_initial_last() { 870 if (!$this->use_initials) { 871 return NULL; 872 } 873 874 return $this->sess->i_last; 875 } 876 877 /** 878 * Helper function, used by {@link print_initials_bar()} to output one initial bar. 879 * @param array $alpha of letters in the alphabet. 880 * @param string $current the currently selected letter. 881 * @param string $class class name to add to this initial bar. 882 * @param string $title the name to put in front of this initial bar. 883 * @param string $urlvar URL parameter name for this initial. 884 */ 885 protected function print_one_initials_bar($alpha, $current, $class, $title, $urlvar) { 886 echo html_writer::start_tag('div', array('class' => 'initialbar ' . $class)) . 887 $title . ' : '; 888 if ($current) { 889 echo html_writer::link($this->baseurl->out(false, array($urlvar => '')), get_string('all')); 890 } else { 891 echo html_writer::tag('strong', get_string('all')); 892 } 893 894 foreach ($alpha as $letter) { 895 if ($letter === $current) { 896 echo html_writer::tag('strong', $letter); 897 } else { 898 echo html_writer::link($this->baseurl->out(false, array($urlvar => $letter)), $letter); 899 } 900 } 901 902 echo html_writer::end_tag('div'); 903 } 904 905 /** 906 * This function is not part of the public api. 907 */ 908 function print_initials_bar() { 909 if ((!empty($this->sess->i_last) || !empty($this->sess->i_first) ||$this->use_initials) 910 && isset($this->columns['fullname'])) { 911 912 $alpha = explode(',', get_string('alphabet', 'langconfig')); 913 914 // Bar of first initials 915 if (!empty($this->sess->i_first)) { 916 $ifirst = $this->sess->i_first; 917 } else { 918 $ifirst = ''; 919 } 920 $this->print_one_initials_bar($alpha, $ifirst, 'firstinitial', 921 get_string('firstname'), $this->request[TABLE_VAR_IFIRST]); 922 923 // Bar of last initials 924 if (!empty($this->sess->i_last)) { 925 $ilast = $this->sess->i_last; 926 } else { 927 $ilast = ''; 928 } 929 $this->print_one_initials_bar($alpha, $ilast, 'lastinitial', 930 get_string('lastname'), $this->request[TABLE_VAR_ILAST]); 931 } 932 } 933 934 /** 935 * This function is not part of the public api. 936 */ 937 function print_nothing_to_display() { 938 global $OUTPUT; 939 $this->print_initials_bar(); 940 941 echo $OUTPUT->heading(get_string('nothingtodisplay')); 942 } 943 944 /** 945 * This function is not part of the public api. 946 */ 947 function get_row_from_keyed($rowwithkeys) { 948 if (is_object($rowwithkeys)) { 949 $rowwithkeys = (array)$rowwithkeys; 950 } 951 $row = array(); 952 foreach (array_keys($this->columns) as $column) { 953 if (isset($rowwithkeys[$column])) { 954 $row [] = $rowwithkeys[$column]; 955 } else { 956 $row[] =''; 957 } 958 } 959 return $row; 960 } 961 /** 962 * This function is not part of the public api. 963 */ 964 function get_download_menu() { 965 $allclasses= get_declared_classes(); 966 $exportclasses = array(); 967 foreach ($allclasses as $class) { 968 $matches = array(); 969 if (preg_match('/^table\_([a-z]+)\_export\_format$/', $class, $matches)) { 970 $type = $matches[1]; 971 $exportclasses[$type]= get_string("download$type", 'table'); 972 } 973 } 974 return $exportclasses; 975 } 976 977 /** 978 * This function is not part of the public api. 979 */ 980 function download_buttons() { 981 if ($this->is_downloadable() && !$this->is_downloading()) { 982 $downloadoptions = $this->get_download_menu(); 983 984 $downloadelements = new stdClass(); 985 $downloadelements->formatsmenu = html_writer::select($downloadoptions, 986 'download', $this->defaultdownloadformat, false); 987 $downloadelements->downloadbutton = '<input type="submit" value="'. 988 get_string('download').'"/>'; 989 $html = '<form action="'. $this->baseurl .'" method="post">'; 990 $html .= '<div class="mdl-align">'; 991 $html .= html_writer::tag('label', get_string('downloadas', 'table', $downloadelements)); 992 $html .= '</div></form>'; 993 994 return $html; 995 } else { 996 return ''; 997 } 998 } 999 /** 1000 * This function is not part of the public api. 1001 * You don't normally need to call this. It is called automatically when 1002 * needed when you start adding data to the table. 1003 * 1004 */ 1005 function start_output() { 1006 $this->started_output = true; 1007 if ($this->exportclass!==null) { 1008 $this->exportclass->start_table($this->sheettitle); 1009 $this->exportclass->output_headers($this->headers); 1010 } else { 1011 $this->start_html(); 1012 $this->print_headers(); 1013 echo html_writer::start_tag('tbody'); 1014 } 1015 } 1016 1017 /** 1018 * This function is not part of the public api. 1019 * 1020 * Please do not use .r0/.r1 for css, as they will be removed in Moodle 2.9. 1021 * @todo MDL-43902 , remove r0 and r1 from tr classes. 1022 */ 1023 function print_row($row, $classname = '') { 1024 echo $this->get_row_html($row, $classname); 1025 } 1026 1027 /** 1028 * Generate html code for the passed row. 1029 * 1030 * @param array $row Row data. 1031 * @param string $classname classes to add. 1032 * 1033 * @return string $html html code for the row passed. 1034 */ 1035 public function get_row_html($row, $classname = '') { 1036 static $suppress_lastrow = NULL; 1037 $oddeven = $this->currentrow % 2; 1038 $rowclasses = array('r' . $oddeven); 1039 1040 if ($classname) { 1041 $rowclasses[] = $classname; 1042 } 1043 1044 $rowid = $this->uniqueid . '_r' . $this->currentrow; 1045 $html = ''; 1046 1047 $html .= html_writer::start_tag('tr', array('class' => implode(' ', $rowclasses), 'id' => $rowid)); 1048 1049 // If we have a separator, print it 1050 if ($row === NULL) { 1051 $colcount = count($this->columns); 1052 $html .= html_writer::tag('td', html_writer::tag('div', '', 1053 array('class' => 'tabledivider')), array('colspan' => $colcount)); 1054 1055 } else { 1056 $colbyindex = array_flip($this->columns); 1057 foreach ($row as $index => $data) { 1058 $column = $colbyindex[$index]; 1059 1060 if (empty($this->sess->collapse[$column])) { 1061 if ($this->column_suppress[$column] && $suppress_lastrow !== NULL && $suppress_lastrow[$index] === $data) { 1062 $content = ' '; 1063 } else { 1064 $content = $data; 1065 } 1066 } else { 1067 $content = ' '; 1068 } 1069 1070 $html .= html_writer::tag('td', $content, array( 1071 'class' => 'cell c' . $index . $this->column_class[$column], 1072 'id' => $rowid . '_c' . $index, 1073 'style' => $this->make_styles_string($this->column_style[$column]))); 1074 } 1075 } 1076 1077 $html .= html_writer::end_tag('tr'); 1078 1079 $suppress_enabled = array_sum($this->column_suppress); 1080 if ($suppress_enabled) { 1081 $suppress_lastrow = $row; 1082 } 1083 $this->currentrow++; 1084 return $html; 1085 } 1086 1087 /** 1088 * This function is not part of the public api. 1089 */ 1090 function finish_html() { 1091 global $OUTPUT; 1092 if (!$this->started_output) { 1093 //no data has been added to the table. 1094 $this->print_nothing_to_display(); 1095 1096 } else { 1097 // Print empty rows to fill the table to the current pagesize. 1098 // This is done so the header aria-controls attributes do not point to 1099 // non existant elements. 1100 $emptyrow = array_fill(0, count($this->columns), ''); 1101 while ($this->currentrow < $this->pagesize) { 1102 $this->print_row($emptyrow, 'emptyrow'); 1103 } 1104 1105 echo html_writer::end_tag('tbody'); 1106 echo html_writer::end_tag('table'); 1107 echo html_writer::end_tag('div'); 1108 $this->wrap_html_finish(); 1109 1110 // Paging bar 1111 if(in_array(TABLE_P_BOTTOM, $this->showdownloadbuttonsat)) { 1112 echo $this->download_buttons(); 1113 } 1114 1115 if($this->use_pages) { 1116 $pagingbar = new paging_bar($this->totalrows, $this->currpage, $this->pagesize, $this->baseurl); 1117 $pagingbar->pagevar = $this->request[TABLE_VAR_PAGE]; 1118 echo $OUTPUT->render($pagingbar); 1119 } 1120 } 1121 } 1122 1123 /** 1124 * Generate the HTML for the collapse/uncollapse icon. This is a helper method 1125 * used by {@link print_headers()}. 1126 * @param string $column the column name, index into various names. 1127 * @param int $index numerical index of the column. 1128 * @return string HTML fragment. 1129 */ 1130 protected function show_hide_link($column, $index) { 1131 global $OUTPUT; 1132 // Some headers contain <br /> tags, do not include in title, hence the 1133 // strip tags. 1134 1135 $ariacontrols = ''; 1136 for ($i = 0; $i < $this->pagesize; $i++) { 1137 $ariacontrols .= $this->uniqueid . '_r' . $i . '_c' . $index . ' '; 1138 } 1139 1140 $ariacontrols = trim($ariacontrols); 1141 1142 if (!empty($this->sess->collapse[$column])) { 1143 $linkattributes = array('title' => get_string('show') . ' ' . strip_tags($this->headers[$index]), 1144 'aria-expanded' => 'false', 1145 'aria-controls' => $ariacontrols); 1146 return html_writer::link($this->baseurl->out(false, array($this->request[TABLE_VAR_SHOW] => $column)), 1147 html_writer::empty_tag('img', array('src' => $OUTPUT->pix_url('t/switch_plus'), 'alt' => get_string('show'))), 1148 $linkattributes); 1149 1150 } else if ($this->headers[$index] !== NULL) { 1151 $linkattributes = array('title' => get_string('hide') . ' ' . strip_tags($this->headers[$index]), 1152 'aria-expanded' => 'true', 1153 'aria-controls' => $ariacontrols); 1154 return html_writer::link($this->baseurl->out(false, array($this->request[TABLE_VAR_HIDE] => $column)), 1155 html_writer::empty_tag('img', array('src' => $OUTPUT->pix_url('t/switch_minus'), 'alt' => get_string('hide'))), 1156 $linkattributes); 1157 } 1158 } 1159 1160 /** 1161 * This function is not part of the public api. 1162 */ 1163 function print_headers() { 1164 global $CFG, $OUTPUT; 1165 1166 echo html_writer::start_tag('thead'); 1167 echo html_writer::start_tag('tr'); 1168 foreach ($this->columns as $column => $index) { 1169 1170 $icon_hide = ''; 1171 if ($this->is_collapsible) { 1172 $icon_hide = $this->show_hide_link($column, $index); 1173 } 1174 1175 $primary_sort_column = ''; 1176 $primary_sort_order = ''; 1177 if (reset($this->sess->sortby)) { 1178 $primary_sort_column = key($this->sess->sortby); 1179 $primary_sort_order = current($this->sess->sortby); 1180 } 1181 1182 switch ($column) { 1183 1184 case 'fullname': 1185 // Check the full name display for sortable fields. 1186 $nameformat = $CFG->fullnamedisplay; 1187 if ($nameformat == 'language') { 1188 $nameformat = get_string('fullnamedisplay'); 1189 } 1190 $requirednames = order_in_string(get_all_user_name_fields(), $nameformat); 1191 1192 if (!empty($requirednames)) { 1193 if ($this->is_sortable($column)) { 1194 // Done this way for the possibility of more than two sortable full name display fields. 1195 $this->headers[$index] = ''; 1196 foreach ($requirednames as $name) { 1197 $sortname = $this->sort_link(get_string($name), 1198 $name, $primary_sort_column === $name, $primary_sort_order); 1199 $this->headers[$index] .= $sortname . ' / '; 1200 } 1201 $this->headers[$index] = substr($this->headers[$index], 0, -3); 1202 } 1203 } 1204 break; 1205 1206 case 'userpic': 1207 // do nothing, do not display sortable links 1208 break; 1209 1210 default: 1211 if ($this->is_sortable($column)) { 1212 $this->headers[$index] = $this->sort_link($this->headers[$index], 1213 $column, $primary_sort_column == $column, $primary_sort_order); 1214 } 1215 } 1216 1217 $attributes = array( 1218 'class' => 'header c' . $index . $this->column_class[$column], 1219 'scope' => 'col', 1220 ); 1221 if ($this->headers[$index] === NULL) { 1222 $content = ' '; 1223 } else if (!empty($this->sess->collapse[$column])) { 1224 $content = $icon_hide; 1225 } else { 1226 if (is_array($this->column_style[$column])) { 1227 $attributes['style'] = $this->make_styles_string($this->column_style[$column]); 1228 } 1229 $content = $this->headers[$index] . html_writer::tag('div', 1230 $icon_hide, array('class' => 'commands')); 1231 } 1232 echo html_writer::tag('th', $content, $attributes); 1233 } 1234 1235 echo html_writer::end_tag('tr'); 1236 echo html_writer::end_tag('thead'); 1237 } 1238 1239 /** 1240 * Generate the HTML for the sort icon. This is a helper method used by {@link sort_link()}. 1241 * @param bool $isprimary whether an icon is needed (it is only needed for the primary sort column.) 1242 * @param int $order SORT_ASC or SORT_DESC 1243 * @return string HTML fragment. 1244 */ 1245 protected function sort_icon($isprimary, $order) { 1246 global $OUTPUT; 1247 1248 if (!$isprimary) { 1249 return ''; 1250 } 1251 1252 if ($order == SORT_ASC) { 1253 return html_writer::empty_tag('img', 1254 array('src' => $OUTPUT->pix_url('t/sort_asc'), 'alt' => get_string('asc'), 'class' => 'iconsort')); 1255 } else { 1256 return html_writer::empty_tag('img', 1257 array('src' => $OUTPUT->pix_url('t/sort_desc'), 'alt' => get_string('desc'), 'class' => 'iconsort')); 1258 } 1259 } 1260 1261 /** 1262 * Generate the correct tool tip for changing the sort order. This is a 1263 * helper method used by {@link sort_link()}. 1264 * @param bool $isprimary whether the is column is the current primary sort column. 1265 * @param int $order SORT_ASC or SORT_DESC 1266 * @return string the correct title. 1267 */ 1268 protected function sort_order_name($isprimary, $order) { 1269 if ($isprimary && $order != SORT_ASC) { 1270 return get_string('desc'); 1271 } else { 1272 return get_string('asc'); 1273 } 1274 } 1275 1276 /** 1277 * Generate the HTML for the sort link. This is a helper method used by {@link print_headers()}. 1278 * @param string $text the text for the link. 1279 * @param string $column the column name, may be a fake column like 'firstname' or a real one. 1280 * @param bool $isprimary whether the is column is the current primary sort column. 1281 * @param int $order SORT_ASC or SORT_DESC 1282 * @return string HTML fragment. 1283 */ 1284 protected function sort_link($text, $column, $isprimary, $order) { 1285 return html_writer::link($this->baseurl->out(false, 1286 array($this->request[TABLE_VAR_SORT] => $column)), 1287 $text . get_accesshide(get_string('sortby') . ' ' . 1288 $text . ' ' . $this->sort_order_name($isprimary, $order))) . ' ' . 1289 $this->sort_icon($isprimary, $order); 1290 } 1291 1292 /** 1293 * This function is not part of the public api. 1294 */ 1295 function start_html() { 1296 global $OUTPUT; 1297 // Do we need to print initial bars? 1298 $this->print_initials_bar(); 1299 1300 // Paging bar 1301 if ($this->use_pages) { 1302 $pagingbar = new paging_bar($this->totalrows, $this->currpage, $this->pagesize, $this->baseurl); 1303 $pagingbar->pagevar = $this->request[TABLE_VAR_PAGE]; 1304 echo $OUTPUT->render($pagingbar); 1305 } 1306 1307 if (in_array(TABLE_P_TOP, $this->showdownloadbuttonsat)) { 1308 echo $this->download_buttons(); 1309 } 1310 1311 $this->wrap_html_start(); 1312 // Start of main data table 1313 1314 echo html_writer::start_tag('div', array('class' => 'no-overflow')); 1315 echo html_writer::start_tag('table', $this->attributes); 1316 1317 } 1318 1319 /** 1320 * This function is not part of the public api. 1321 * @param array $styles CSS-property => value 1322 * @return string values suitably to go in a style="" attribute in HTML. 1323 */ 1324 function make_styles_string($styles) { 1325 if (empty($styles)) { 1326 return null; 1327 } 1328 1329 $string = ''; 1330 foreach($styles as $property => $value) { 1331 $string .= $property . ':' . $value . ';'; 1332 } 1333 return $string; 1334 } 1335 } 1336 1337 1338 /** 1339 * @package moodlecore 1340 * @copyright 1999 onwards Martin Dougiamas {@link http://moodle.com} 1341 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 1342 */ 1343 class table_sql extends flexible_table { 1344 1345 public $countsql = NULL; 1346 public $countparams = NULL; 1347 /** 1348 * @var object sql for querying db. Has fields 'fields', 'from', 'where', 'params'. 1349 */ 1350 public $sql = NULL; 1351 /** 1352 * @var array Data fetched from the db. 1353 */ 1354 public $rawdata = NULL; 1355 1356 /** 1357 * @var bool Overriding default for this. 1358 */ 1359 public $is_sortable = true; 1360 /** 1361 * @var bool Overriding default for this. 1362 */ 1363 public $is_collapsible = true; 1364 1365 /** 1366 * @param string $uniqueid a string identifying this table.Used as a key in 1367 * session vars. 1368 */ 1369 function __construct($uniqueid) { 1370 parent::__construct($uniqueid); 1371 // some sensible defaults 1372 $this->set_attribute('cellspacing', '0'); 1373 $this->set_attribute('class', 'generaltable generalbox'); 1374 } 1375 1376 /** 1377 * Take the data returned from the db_query and go through all the rows 1378 * processing each col using either col_{columnname} method or other_cols 1379 * method or if other_cols returns NULL then put the data straight into the 1380 * table. 1381 */ 1382 function build_table() { 1383 if ($this->rawdata) { 1384 foreach ($this->rawdata as $row) { 1385 $formattedrow = $this->format_row($row); 1386 $this->add_data_keyed($formattedrow, 1387 $this->get_row_class($row)); 1388 } 1389 } 1390 } 1391 1392 /** 1393 * Get any extra classes names to add to this row in the HTML. 1394 * @param $row array the data for this row. 1395 * @return string added to the class="" attribute of the tr. 1396 */ 1397 function get_row_class($row) { 1398 return ''; 1399 } 1400 1401 /** 1402 * This is only needed if you want to use different sql to count rows. 1403 * Used for example when perhaps all db JOINS are not needed when counting 1404 * records. You don't need to call this function the count_sql 1405 * will be generated automatically. 1406 * 1407 * We need to count rows returned by the db seperately to the query itself 1408 * as we need to know how many pages of data we have to display. 1409 */ 1410 function set_count_sql($sql, array $params = NULL) { 1411 $this->countsql = $sql; 1412 $this->countparams = $params; 1413 } 1414 1415 /** 1416 * Set the sql to query the db. Query will be : 1417 * SELECT $fields FROM $from WHERE $where 1418 * Of course you can use sub-queries, JOINS etc. by putting them in the 1419 * appropriate clause of the query. 1420 */ 1421 function set_sql($fields, $from, $where, array $params = NULL) { 1422 $this->sql = new stdClass(); 1423 $this->sql->fields = $fields; 1424 $this->sql->from = $from; 1425 $this->sql->where = $where; 1426 $this->sql->params = $params; 1427 } 1428 1429 /** 1430 * Query the db. Store results in the table object for use by build_table. 1431 * 1432 * @param int $pagesize size of page for paginated displayed table. 1433 * @param bool $useinitialsbar do you want to use the initials bar. Bar 1434 * will only be used if there is a fullname column defined for the table. 1435 */ 1436 function query_db($pagesize, $useinitialsbar=true) { 1437 global $DB; 1438 if (!$this->is_downloading()) { 1439 if ($this->countsql === NULL) { 1440 $this->countsql = 'SELECT COUNT(1) FROM '.$this->sql->from.' WHERE '.$this->sql->where; 1441 $this->countparams = $this->sql->params; 1442 } 1443 $grandtotal = $DB->count_records_sql($this->countsql, $this->countparams); 1444 if ($useinitialsbar && !$this->is_downloading()) { 1445 $this->initialbars($grandtotal > $pagesize); 1446 } 1447 1448 list($wsql, $wparams) = $this->get_sql_where(); 1449 if ($wsql) { 1450 $this->countsql .= ' AND '.$wsql; 1451 $this->countparams = array_merge($this->countparams, $wparams); 1452 1453 $this->sql->where .= ' AND '.$wsql; 1454 $this->sql->params = array_merge($this->sql->params, $wparams); 1455 1456 $total = $DB->count_records_sql($this->countsql, $this->countparams); 1457 } else { 1458 $total = $grandtotal; 1459 } 1460 1461 $this->pagesize($pagesize, $total); 1462 } 1463 1464 // Fetch the attempts 1465 $sort = $this->get_sql_sort(); 1466 if ($sort) { 1467 $sort = "ORDER BY $sort"; 1468 } 1469 $sql = "SELECT 1470 {$this->sql->fields} 1471 FROM {$this->sql->from} 1472 WHERE {$this->sql->where} 1473 {$sort}"; 1474 1475 if (!$this->is_downloading()) { 1476 $this->rawdata = $DB->get_records_sql($sql, $this->sql->params, $this->get_page_start(), $this->get_page_size()); 1477 } else { 1478 $this->rawdata = $DB->get_records_sql($sql, $this->sql->params); 1479 } 1480 } 1481 1482 /** 1483 * Convenience method to call a number of methods for you to display the 1484 * table. 1485 */ 1486 function out($pagesize, $useinitialsbar, $downloadhelpbutton='') { 1487 global $DB; 1488 if (!$this->columns) { 1489 $onerow = $DB->get_record_sql("SELECT {$this->sql->fields} FROM {$this->sql->from} WHERE {$this->sql->where}", $this->sql->params); 1490 //if columns is not set then define columns as the keys of the rows returned 1491 //from the db. 1492 $this->define_columns(array_keys((array)$onerow)); 1493 $this->define_headers(array_keys((array)$onerow)); 1494 } 1495 $this->setup(); 1496 $this->query_db($pagesize, $useinitialsbar); 1497 $this->build_table(); 1498 $this->finish_output(); 1499 } 1500 } 1501 1502 1503 /** 1504 * @package moodlecore 1505 * @copyright 1999 onwards Martin Dougiamas {@link http://moodle.com} 1506 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 1507 */ 1508 class table_default_export_format_parent { 1509 /** 1510 * @var flexible_table or child class reference pointing to table class 1511 * object from which to export data. 1512 */ 1513 var $table; 1514 1515 /** 1516 * @var bool output started. Keeps track of whether any output has been 1517 * started yet. 1518 */ 1519 var $documentstarted = false; 1520 function table_default_export_format_parent(&$table) { 1521 $this->table =& $table; 1522 } 1523 1524 function set_table(&$table) { 1525 $this->table =& $table; 1526 } 1527 1528 function add_data($row) { 1529 return false; 1530 } 1531 1532 function add_seperator() { 1533 return false; 1534 } 1535 1536 function document_started() { 1537 return $this->documentstarted; 1538 } 1539 /** 1540 * Given text in a variety of format codings, this function returns 1541 * the text as safe HTML or as plain text dependent on what is appropriate 1542 * for the download format. The default removes all tags. 1543 */ 1544 function format_text($text, $format=FORMAT_MOODLE, $options=NULL, $courseid=NULL) { 1545 //use some whitespace to indicate where there was some line spacing. 1546 $text = str_replace(array('</p>', "\n", "\r"), ' ', $text); 1547 return strip_tags($text); 1548 } 1549 } 1550 1551 1552 /** 1553 * @package moodlecore 1554 * @copyright 1999 onwards Martin Dougiamas {@link http://moodle.com} 1555 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 1556 */ 1557 class table_spreadsheet_export_format_parent extends table_default_export_format_parent { 1558 var $currentrow; 1559 var $workbook; 1560 var $worksheet; 1561 /** 1562 * @var object format object - format for normal table cells 1563 */ 1564 var $formatnormal; 1565 /** 1566 * @var object format object - format for header table cells 1567 */ 1568 var $formatheaders; 1569 1570 /** 1571 * should be overriden in child class. 1572 */ 1573 var $fileextension; 1574 1575 /** 1576 * This method will be overridden in the child class. 1577 */ 1578 function define_workbook() { 1579 } 1580 1581 function start_document($filename) { 1582 $filename = $filename.'.'.$this->fileextension; 1583 $this->define_workbook(); 1584 // format types 1585 $this->formatnormal = $this->workbook->add_format(); 1586 $this->formatnormal->set_bold(0); 1587 $this->formatheaders = $this->workbook->add_format(); 1588 $this->formatheaders->set_bold(1); 1589 $this->formatheaders->set_align('center'); 1590 // Sending HTTP headers 1591 $this->workbook->send($filename); 1592 $this->documentstarted = true; 1593 } 1594 1595 function start_table($sheettitle) { 1596 $this->worksheet = $this->workbook->add_worksheet($sheettitle); 1597 $this->currentrow=0; 1598 } 1599 1600 function output_headers($headers) { 1601 $colnum = 0; 1602 foreach ($headers as $item) { 1603 $this->worksheet->write($this->currentrow,$colnum,$item,$this->formatheaders); 1604 $colnum++; 1605 } 1606 $this->currentrow++; 1607 } 1608 1609 function add_data($row) { 1610 $colnum = 0; 1611 foreach ($row as $item) { 1612 $this->worksheet->write($this->currentrow,$colnum,$item,$this->formatnormal); 1613 $colnum++; 1614 } 1615 $this->currentrow++; 1616 return true; 1617 } 1618 1619 function add_seperator() { 1620 $this->currentrow++; 1621 return true; 1622 } 1623 1624 function finish_table() { 1625 } 1626 1627 function finish_document() { 1628 $this->workbook->close(); 1629 exit; 1630 } 1631 } 1632 1633 1634 /** 1635 * @package moodlecore 1636 * @copyright 1999 onwards Martin Dougiamas {@link http://moodle.com} 1637 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 1638 */ 1639 class table_excel_export_format extends table_spreadsheet_export_format_parent { 1640 var $fileextension = 'xls'; 1641 1642 function define_workbook() { 1643 global $CFG; 1644 require_once("$CFG->libdir/excellib.class.php"); 1645 // Creating a workbook 1646 $this->workbook = new MoodleExcelWorkbook("-"); 1647 } 1648 1649 } 1650 1651 1652 /** 1653 * @package moodlecore 1654 * @copyright 1999 onwards Martin Dougiamas {@link http://moodle.com} 1655 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 1656 */ 1657 class table_ods_export_format extends table_spreadsheet_export_format_parent { 1658 var $fileextension = 'ods'; 1659 function define_workbook() { 1660 global $CFG; 1661 require_once("$CFG->libdir/odslib.class.php"); 1662 // Creating a workbook 1663 $this->workbook = new MoodleODSWorkbook("-"); 1664 } 1665 } 1666 1667 1668 /** 1669 * @package moodlecore 1670 * @copyright 1999 onwards Martin Dougiamas {@link http://moodle.com} 1671 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 1672 */ 1673 class table_text_export_format_parent extends table_default_export_format_parent { 1674 protected $seperator = "tab"; 1675 protected $mimetype = 'text/tab-separated-values'; 1676 protected $ext = '.txt'; 1677 protected $myexporter; 1678 1679 public function __construct() { 1680 $this->myexporter = new csv_export_writer($this->seperator, '"', $this->mimetype); 1681 } 1682 1683 public function start_document($filename) { 1684 $this->filename = $filename; 1685 $this->documentstarted = true; 1686 $this->myexporter->set_filename($filename, $this->ext); 1687 } 1688 1689 public function start_table($sheettitle) { 1690 //nothing to do here 1691 } 1692 1693 public function output_headers($headers) { 1694 $this->myexporter->add_data($headers); 1695 } 1696 1697 public function add_data($row) { 1698 $this->myexporter->add_data($row); 1699 return true; 1700 } 1701 1702 public function finish_table() { 1703 //nothing to do here 1704 } 1705 1706 public function finish_document() { 1707 $this->myexporter->download_file(); 1708 exit; 1709 } 1710 1711 /** 1712 * Format a row of data. 1713 * @param array $data 1714 */ 1715 protected function format_row($data) { 1716 $escapeddata = array(); 1717 foreach ($data as $value) { 1718 $escapeddata[] = '"' . str_replace('"', '""', $value) . '"'; 1719 } 1720 return implode($this->seperator, $escapeddata) . "\n"; 1721 } 1722 } 1723 1724 1725 /** 1726 * @package moodlecore 1727 * @copyright 1999 onwards Martin Dougiamas {@link http://moodle.com} 1728 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 1729 */ 1730 class table_tsv_export_format extends table_text_export_format_parent { 1731 protected $seperator = "tab"; 1732 protected $mimetype = 'text/tab-separated-values'; 1733 protected $ext = '.txt'; 1734 } 1735 1736 require_once($CFG->libdir . '/csvlib.class.php'); 1737 /** 1738 * @package moodlecore 1739 * @copyright 1999 onwards Martin Dougiamas {@link http://moodle.com} 1740 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 1741 */ 1742 class table_csv_export_format extends table_text_export_format_parent { 1743 protected $seperator = "comma"; 1744 protected $mimetype = 'text/csv'; 1745 protected $ext = '.csv'; 1746 } 1747 1748 /** 1749 * @package moodlecore 1750 * @copyright 1999 onwards Martin Dougiamas {@link http://moodle.com} 1751 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 1752 */ 1753 class table_xhtml_export_format extends table_default_export_format_parent { 1754 function start_document($filename) { 1755 header("Content-Type: application/download\n"); 1756 header("Content-Disposition: attachment; filename=\"$filename.html\""); 1757 header("Expires: 0"); 1758 header("Cache-Control: must-revalidate,post-check=0,pre-check=0"); 1759 header("Pragma: public"); 1760 //html headers 1761 echo <<<EOF 1762 <?xml version="1.0" encoding="UTF-8"?> 1763 <!DOCTYPE html 1764 PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 1765 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> 1766 1767 <html xmlns="http://www.w3.org/1999/xhtml" 1768 xml:lang="en" lang="en"> 1769 <head> 1770 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> 1771 <style type="text/css">/*<![CDATA[*/ 1772 1773 .flexible th { 1774 white-space:normal; 1775 } 1776 th.header, td.header, div.header { 1777 border-color:#DDDDDD; 1778 background-color:lightGrey; 1779 } 1780 .flexible th { 1781 white-space:nowrap; 1782 } 1783 th { 1784 font-weight:bold; 1785 } 1786 1787 .generaltable { 1788 border-style:solid; 1789 } 1790 .generalbox { 1791 border-style:solid; 1792 } 1793 body, table, td, th { 1794 font-family:Arial,Verdana,Helvetica,sans-serif; 1795 font-size:100%; 1796 } 1797 td { 1798 border-style:solid; 1799 border-width:1pt; 1800 } 1801 table { 1802 border-collapse:collapse; 1803 border-spacing:0pt; 1804 width:80%; 1805 margin:auto; 1806 } 1807 1808 h1, h2 { 1809 text-align:center; 1810 } 1811 .bold { 1812 font-weight:bold; 1813 } 1814 .mdl-align { 1815 text-align:center; 1816 } 1817 /*]]>*/</style> 1818 <title>$filename</title> 1819 </head> 1820 <body> 1821 EOF; 1822 $this->documentstarted = true; 1823 } 1824 1825 function start_table($sheettitle) { 1826 $this->table->sortable(false); 1827 $this->table->collapsible(false); 1828 echo "<h2>{$sheettitle}</h2>"; 1829 $this->table->start_html(); 1830 } 1831 1832 function output_headers($headers) { 1833 $this->table->print_headers(); 1834 echo html_writer::start_tag('tbody'); 1835 } 1836 1837 function add_data($row) { 1838 $this->table->print_row($row); 1839 return true; 1840 } 1841 1842 function add_seperator() { 1843 $this->table->print_row(NULL); 1844 return true; 1845 } 1846 1847 function finish_table() { 1848 $this->table->finish_html(); 1849 } 1850 1851 function finish_document() { 1852 echo "</body>\n</html>"; 1853 exit; 1854 } 1855 1856 function format_text($text, $format=FORMAT_MOODLE, $options=NULL, $courseid=NULL) { 1857 if (is_null($options)) { 1858 $options = new stdClass; 1859 } 1860 //some sensible defaults 1861 if (!isset($options->para)) { 1862 $options->para = false; 1863 } 1864 if (!isset($options->newlines)) { 1865 $options->newlines = false; 1866 } 1867 if (!isset($options->smiley)) { 1868 $options->smiley = false; 1869 } 1870 if (!isset($options->filter)) { 1871 $options->filter = false; 1872 } 1873 return format_text($text, $format, $options); 1874 } 1875 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Fri Nov 28 20:29:05 2014 | Cross-referenced by PHPXref 0.7.1 |