[ Index ]

PHP Cross Reference of moodle-2.8

title

Body

[close]

/lib/ -> tablelib.php (source)

   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 = '&nbsp;';
1063                      } else {
1064                          $content = $data;
1065                      }
1066                  } else {
1067                      $content = '&nbsp;';
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 = '&nbsp;';
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  }


Generated: Fri Nov 28 20:29:05 2014 Cross-referenced by PHPXref 0.7.1