[ Index ]

PHP Cross Reference of Moodle 1.9.3 [Build 15-Oct-2008]

title

Body

[close]

/lib/ -> formslib.php (source)

   1  <?php // $Id: formslib.php,v 1.129.2.17 2008/10/13 19:38:49 skodak Exp $
   2  /**
   3   * formslib.php - library of classes for creating forms in Moodle, based on PEAR QuickForms.
   4   *
   5   * To use formslib then you will want to create a new file purpose_form.php eg. edit_form.php
   6   * and you want to name your class something like {modulename}_{purpose}_form. Your class will
   7   * extend moodleform overriding abstract classes definition and optionally defintion_after_data
   8   * and validation.
   9   *
  10   * See examples of use of this library in course/edit.php and course/edit_form.php
  11   *
  12   * A few notes :
  13   *      form defintion is used for both printing of form and processing and should be the same
  14   *              for both or you may lose some submitted data which won't be let through.
  15   *      you should be using setType for every form element except select, radio or checkbox
  16   *              elements, these elements clean themselves.
  17   *
  18   *
  19   * @author  Jamie Pratt
  20   * @version $Id: formslib.php,v 1.129.2.17 2008/10/13 19:38:49 skodak Exp $
  21   * @license http://www.gnu.org/copyleft/gpl.html GNU Public License
  22   */
  23  
  24  //setup.php icludes our hacked pear libs first
  25  require_once 'HTML/QuickForm.php';
  26  require_once 'HTML/QuickForm/DHTMLRulesTableless.php';
  27  require_once 'HTML/QuickForm/Renderer/Tableless.php';
  28  
  29  require_once $CFG->libdir.'/uploadlib.php';
  30  
  31  /**
  32   * Callback called when PEAR throws an error
  33   *
  34   * @param PEAR_Error $error
  35   */
  36  function pear_handle_error($error){
  37      echo '<strong>'.$error->GetMessage().'</strong> '.$error->getUserInfo();
  38      echo '<br /> <strong>Backtrace </strong>:';
  39      print_object($error->backtrace);
  40  }
  41  
  42  if ($CFG->debug >= DEBUG_ALL){
  43      PEAR::setErrorHandling(PEAR_ERROR_CALLBACK, 'pear_handle_error');
  44  }
  45  
  46  
  47  /**
  48   * Moodle specific wrapper that separates quickforms syntax from moodle code. You won't directly
  49   * use this class you should write a class definition which extends this class or a more specific
  50   * subclass such a moodleform_mod for each form you want to display and/or process with formslib.
  51   *
  52   * You will write your own definition() method which performs the form set up.
  53   */
  54  class moodleform {
  55      var $_formname;       // form name
  56      /**
  57       * quickform object definition
  58       *
  59       * @var MoodleQuickForm
  60       */
  61      var $_form;
  62      /**
  63       * globals workaround
  64       *
  65       * @var array
  66       */
  67      var $_customdata;
  68      /**
  69       * file upload manager
  70       *
  71       * @var upload_manager
  72       */
  73      var $_upload_manager; //
  74      /**
  75       * definition_after_data executed flag
  76       * @var definition_finalized
  77       */
  78      var $_definition_finalized = false;
  79  
  80      /**
  81       * The constructor function calls the abstract function definition() and it will then
  82       * process and clean and attempt to validate incoming data.
  83       *
  84       * It will call your custom validate method to validate data and will also check any rules
  85       * you have specified in definition using addRule
  86       *
  87       * The name of the form (id attribute of the form) is automatically generated depending on
  88       * the name you gave the class extending moodleform. You should call your class something
  89       * like
  90       *
  91       * @param mixed $action the action attribute for the form. If empty defaults to auto detect the
  92       *                  current url. If a moodle_url object then outputs params as hidden variables.
  93       * @param array $customdata if your form defintion method needs access to data such as $course
  94       *               $cm, etc. to construct the form definition then pass it in this array. You can
  95       *               use globals for somethings.
  96       * @param string $method if you set this to anything other than 'post' then _GET and _POST will
  97       *               be merged and used as incoming data to the form.
  98       * @param string $target target frame for form submission. You will rarely use this. Don't use
  99       *                  it if you don't need to as the target attribute is deprecated in xhtml
 100       *                  strict.
 101       * @param mixed $attributes you can pass a string of html attributes here or an array.
 102       * @return moodleform
 103       */
 104      function moodleform($action=null, $customdata=null, $method='post', $target='', $attributes=null, $editable=true) {
 105          if (empty($action)){
 106              $action = strip_querystring(qualified_me());
 107          }
 108  
 109          $this->_formname = get_class($this); // '_form' suffix kept in order to prevent collisions of form id and other element
 110          $this->_customdata = $customdata;
 111          $this->_form =& new MoodleQuickForm($this->_formname, $method, $action, $target, $attributes);
 112          if (!$editable){
 113              $this->_form->hardFreeze();
 114          }
 115          $this->set_upload_manager(new upload_manager());
 116  
 117          $this->definition();
 118  
 119          $this->_form->addElement('hidden', 'sesskey', null); // automatic sesskey protection
 120          $this->_form->setDefault('sesskey', sesskey());
 121          $this->_form->addElement('hidden', '_qf__'.$this->_formname, null);   // form submission marker
 122          $this->_form->setDefault('_qf__'.$this->_formname, 1);
 123          $this->_form->_setDefaultRuleMessages();
 124  
 125          // we have to know all input types before processing submission ;-)
 126          $this->_process_submission($method);
 127      }
 128  
 129      /**
 130       * To autofocus on first form element or first element with error.
 131       *
 132       * @param string $name if this is set then the focus is forced to a field with this name
 133       *
 134       * @return string  javascript to select form element with first error or
 135       *                  first element if no errors. Use this as a parameter
 136       *                  when calling print_header
 137       */
 138      function focus($name=NULL) {
 139          $form =& $this->_form;
 140          $elkeys = array_keys($form->_elementIndex);
 141          $error = false;
 142          if (isset($form->_errors) &&  0 != count($form->_errors)){
 143              $errorkeys = array_keys($form->_errors);
 144              $elkeys = array_intersect($elkeys, $errorkeys);
 145              $error = true;
 146          }
 147  
 148          if ($error or empty($name)) {
 149              $names = array();
 150              while (empty($names) and !empty($elkeys)) {
 151                  $el = array_shift($elkeys);
 152                  $names = $form->_getElNamesRecursive($el);
 153              }
 154              if (!empty($names)) {
 155                  $name = array_shift($names);
 156              }
 157          }
 158  
 159          $focus = '';
 160          if (!empty($name)) {
 161              $focus = 'forms[\''.$form->getAttribute('id').'\'].elements[\''.$name.'\']';
 162          }
 163  
 164          return $focus;
 165       }
 166  
 167      /**
 168       * Internal method. Alters submitted data to be suitable for quickforms processing.
 169       * Must be called when the form is fully set up.
 170       */
 171      function _process_submission($method) {
 172          $submission = array();
 173          if ($method == 'post') {
 174              if (!empty($_POST)) {
 175                  $submission = $_POST;
 176              }
 177          } else {
 178              $submission = array_merge_recursive($_GET, $_POST); // emulate handling of parameters in xxxx_param()
 179          }
 180  
 181          // following trick is needed to enable proper sesskey checks when using GET forms
 182          // the _qf__.$this->_formname serves as a marker that form was actually submitted
 183          if (array_key_exists('_qf__'.$this->_formname, $submission) and $submission['_qf__'.$this->_formname] == 1) {
 184              if (!confirm_sesskey()) {
 185                  print_error('invalidsesskey');
 186              }
 187              $files = $_FILES;
 188          } else {
 189              $submission = array();
 190              $files = array();
 191          }
 192  
 193          $this->_form->updateSubmission($submission, $files);
 194      }
 195  
 196      /**
 197       * Internal method. Validates all uploaded files.
 198       */
 199      function _validate_files(&$files) {
 200          $files = array();
 201  
 202          if (empty($_FILES)) {
 203              // we do not need to do any checks because no files were submitted
 204              // note: server side rules do not work for files - use custom verification in validate() instead
 205              return true;
 206          }
 207          $errors = array();
 208          $mform =& $this->_form;
 209  
 210          // check the files
 211          $status = $this->_upload_manager->preprocess_files();
 212  
 213          // now check that we really want each file
 214          foreach ($_FILES as $elname=>$file) {
 215              if ($mform->elementExists($elname) and $mform->getElementType($elname)=='file') {
 216                  $required = $mform->isElementRequired($elname);
 217                  if (!empty($this->_upload_manager->files[$elname]['uploadlog']) and empty($this->_upload_manager->files[$elname]['clear'])) {
 218                      if (!$required and $file['error'] == UPLOAD_ERR_NO_FILE) {
 219                          // file not uploaded and not required - ignore it
 220                          continue;
 221                      }
 222                      $errors[$elname] = $this->_upload_manager->files[$elname]['uploadlog'];
 223  
 224                  } else if (!empty($this->_upload_manager->files[$elname]['clear'])) {
 225                      $files[$elname] = $this->_upload_manager->files[$elname]['tmp_name'];
 226                  }
 227              } else {
 228                  error('Incorrect upload attempt!');
 229              }
 230          }
 231  
 232          // return errors if found
 233          if ($status and 0 == count($errors)){
 234              return true;
 235  
 236          } else {
 237              $files = array();
 238              return $errors;
 239          }
 240      }
 241  
 242      /**
 243       * Load in existing data as form defaults. Usually new entry defaults are stored directly in
 244       * form definition (new entry form); this function is used to load in data where values
 245       * already exist and data is being edited (edit entry form).
 246       *
 247       * @param mixed $default_values object or array of default values
 248       * @param bool $slased true if magic quotes applied to data values
 249       */
 250      function set_data($default_values, $slashed=false) {
 251          if (is_object($default_values)) {
 252              $default_values = (array)$default_values;
 253          }
 254          $filter = $slashed ? 'stripslashes' : NULL;
 255          $this->_form->setDefaults($default_values, $filter);
 256      }
 257  
 258      /**
 259       * Set custom upload manager.
 260       * Must be used BEFORE creating of file element!
 261       *
 262       * @param object $um - custom upload manager
 263       */
 264      function set_upload_manager($um=false) {
 265          if ($um === false) {
 266              $um = new upload_manager();
 267          }
 268          $this->_upload_manager = $um;
 269  
 270          $this->_form->setMaxFileSize($um->config->maxbytes);
 271      }
 272  
 273      /**
 274       * Check that form was submitted. Does not check validity of submitted data.
 275       *
 276       * @return bool true if form properly submitted
 277       */
 278      function is_submitted() {
 279          return $this->_form->isSubmitted();
 280      }
 281  
 282      function no_submit_button_pressed(){
 283          static $nosubmit = null; // one check is enough
 284          if (!is_null($nosubmit)){
 285              return $nosubmit;
 286          }
 287          $mform =& $this->_form;
 288          $nosubmit = false;
 289          if (!$this->is_submitted()){
 290              return false;
 291          }
 292          foreach ($mform->_noSubmitButtons as $nosubmitbutton){
 293              if (optional_param($nosubmitbutton, 0, PARAM_RAW)){
 294                  $nosubmit = true;
 295                  break;
 296              }
 297          }
 298          return $nosubmit;
 299      }
 300  
 301  
 302      /**
 303       * Check that form data is valid.
 304       *
 305       * @return bool true if form data valid
 306       */
 307      function is_validated() {
 308          static $validated = null; // one validation is enough
 309          $mform =& $this->_form;
 310  
 311          //finalize the form definition before any processing
 312          if (!$this->_definition_finalized) {
 313              $this->_definition_finalized = true;
 314              $this->definition_after_data();
 315          }
 316  
 317          if ($this->no_submit_button_pressed()){
 318              return false;
 319          } elseif ($validated === null) {
 320              $internal_val = $mform->validate();
 321  
 322              $files = array();
 323              $file_val = $this->_validate_files($files);
 324              if ($file_val !== true) {
 325                  if (!empty($file_val)) {
 326                      foreach ($file_val as $element=>$msg) {
 327                          $mform->setElementError($element, $msg);
 328                      }
 329                  }
 330                  $file_val = false;
 331              }
 332  
 333              $data = $mform->exportValues(null, true);
 334              $moodle_val = $this->validation($data, $files);
 335              if ((is_array($moodle_val) && count($moodle_val)!==0)) {
 336                  // non-empty array means errors
 337                  foreach ($moodle_val as $element=>$msg) {
 338                      $mform->setElementError($element, $msg);
 339                  }
 340                  $moodle_val = false;
 341  
 342              } else {
 343                  // anything else means validation ok
 344                  $moodle_val = true;
 345              }
 346  
 347              $validated = ($internal_val and $moodle_val and $file_val);
 348          }
 349          return $validated;
 350      }
 351  
 352      /**
 353       * Return true if a cancel button has been pressed resulting in the form being submitted.
 354       *
 355       * @return boolean true if a cancel button has been pressed
 356       */
 357      function is_cancelled(){
 358          $mform =& $this->_form;
 359          if ($mform->isSubmitted()){
 360              foreach ($mform->_cancelButtons as $cancelbutton){
 361                  if (optional_param($cancelbutton, 0, PARAM_RAW)){
 362                      return true;
 363                  }
 364              }
 365          }
 366          return false;
 367      }
 368  
 369      /**
 370       * Return submitted data if properly submitted or returns NULL if validation fails or
 371       * if there is no submitted data.
 372       *
 373       * @param bool $slashed true means return data with addslashes applied
 374       * @return object submitted data; NULL if not valid or not submitted
 375       */
 376      function get_data($slashed=true) {
 377          $mform =& $this->_form;
 378  
 379          if ($this->is_submitted() and $this->is_validated()) {
 380              $data = $mform->exportValues(null, $slashed);
 381              unset($data['sesskey']); // we do not need to return sesskey
 382              unset($data['_qf__'.$this->_formname]);   // we do not need the submission marker too
 383              if (empty($data)) {
 384                  return NULL;
 385              } else {
 386                  return (object)$data;
 387              }
 388          } else {
 389              return NULL;
 390          }
 391      }
 392  
 393      /**
 394       * Return submitted data without validation or NULL if there is no submitted data.
 395       *
 396       * @param bool $slashed true means return data with addslashes applied
 397       * @return object submitted data; NULL if not submitted
 398       */
 399      function get_submitted_data($slashed=true) {
 400          $mform =& $this->_form;
 401  
 402          if ($this->is_submitted()) {
 403              $data = $mform->exportValues(null, $slashed);
 404              unset($data['sesskey']); // we do not need to return sesskey
 405              unset($data['_qf__'.$this->_formname]);   // we do not need the submission marker too
 406              if (empty($data)) {
 407                  return NULL;
 408              } else {
 409                  return (object)$data;
 410              }
 411          } else {
 412              return NULL;
 413          }
 414      }
 415  
 416      /**
 417       * Save verified uploaded files into directory. Upload process can be customised from definition()
 418       * method by creating instance of upload manager and storing it in $this->_upload_form
 419       *
 420       * @param string $destination where to store uploaded files
 421       * @return bool success
 422       */
 423      function save_files($destination) {
 424          if ($this->is_submitted() and $this->is_validated()) {
 425              return $this->_upload_manager->save_files($destination);
 426          }
 427          return false;
 428      }
 429  
 430      /**
 431       * If we're only handling one file (if inputname was given in the constructor)
 432       * this will return the (possibly changed) filename of the file.
 433       * @return mixed false in case of failure, string if ok
 434       */
 435      function get_new_filename() {
 436          return $this->_upload_manager->get_new_filename();
 437      }
 438  
 439      /**
 440       * Get content of uploaded file.
 441       * @param $element name of file upload element
 442       * @return mixed false in case of failure, string if ok
 443       */
 444      function get_file_content($elname) {
 445          if (!$this->is_submitted() or !$this->is_validated()) {
 446              return false;
 447          }
 448  
 449          if (!$this->_form->elementExists($elname)) {
 450              return false;
 451          }
 452  
 453          if (empty($this->_upload_manager->files[$elname]['clear'])) {
 454              return false;
 455          }        
 456  
 457          if (empty($this->_upload_manager->files[$elname]['tmp_name'])) {
 458              return false;
 459          }
 460  
 461          $data = "";
 462          $file = @fopen($this->_upload_manager->files[$elname]['tmp_name'], "rb");
 463          if ($file) {
 464              while (!feof($file)) {
 465                  $data .= fread($file, 1024); // TODO: do we really have to do this?
 466              }
 467              fclose($file);
 468              return $data;
 469          } else {
 470              return false;
 471          }
 472      }
 473  
 474      /**
 475       * Print html form.
 476       */
 477      function display() {
 478          //finalize the form definition if not yet done
 479          if (!$this->_definition_finalized) {
 480              $this->_definition_finalized = true;
 481              $this->definition_after_data();
 482          }
 483          $this->_form->display();
 484      }
 485  
 486      /**
 487       * Abstract method - always override!
 488       *
 489       * If you need special handling of uploaded files, create instance of $this->_upload_manager here.
 490       */
 491      function definition() {
 492          error('Abstract form_definition() method in class '.get_class($this).' must be overriden, please fix the code.');
 493      }
 494  
 495      /**
 496       * Dummy stub method - override if you need to setup the form depending on current
 497       * values. This method is called after definition(), data submission and set_data().
 498       * All form setup that is dependent on form values should go in here.
 499       */
 500      function definition_after_data(){
 501      }
 502  
 503      /**
 504       * Dummy stub method - override if you needed to perform some extra validation.
 505       * If there are errors return array of errors ("fieldname"=>"error message"),
 506       * otherwise true if ok.
 507       *
 508       * Server side rules do not work for uploaded files, implement serverside rules here if needed.
 509       *
 510       * @param array $data array of ("fieldname"=>value) of submitted data
 511       * @param array $files array of uploaded files "element_name"=>tmp_file_path
 512       * @return array of "element_name"=>"error_description" if there are errors,
 513       *               or an empty array if everything is OK (true allowed for backwards compatibility too).
 514       */
 515      function validation($data, $files) {
 516          return array();
 517      }
 518  
 519      /**
 520       * Method to add a repeating group of elements to a form.
 521       *
 522       * @param array $elementobjs Array of elements or groups of elements that are to be repeated
 523       * @param integer $repeats no of times to repeat elements initially
 524       * @param array $options Array of options to apply to elements. Array keys are element names.
 525       *                      This is an array of arrays. The second sets of keys are the option types
 526       *                      for the elements :
 527       *                          'default' - default value is value
 528       *                          'type' - PARAM_* constant is value
 529       *                          'helpbutton' - helpbutton params array is value
 530       *                          'disabledif' - last three moodleform::disabledIf()
 531       *                                           params are value as an array
 532       * @param string $repeathiddenname name for hidden element storing no of repeats in this form
 533       * @param string $addfieldsname name for button to add more fields
 534       * @param int $addfieldsno how many fields to add at a time
 535       * @param string $addstring name of button, {no} is replaced by no of blanks that will be added.
 536       * @param boolean $addbuttoninside if true, don't call closeHeaderBefore($addfieldsname). Default false.
 537       * @return int no of repeats of element in this page
 538       */
 539      function repeat_elements($elementobjs, $repeats, $options, $repeathiddenname,
 540              $addfieldsname, $addfieldsno=5, $addstring=null, $addbuttoninside=false){
 541          if ($addstring===null){
 542              $addstring = get_string('addfields', 'form', $addfieldsno);
 543          } else {
 544              $addstring = str_ireplace('{no}', $addfieldsno, $addstring);
 545          }
 546          $repeats = optional_param($repeathiddenname, $repeats, PARAM_INT);
 547          $addfields = optional_param($addfieldsname, '', PARAM_TEXT);
 548          if (!empty($addfields)){
 549              $repeats += $addfieldsno;
 550          }
 551          $mform =& $this->_form;
 552          $mform->registerNoSubmitButton($addfieldsname);
 553          $mform->addElement('hidden', $repeathiddenname, $repeats);
 554          //value not to be overridden by submitted value
 555          $mform->setConstants(array($repeathiddenname=>$repeats));
 556          for ($i=0; $i<$repeats; $i++) {
 557              foreach ($elementobjs as $elementobj){
 558                  $elementclone = fullclone($elementobj);
 559                  $name = $elementclone->getName();
 560                  if (!empty($name)){
 561                      $elementclone->setName($name."[$i]");
 562                  }
 563                  if (is_a($elementclone, 'HTML_QuickForm_header')){
 564                      $value=$elementclone->_text;
 565                      $elementclone->setValue(str_replace('{no}', ($i+1), $value));
 566  
 567                  } else {
 568                      $value=$elementclone->getLabel();
 569                      $elementclone->setLabel(str_replace('{no}', ($i+1), $value));
 570  
 571                  }
 572  
 573                  $mform->addElement($elementclone);
 574              }
 575          }
 576          for ($i=0; $i<$repeats; $i++) {
 577              foreach ($options as $elementname => $elementoptions){
 578                  $pos=strpos($elementname, '[');
 579                  if ($pos!==FALSE){
 580                      $realelementname = substr($elementname, 0, $pos+1)."[$i]";
 581                      $realelementname .= substr($elementname, $pos+1);
 582                  }else {
 583                      $realelementname = $elementname."[$i]";
 584                  }
 585                  foreach ($elementoptions as  $option => $params){
 586  
 587                      switch ($option){
 588                          case 'default' :
 589                              $mform->setDefault($realelementname, $params);
 590                              break;
 591                          case 'helpbutton' :
 592                              $mform->setHelpButton($realelementname, $params);
 593                              break;
 594                          case 'disabledif' :
 595                              $params = array_merge(array($realelementname), $params);
 596                              call_user_func_array(array(&$mform, 'disabledIf'), $params);
 597                              break;
 598                          case 'rule' :
 599                              if (is_string($params)){
 600                                  $params = array(null, $params, null, 'client');
 601                              }
 602                              $params = array_merge(array($realelementname), $params);
 603                              call_user_func_array(array(&$mform, 'addRule'), $params);
 604                              break;
 605  
 606                      }
 607                  }
 608              }
 609          }
 610          $mform->addElement('submit', $addfieldsname, $addstring);
 611  
 612          if (!$addbuttoninside) {
 613              $mform->closeHeaderBefore($addfieldsname);
 614          }
 615  
 616          return $repeats;
 617      }
 618  
 619      /**
 620       * Adds a link/button that controls the checked state of a group of checkboxes.
 621       * @param int    $groupid The id of the group of advcheckboxes this element controls
 622       * @param string $text The text of the link. Defaults to "select all/none"
 623       * @param array  $attributes associative array of HTML attributes
 624       * @param int    $originalValue The original general state of the checkboxes before the user first clicks this element
 625       */
 626      function add_checkbox_controller($groupid, $buttontext, $attributes, $originalValue = 0) { 
 627          global $CFG;
 628          if (empty($text)) {
 629              $text = get_string('selectallornone', 'form');
 630          }
 631  
 632          $mform = $this->_form;
 633          $select_value = optional_param('checkbox_controller'. $groupid, null, PARAM_INT);
 634  
 635          if ($select_value == 0 || is_null($select_value)) {
 636              $new_select_value = 1;
 637          } else {
 638              $new_select_value = 0;
 639          }
 640  
 641          $mform->addElement('hidden', "checkbox_controller$groupid");
 642          $mform->setConstants(array("checkbox_controller$groupid" => $new_select_value));
 643          
 644          // Locate all checkboxes for this group and set their value, IF the optional param was given
 645          if (!is_null($select_value)) {
 646              foreach ($this->_form->_elements as $element) {
 647                  if ($element->getAttribute('class') == "checkboxgroup$groupid") {
 648                      $mform->setConstants(array($element->getAttribute('name') => $select_value));
 649                  }
 650              }
 651          }
 652  
 653          $checkbox_controller_name = 'nosubmit_checkbox_controller' . $groupid;
 654          $mform->registerNoSubmitButton($checkbox_controller_name);
 655          
 656          // Prepare Javascript for submit element
 657          $js = "\n//<![CDATA[\n";
 658          if (!defined('HTML_QUICKFORM_CHECKBOXCONTROLLER_EXISTS')) {
 659              $js .= <<<EOS
 660  function html_quickform_toggle_checkboxes(group) {
 661      var checkboxes = getElementsByClassName(document, 'input', 'checkboxgroup' + group);
 662      var newvalue = false;
 663      var global = eval('html_quickform_checkboxgroup' + group + ';');
 664      if (global == 1) {
 665          eval('html_quickform_checkboxgroup' + group + ' = 0;'); 
 666          newvalue = '';
 667      } else {
 668          eval('html_quickform_checkboxgroup' + group + ' = 1;'); 
 669          newvalue = 'checked';
 670      }
 671  
 672      for (i = 0; i < checkboxes.length; i++) {
 673          checkboxes[i].checked = newvalue; 
 674      }
 675  }
 676  EOS;
 677              define('HTML_QUICKFORM_CHECKBOXCONTROLLER_EXISTS', true);
 678          }
 679          $js .= "\nvar html_quickform_checkboxgroup$groupid=$originalValue;\n";
 680          
 681          $js .= "//]]>\n";
 682          
 683          require_once("$CFG->libdir/form/submitlink.php");
 684          $submitlink = new MoodleQuickForm_submitlink($checkbox_controller_name, $attributes);
 685          $submitlink->_js = $js;
 686          $submitlink->_onclick = "html_quickform_toggle_checkboxes($groupid); return false;";
 687          $mform->addElement($submitlink); 
 688          $mform->setDefault($checkbox_controller_name, $text);
 689      }
 690  
 691      /**
 692       * Use this method to a cancel and submit button to the end of your form. Pass a param of false
 693       * if you don't want a cancel button in your form. If you have a cancel button make sure you
 694       * check for it being pressed using is_cancelled() and redirecting if it is true before trying to
 695       * get data with get_data().
 696       *
 697       * @param boolean $cancel whether to show cancel button, default true
 698       * @param string $submitlabel label for submit button, defaults to get_string('savechanges')
 699       */
 700      function add_action_buttons($cancel = true, $submitlabel=null){
 701          if (is_null($submitlabel)){
 702              $submitlabel = get_string('savechanges');
 703          }
 704          $mform =& $this->_form;
 705          if ($cancel){
 706              //when two elements we need a group
 707              $buttonarray=array();
 708              $buttonarray[] = &$mform->createElement('submit', 'submitbutton', $submitlabel);
 709              $buttonarray[] = &$mform->createElement('cancel');
 710              $mform->addGroup($buttonarray, 'buttonar', '', array(' '), false);
 711              $mform->closeHeaderBefore('buttonar');
 712          } else {
 713              //no group needed
 714              $mform->addElement('submit', 'submitbutton', $submitlabel);
 715              $mform->closeHeaderBefore('submitbutton');
 716          }
 717      }
 718  }
 719  
 720  /**
 721   * You never extend this class directly. The class methods of this class are available from
 722   * the private $this->_form property on moodleform and its children. You generally only
 723   * call methods on this class from within abstract methods that you override on moodleform such
 724   * as definition and definition_after_data
 725   *
 726   */
 727  class MoodleQuickForm extends HTML_QuickForm_DHTMLRulesTableless {
 728      var $_types = array();
 729      var $_dependencies = array();
 730      /**
 731       * Array of buttons that if pressed do not result in the processing of the form.
 732       *
 733       * @var array
 734       */
 735      var $_noSubmitButtons=array();
 736      /**
 737       * Array of buttons that if pressed do not result in the processing of the form.
 738       *
 739       * @var array
 740       */
 741      var $_cancelButtons=array();
 742  
 743      /**
 744       * Array whose keys are element names. If the key exists this is a advanced element
 745       *
 746       * @var array
 747       */
 748      var $_advancedElements = array();
 749  
 750      /**
 751       * Whether to display advanced elements (on page load)
 752       *
 753       * @var boolean
 754       */
 755      var $_showAdvanced = null;
 756  
 757      /**
 758       * The form name is derrived from the class name of the wrapper minus the trailing form
 759       * It is a name with words joined by underscores whereas the id attribute is words joined by
 760       * underscores.
 761       *
 762       * @var unknown_type
 763       */
 764      var $_formName = '';
 765  
 766      /**
 767       * String with the html for hidden params passed in as part of a moodle_url object for the action. Output in the form.
 768       *
 769       * @var string
 770       */
 771      var $_pageparams = '';
 772  
 773      /**
 774       * Class constructor - same parameters as HTML_QuickForm_DHTMLRulesTableless
 775       * @param    string      $formName          Form's name.
 776       * @param    string      $method            (optional)Form's method defaults to 'POST'
 777       * @param    mixed      $action             (optional)Form's action - string or moodle_url
 778       * @param    string      $target            (optional)Form's target defaults to none
 779       * @param    mixed       $attributes        (optional)Extra attributes for <form> tag
 780       * @param    bool        $trackSubmit       (optional)Whether to track if the form was submitted by adding a special hidden field
 781       * @access   public
 782       */
 783      function MoodleQuickForm($formName, $method, $action, $target='', $attributes=null){
 784          global $CFG;
 785  
 786          static $formcounter = 1;
 787  
 788          HTML_Common::HTML_Common($attributes);
 789          $target = empty($target) ? array() : array('target' => $target);
 790          $this->_formName = $formName;
 791          if (is_a($action, 'moodle_url')){
 792              $this->_pageparams = $action->hidden_params_out();
 793              $action = $action->out(true);
 794          } else {
 795              $this->_pageparams = '';
 796          }
 797          //no 'name' atttribute for form in xhtml strict :
 798          $attributes = array('action'=>$action, 'method'=>$method, 'id'=>'mform'.$formcounter) + $target;
 799          $formcounter++;
 800          $this->updateAttributes($attributes);
 801  
 802          //this is custom stuff for Moodle :
 803          $oldclass=   $this->getAttribute('class');
 804          if (!empty($oldclass)){
 805              $this->updateAttributes(array('class'=>$oldclass.' mform'));
 806          }else {
 807              $this->updateAttributes(array('class'=>'mform'));
 808          }
 809          $this->_reqHTML = '<img class="req" title="'.get_string('requiredelement', 'form').'" alt="'.get_string('requiredelement', 'form').'" src="'.$CFG->pixpath.'/req.gif'.'" />';
 810          $this->_advancedHTML = '<img class="adv" title="'.get_string('advancedelement', 'form').'" alt="'.get_string('advancedelement', 'form').'" src="'.$CFG->pixpath.'/adv.gif'.'" />';
 811          $this->setRequiredNote(get_string('somefieldsrequired', 'form', '<img alt="'.get_string('requiredelement', 'form').'" src="'.$CFG->pixpath.'/req.gif'.'" />'));
 812          //(Help file doesn't add anything) helpbutton('requiredelement', get_string('requiredelement', 'form'), 'moodle', true, false, '', true));
 813      }
 814  
 815      /**
 816       * Use this method to indicate an element in a form is an advanced field. If items in a form
 817       * are marked as advanced then 'Hide/Show Advanced' buttons will automatically be displayed in the
 818       * form so the user can decide whether to display advanced form controls.
 819       *
 820       * If you set a header element to advanced then all elements it contains will also be set as advanced.
 821       *
 822       * @param string $elementName group or element name (not the element name of something inside a group).
 823       * @param boolean $advanced default true sets the element to advanced. False removes advanced mark.
 824       */
 825      function setAdvanced($elementName, $advanced=true){
 826          if ($advanced){
 827              $this->_advancedElements[$elementName]='';
 828          } elseif (isset($this->_advancedElements[$elementName])) {
 829              unset($this->_advancedElements[$elementName]);
 830          }
 831          if ($advanced && $this->getElementType('mform_showadvanced_last')===false){
 832              $this->setShowAdvanced();
 833              $this->registerNoSubmitButton('mform_showadvanced');
 834  
 835              $this->addElement('hidden', 'mform_showadvanced_last');
 836          }
 837      }
 838      /**
 839       * Set whether to show advanced elements in the form on first displaying form. Default is not to
 840       * display advanced elements in the form until 'Show Advanced' is pressed.
 841       *
 842       * You can get the last state of the form and possibly save it for this user by using
 843       * value 'mform_showadvanced_last' in submitted data.
 844       *
 845       * @param boolean $showadvancedNow
 846       */
 847      function setShowAdvanced($showadvancedNow = null){
 848          if ($showadvancedNow === null){
 849              if ($this->_showAdvanced !== null){
 850                  return;
 851              } else { //if setShowAdvanced is called without any preference
 852                       //make the default to not show advanced elements.
 853                  $showadvancedNow = get_user_preferences(
 854                                  moodle_strtolower($this->_formName.'_showadvanced', 0));
 855              }
 856          }
 857          //value of hidden element
 858          $hiddenLast = optional_param('mform_showadvanced_last', -1, PARAM_INT);
 859          //value of button
 860          $buttonPressed = optional_param('mform_showadvanced', 0, PARAM_RAW);
 861          //toggle if button pressed or else stay the same
 862          if ($hiddenLast == -1) {
 863              $next = $showadvancedNow;
 864          } elseif ($buttonPressed) { //toggle on button press
 865              $next = !$hiddenLast;
 866          } else {
 867              $next = $hiddenLast;
 868          }
 869          $this->_showAdvanced = $next;
 870          if ($showadvancedNow != $next){
 871              set_user_preference($this->_formName.'_showadvanced', $next);
 872          }
 873          $this->setConstants(array('mform_showadvanced_last'=>$next));
 874      }
 875      function getShowAdvanced(){
 876          return $this->_showAdvanced;
 877      }
 878  
 879  
 880     /**
 881      * Accepts a renderer
 882      *
 883      * @param HTML_QuickForm_Renderer  An HTML_QuickForm_Renderer object
 884      * @since 3.0
 885      * @access public
 886      * @return void
 887      */
 888      function accept(&$renderer) {
 889          if (method_exists($renderer, 'setAdvancedElements')){
 890              //check for visible fieldsets where all elements are advanced
 891              //and mark these headers as advanced as well.
 892              //And mark all elements in a advanced header as advanced
 893              $stopFields = $renderer->getStopFieldSetElements();
 894              $lastHeader = null;
 895              $lastHeaderAdvanced = false;
 896              $anyAdvanced = false;
 897              foreach (array_keys($this->_elements) as $elementIndex){
 898                  $element =& $this->_elements[$elementIndex];
 899  
 900                  // if closing header and any contained element was advanced then mark it as advanced
 901                  if ($element->getType()=='header' || in_array($element->getName(), $stopFields)){
 902                      if ($anyAdvanced && !is_null($lastHeader)){
 903                          $this->setAdvanced($lastHeader->getName());
 904                      }
 905                      $lastHeaderAdvanced = false;
 906                      unset($lastHeader);
 907                      $lastHeader = null;
 908                  } elseif ($lastHeaderAdvanced) {
 909                      $this->setAdvanced($element->getName());
 910                  }
 911  
 912                  if ($element->getType()=='header'){
 913                      $lastHeader =& $element;
 914                      $anyAdvanced = false;
 915                      $lastHeaderAdvanced = isset($this->_advancedElements[$element->getName()]);
 916                  } elseif (isset($this->_advancedElements[$element->getName()])){
 917                      $anyAdvanced = true;
 918                  }
 919              }
 920              // the last header may not be closed yet...
 921              if ($anyAdvanced && !is_null($lastHeader)){
 922                  $this->setAdvanced($lastHeader->getName());
 923              }
 924              $renderer->setAdvancedElements($this->_advancedElements);
 925  
 926          }
 927          parent::accept($renderer);
 928      }
 929  
 930  
 931  
 932      function closeHeaderBefore($elementName){
 933          $renderer =& $this->defaultRenderer();
 934          $renderer->addStopFieldsetElements($elementName);
 935      }
 936  
 937      /**
 938       * Should be used for all elements of a form except for select, radio and checkboxes which
 939       * clean their own data.
 940       *
 941       * @param string $elementname
 942       * @param integer $paramtype use the constants PARAM_*.
 943       *     *  PARAM_CLEAN is deprecated and you should try to use a more specific type.
 944       *     *  PARAM_TEXT should be used for cleaning data that is expected to be plain text.
 945       *          It will strip all html tags. But will still let tags for multilang support
 946       *          through.
 947       *     *  PARAM_RAW means no cleaning whatsoever, it is used mostly for data from the
 948       *          html editor. Data from the editor is later cleaned before display using
 949       *          format_text() function. PARAM_RAW can also be used for data that is validated
 950       *          by some other way or printed by p() or s().
 951       *     *  PARAM_INT should be used for integers.
 952       *     *  PARAM_ACTION is an alias of PARAM_ALPHA and is used for hidden fields specifying
 953       *          form actions.
 954       */
 955      function setType($elementname, $paramtype) {
 956          $this->_types[$elementname] = $paramtype;
 957      }
 958  
 959      /**
 960       * See description of setType above. This can be used to set several types at once.
 961       *
 962       * @param array $paramtypes
 963       */
 964      function setTypes($paramtypes) {
 965          $this->_types = $paramtypes + $this->_types;
 966      }
 967  
 968      function updateSubmission($submission, $files) {
 969          $this->_flagSubmitted = false;
 970  
 971          if (empty($submission)) {
 972              $this->_submitValues = array();
 973          } else {
 974              foreach ($submission as $key=>$s) {
 975                  if (array_key_exists($key, $this->_types)) {
 976                      $submission[$key] = clean_param($s, $this->_types[$key]);
 977                  }
 978              }
 979              $this->_submitValues = $this->_recursiveFilter('stripslashes', $submission);
 980              $this->_flagSubmitted = true;
 981          }
 982  
 983          if (empty($files)) {
 984              $this->_submitFiles = array();
 985          } else {
 986              if (1 == get_magic_quotes_gpc()) {
 987                  foreach (array_keys($files) as $elname) {
 988                      // dangerous characters in filenames are cleaned later in upload_manager
 989                      $files[$elname]['name'] = stripslashes($files[$elname]['name']);
 990                  }
 991              }
 992              $this->_submitFiles = $files;
 993              $this->_flagSubmitted = true;
 994          }
 995  
 996          // need to tell all elements that they need to update their value attribute.
 997           foreach (array_keys($this->_elements) as $key) {
 998               $this->_elements[$key]->onQuickFormEvent('updateValue', null, $this);
 999           }
1000      }
1001  
1002      function getReqHTML(){
1003          return $this->_reqHTML;
1004      }
1005  
1006      function getAdvancedHTML(){
1007          return $this->_advancedHTML;
1008      }
1009  
1010      /**
1011       * Initializes a default form value. Used to specify the default for a new entry where
1012       * no data is loaded in using moodleform::set_data()
1013       *
1014       * @param     string   $elementname        element name
1015       * @param     mixed    $values             values for that element name
1016       * @param     bool     $slashed            the default value is slashed
1017       * @access    public
1018       * @return    void
1019       */
1020      function setDefault($elementName, $defaultValue, $slashed=false){
1021          $filter = $slashed ? 'stripslashes' : NULL;
1022          $this->setDefaults(array($elementName=>$defaultValue), $filter);
1023      } // end func setDefault
1024      /**
1025       * Add an array of buttons to the form
1026       * @param    array       $buttons          An associative array representing help button to attach to
1027       *                                          to the form. keys of array correspond to names of elements in form.
1028       *
1029       * @access   public
1030      */
1031      function setHelpButtons($buttons, $suppresscheck=false, $function='helpbutton'){
1032  
1033          foreach ($buttons as $elementname => $button){
1034              $this->setHelpButton($elementname, $button, $suppresscheck, $function);
1035          }
1036      }
1037      /**
1038       * Add a single button.
1039       *
1040       * @param string $elementname name of the element to add the item to
1041       * @param array $button - arguments to pass to function $function
1042       * @param boolean $suppresscheck - whether to throw an error if the element
1043       *                                  doesn't exist.
1044       * @param string $function - function to generate html from the arguments in $button
1045       */
1046      function setHelpButton($elementname, $button, $suppresscheck=false, $function='helpbutton'){
1047          if (array_key_exists($elementname, $this->_elementIndex)){
1048              //_elements has a numeric index, this code accesses the elements by name
1049              $element=&$this->_elements[$this->_elementIndex[$elementname]];
1050              if (method_exists($element, 'setHelpButton')){
1051                  $element->setHelpButton($button, $function);
1052              }else{
1053                  $a=new object();
1054                  $a->name=$element->getName();
1055                  $a->classname=get_class($element);
1056                  print_error('nomethodforaddinghelpbutton', 'form', '', $a);
1057              }
1058          }elseif (!$suppresscheck){
1059              print_error('nonexistentformelements', 'form', '', $elementname);
1060          }
1061      }
1062  
1063      /**
1064       * Set constant value not overriden by _POST or _GET
1065       * note: this does not work for complex names with [] :-(
1066       * @param string $elname name of element
1067       * @param mixed $value
1068       * @return void
1069       */
1070      function setConstant($elname, $value) {
1071          $this->_constantValues = HTML_QuickForm::arrayMerge($this->_constantValues, array($elname=>$value));
1072          $element =& $this->getElement($elname);
1073          $element->onQuickFormEvent('updateValue', null, $this);
1074      }
1075  
1076      function exportValues($elementList= null, $addslashes=true){
1077          $unfiltered = array();
1078          if (null === $elementList) {
1079              // iterate over all elements, calling their exportValue() methods
1080              $emptyarray = array();
1081              foreach (array_keys($this->_elements) as $key) {
1082                  if ($this->_elements[$key]->isFrozen() && !$this->_elements[$key]->_persistantFreeze){
1083                      $value = $this->_elements[$key]->exportValue($emptyarray, true);
1084                  } else {
1085                      $value = $this->_elements[$key]->exportValue($this->_submitValues, true);
1086                  }
1087  
1088                  if (is_array($value)) {
1089                      // This shit throws a bogus warning in PHP 4.3.x
1090                      $unfiltered = HTML_QuickForm::arrayMerge($unfiltered, $value);
1091                  }
1092              }
1093          } else {
1094              if (!is_array($elementList)) {
1095                  $elementList = array_map('trim', explode(',', $elementList));
1096              }
1097              foreach ($elementList as $elementName) {
1098                  $value = $this->exportValue($elementName);
1099                  if (PEAR::isError($value)) {
1100                      return $value;
1101                  }
1102                  $unfiltered[$elementName] = $value;
1103              }
1104          }
1105  
1106          if ($addslashes){
1107              return $this->_recursiveFilter('addslashes', $unfiltered);
1108          } else {
1109              return $unfiltered;
1110          }
1111      }
1112      /**
1113       * Adds a validation rule for the given field
1114       *
1115       * If the element is in fact a group, it will be considered as a whole.
1116       * To validate grouped elements as separated entities,
1117       * use addGroupRule instead of addRule.
1118       *
1119       * @param    string     $element       Form element name
1120       * @param    string     $message       Message to display for invalid data
1121       * @param    string     $type          Rule type, use getRegisteredRules() to get types
1122       * @param    string     $format        (optional)Required for extra rule data
1123       * @param    string     $validation    (optional)Where to perform validation: "server", "client"
1124       * @param    boolean    $reset         Client-side validation: reset the form element to its original value if there is an error?
1125       * @param    boolean    $force         Force the rule to be applied, even if the target form element does not exist
1126       * @since    1.0
1127       * @access   public
1128       * @throws   HTML_QuickForm_Error
1129       */
1130      function addRule($element, $message, $type, $format=null, $validation='server', $reset = false, $force = false)
1131      {
1132          parent::addRule($element, $message, $type, $format, $validation, $reset, $force);
1133          if ($validation == 'client') {
1134              $this->updateAttributes(array('onsubmit' => 'try { var myValidator = validate_' . $this->_formName . '; } catch(e) { return true; } return myValidator(this);'));
1135          }
1136  
1137      } // end func addRule
1138      /**
1139       * Adds a validation rule for the given group of elements
1140       *
1141       * Only groups with a name can be assigned a validation rule
1142       * Use addGroupRule when you need to validate elements inside the group.
1143       * Use addRule if you need to validate the group as a whole. In this case,
1144       * the same rule will be applied to all elements in the group.
1145       * Use addRule if you need to validate the group against a function.
1146       *
1147       * @param    string     $group         Form group name
1148       * @param    mixed      $arg1          Array for multiple elements or error message string for one element
1149       * @param    string     $type          (optional)Rule type use getRegisteredRules() to get types
1150       * @param    string     $format        (optional)Required for extra rule data
1151       * @param    int        $howmany       (optional)How many valid elements should be in the group
1152       * @param    string     $validation    (optional)Where to perform validation: "server", "client"
1153       * @param    bool       $reset         Client-side: whether to reset the element's value to its original state if validation failed.
1154       * @since    2.5
1155       * @access   public
1156       * @throws   HTML_QuickForm_Error
1157       */
1158      function addGroupRule($group, $arg1, $type='', $format=null, $howmany=0, $validation = 'server', $reset = false)
1159      {
1160          parent::addGroupRule($group, $arg1, $type, $format, $howmany, $validation, $reset);
1161          if (is_array($arg1)) {
1162               foreach ($arg1 as $rules) {
1163                  foreach ($rules as $rule) {
1164                      $validation = (isset($rule[3]) && 'client' == $rule[3])? 'client': 'server';
1165  
1166                      if ('client' == $validation) {
1167                          $this->updateAttributes(array('onsubmit' => 'try { var myValidator = validate_' . $this->_formName . '; } catch(e) { return true; } return myValidator(this);'));
1168                      }
1169                  }
1170              }
1171          } elseif (is_string($arg1)) {
1172  
1173              if ($validation == 'client') {
1174                  $this->updateAttributes(array('onsubmit' => 'try { var myValidator = validate_' . $this->_formName . '; } catch(e) { return true; } return myValidator(this);'));
1175              }
1176          }
1177      } // end func addGroupRule
1178  
1179      // }}}
1180      /**
1181       * Returns the client side validation script
1182       *
1183       * The code here was copied from HTML_QuickForm_DHTMLRulesTableless who copied it from  HTML_QuickForm
1184       * and slightly modified to run rules per-element
1185       * Needed to override this because of an error with client side validation of grouped elements.
1186       *
1187       * @access    public
1188       * @return    string    Javascript to perform validation, empty string if no 'client' rules were added
1189       */
1190      function getValidationScript()
1191      {
1192          if (empty($this->_rules) || empty($this->_attributes['onsubmit'])) {
1193              return '';
1194          }
1195  
1196          include_once('HTML/QuickForm/RuleRegistry.php');
1197          $registry =& HTML_QuickForm_RuleRegistry::singleton();
1198          $test = array();
1199          $js_escape = array(
1200              "\r"    => '\r',
1201              "\n"    => '\n',
1202              "\t"    => '\t',
1203              "'"     => "\\'",
1204              '"'     => '\"',
1205              '\\'    => '\\\\'
1206          );
1207  
1208          foreach ($this->_rules as $elementName => $rules) {
1209              foreach ($rules as $rule) {
1210                  if ('client' == $rule['validation']) {
1211                      unset($element); //TODO: find out how to properly initialize it
1212  
1213                      $dependent  = isset($rule['dependent']) && is_array($rule['dependent']);
1214                      $rule['message'] = strtr($rule['message'], $js_escape);
1215  
1216                      if (isset($rule['group'])) {
1217                          $group    =& $this->getElement($rule['group']);
1218                          // No JavaScript validation for frozen elements
1219                          if ($group->isFrozen()) {
1220                              continue 2;
1221                          }
1222                          $elements =& $group->getElements();
1223                          foreach (array_keys($elements) as $key) {
1224                              if ($elementName == $group->getElementName($key)) {
1225                                  $element =& $elements[$key];
1226                                  break;
1227                              }
1228                          }
1229                      } elseif ($dependent) {
1230                          $element   =  array();
1231                          $element[] =& $this->getElement($elementName);
1232                          foreach ($rule['dependent'] as $elName) {
1233                              $element[] =& $this->getElement($elName);
1234                          }
1235                      } else {
1236                          $element =& $this->getElement($elementName);
1237                      }
1238                      // No JavaScript validation for frozen elements
1239                      if (is_object($element) && $element->isFrozen()) {
1240                          continue 2;
1241                      } elseif (is_array($element)) {
1242                          foreach (array_keys($element) as $key) {
1243                              if ($element[$key]->isFrozen()) {
1244                                  continue 3;
1245                              }
1246                          }
1247                      }
1248                      // Fix for bug displaying errors for elements in a group
1249                      //$test[$elementName][] = $registry->getValidationScript($element, $elementName, $rule);
1250                      $test[$elementName][0][] = $registry->getValidationScript($element, $elementName, $rule);
1251                      $test[$elementName][1]=$element;
1252                      //end of fix
1253                  }
1254              }
1255          }
1256  
1257          // Fix for MDL-9524. If you don't do this, then $element may be left as a reference to one of the fields in
1258          // the form, and then that form field gets corrupted by the code that follows.
1259          unset($element);
1260  
1261          $js = '
1262  <script type="text/javascript">
1263  //<![CDATA[
1264  
1265  var skipClientValidation = false;
1266  
1267  function qf_errorHandler(element, _qfMsg) {
1268    div = element.parentNode;
1269    if (_qfMsg != \'\') {
1270      var errorSpan = document.getElementById(\'id_error_\'+element.name);
1271      if (!errorSpan) {
1272        errorSpan = document.createElement("span");
1273        errorSpan.id = \'id_error_\'+element.name;
1274        errorSpan.className = "error";
1275        element.parentNode.insertBefore(errorSpan, element.parentNode.firstChild);
1276      }
1277  
1278      while (errorSpan.firstChild) {
1279        errorSpan.removeChild(errorSpan.firstChild);
1280      }
1281  
1282      errorSpan.appendChild(document.createTextNode(_qfMsg.substring(3)));
1283      errorSpan.appendChild(document.createElement("br"));
1284  
1285      if (div.className.substr(div.className.length - 6, 6) != " error"
1286          && div.className != "error") {
1287        div.className += " error";
1288      }
1289  
1290      return false;
1291    } else {
1292      var errorSpan = document.getElementById(\'id_error_\'+element.name);
1293      if (errorSpan) {
1294        errorSpan.parentNode.removeChild(errorSpan);
1295      }
1296  
1297      if (div.className.substr(div.className.length - 6, 6) == " error") {
1298        div.className = div.className.substr(0, div.className.length - 6);
1299      } else if (div.className == "error") {
1300        div.className = "";
1301      }
1302  
1303      return true;
1304    }
1305  }';
1306          $validateJS = '';
1307          foreach ($test as $elementName => $jsandelement) {
1308              // Fix for bug displaying errors for elements in a group
1309              //unset($element);
1310              list($jsArr,$element)=$jsandelement;
1311              //end of fix
1312              $js .= '
1313  function validate_' . $this->_formName . '_' . $elementName . '(element) {
1314    var value = \'\';
1315    var errFlag = new Array();
1316    var _qfGroups = {};
1317    var _qfMsg = \'\';
1318    var frm = element.parentNode;
1319    while (frm && frm.nodeName.toUpperCase() != "FORM") {
1320      frm = frm.parentNode;
1321    }
1322  ' . join("\n", $jsArr) . '
1323    return qf_errorHandler(element, _qfMsg);
1324  }
1325  ';
1326              $validateJS .= '
1327    ret = validate_' . $this->_formName . '_' . $elementName.'(frm.elements[\''.$elementName.'\']) && ret;
1328    if (!ret && !first_focus) {
1329      first_focus = true;
1330      frm.elements[\''.$elementName.'\'].focus();
1331    }
1332  ';
1333  
1334              // Fix for bug displaying errors for elements in a group
1335              //unset($element);
1336              //$element =& $this->getElement($elementName);
1337              //end of fix
1338              $valFunc = 'validate_' . $this->_formName . '_' . $elementName . '(this)';
1339              $onBlur = $element->getAttribute('onBlur');
1340              $onChange = $element->getAttribute('onChange');
1341              $element->updateAttributes(array('onBlur' => $onBlur . $valFunc,
1342                                               'onChange' => $onChange . $valFunc));
1343          }
1344  //  do not rely on frm function parameter, because htmlarea breaks it when overloading the onsubmit method
1345          $js .= '
1346  function validate_' . $this->_formName . '(frm) {
1347    if (skipClientValidation) {
1348       return true;
1349    }
1350    var ret = true;
1351  
1352    var frm = document.getElementById(\''. $this->_attributes['id'] .'\')
1353    var first_focus = false;
1354  ' . $validateJS . ';
1355    return ret;
1356  }
1357  //]]>
1358  </script>';
1359          return $js;
1360      } // end func getValidationScript
1361      function _setDefaultRuleMessages(){
1362          foreach ($this->_rules as $field => $rulesarr){
1363              foreach ($rulesarr as $key => $rule){
1364                  if ($rule['message']===null){
1365                      $a=new object();
1366                      $a->format=$rule['format'];
1367                      $str=get_string('err_'.$rule['type'], 'form', $a);
1368                      if (strpos($str, '[[')!==0){
1369                          $this->_rules[$field][$key]['message']=$str;
1370                      }
1371                  }
1372              }
1373          }
1374      }
1375  
1376      function getLockOptionEndScript(){
1377  
1378          $iname = $this->getAttribute('id').'items';
1379          $js = '<script type="text/javascript">'."\n";
1380          $js .= '//<![CDATA['."\n";
1381          $js .= "var $iname = Array();\n";
1382  
1383          foreach ($this->_dependencies as $dependentOn => $conditions){
1384              $js .= "{$iname}['$dependentOn'] = Array();\n";
1385              foreach ($conditions as $condition=>$values) {
1386                  $js .= "{$iname}['$dependentOn']['$condition'] = Array();\n";
1387                  foreach ($values as $value=>$dependents) {
1388                      $js .= "{$iname}['$dependentOn']['$condition']['$value'] = Array();\n";
1389                      $i = 0;
1390                      foreach ($dependents as $dependent) {
1391                          $elements = $this->_getElNamesRecursive($dependent);
1392                          if (empty($elements)) {
1393                              // probably element inside of some group
1394                              $elements = array($dependent);
1395                          }
1396                          foreach($elements as $element) {
1397                              if ($element == $dependentOn) {
1398                                  continue;
1399                              }
1400                              $js .= "{$iname}['$dependentOn']['$condition']['$value'][$i]='$element';\n";
1401                              $i++;
1402                          }
1403                      }
1404                  }
1405              }
1406          }
1407          $js .="lockoptionsallsetup('".$this->getAttribute('id')."');\n";
1408          $js .='//]]>'."\n";
1409          $js .='</script>'."\n";
1410          return $js;
1411      }
1412  
1413      function _getElNamesRecursive($element) {
1414          if (is_string($element)) {
1415              if (!$this->elementExists($element)) {
1416                  return array();
1417              }
1418              $element = $this->getElement($element);
1419          }
1420  
1421          if (is_a($element, 'HTML_QuickForm_group')) {
1422              $elsInGroup = $element->getElements();
1423              $elNames = array();
1424              foreach ($elsInGroup as $elInGroup){
1425                  if (is_a($elInGroup, 'HTML_QuickForm_group')) {
1426                      // not sure if this would work - groups nested in groups
1427                      $elNames = array_merge($elNames, $this->_getElNamesRecursive($elInGroup));
1428                  } else {
1429                      $elNames[] = $element->getElementName($elInGroup->getName());
1430                  }
1431              }
1432  
1433          } else if (is_a($element, 'HTML_QuickForm_header')) {
1434              return array();
1435  
1436          } else if (is_a($element, 'HTML_QuickForm_hidden')) {
1437              return array();
1438  
1439          } else if (method_exists($element, 'getPrivateName')) {
1440              return array($element->getPrivateName());
1441  
1442          } else {
1443              $elNames = array($element->getName());
1444          }
1445  
1446          return $elNames;
1447      }
1448  
1449      /**
1450       * Adds a dependency for $elementName which will be disabled if $condition is met.
1451       * If $condition = 'notchecked' (default) then the condition is that the $dependentOn element
1452       * is not checked. If $condition = 'checked' then the condition is that the $dependentOn element
1453       * is checked. If $condition is something else (like "eq" for equals) then it is checked to see if the value
1454       * of the $dependentOn element is $condition (such as equal) to $value.
1455       *
1456       * @param string $elementName the name of the element which will be disabled
1457       * @param string $dependentOn the name of the element whose state will be checked for
1458       *                            condition
1459       * @param string $condition the condition to check
1460       * @param mixed $value used in conjunction with condition.
1461       */
1462      function disabledIf($elementName, $dependentOn, $condition = 'notchecked', $value='1'){
1463          if (!array_key_exists($dependentOn, $this->_dependencies)) {
1464              $this->_dependencies[$dependentOn] = array();
1465          }
1466          if (!array_key_exists($condition, $this->_dependencies[$dependentOn])) {
1467              $this->_dependencies[$dependentOn][$condition] = array();
1468          }
1469          if (!array_key_exists($value, $this->_dependencies[$dependentOn][$condition])) {
1470              $this->_dependencies[$dependentOn][$condition][$value] = array();
1471          }
1472          $this->_dependencies[$dependentOn][$condition][$value][] = $elementName;
1473      }
1474  
1475      function registerNoSubmitButton($buttonname){
1476          $this->_noSubmitButtons[]=$buttonname;
1477      }
1478  
1479      function isNoSubmitButton($buttonname){
1480          return (array_search($buttonname, $this->_noSubmitButtons)!==FALSE);
1481      }
1482  
1483      function _registerCancelButton($addfieldsname){
1484          $this->_cancelButtons[]=$addfieldsname;
1485      }
1486      /**
1487       * Displays elements without HTML input tags.
1488       * This method is different to freeze() in that it makes sure no hidden
1489       * elements are included in the form.
1490       * Note: If you want to make sure the submitted value is ignored, please use setDefaults().
1491       *
1492       * This function also removes all previously defined rules.
1493       *
1494       * @param    mixed   $elementList       array or string of element(s) to be frozen
1495       * @since     1.0
1496       * @access   public
1497       * @throws   HTML_QuickForm_Error
1498       */
1499      function hardFreeze($elementList=null)
1500      {
1501          if (!isset($elementList)) {
1502              $this->_freezeAll = true;
1503              $elementList = array();
1504          } else {
1505              if (!is_array($elementList)) {
1506                  $elementList = preg_split('/[ ]*,[ ]*/', $elementList);
1507              }
1508              $elementList = array_flip($elementList);
1509          }
1510  
1511          foreach (array_keys($this->_elements) as $key) {
1512              $name = $this->_elements[$key]->getName();
1513              if ($this->_freezeAll || isset($elementList[$name])) {
1514                  $this->_elements[$key]->freeze();
1515                  $this->_elements[$key]->setPersistantFreeze(false);
1516                  unset($elementList[$name]);
1517  
1518                  // remove all rules
1519                  $this->_rules[$name] = array();
1520                  // if field is required, remove the rule
1521                  $unset = array_search($name, $this->_required);
1522                  if ($unset !== false) {
1523                      unset($this->_required[$unset]);
1524                  }
1525              }
1526          }
1527  
1528          if (!empty($elementList)) {
1529              return PEAR::raiseError(null, QUICKFORM_NONEXIST_ELEMENT, null, E_USER_WARNING, "Nonexistant element(s): '" . implode("', '", array_keys($elementList)) . "' in HTML_QuickForm::freeze()", 'HTML_QuickForm_Error', true);
1530          }
1531          return true;
1532      }
1533      /**
1534       * Hard freeze all elements in a form except those whose names are in $elementList or hidden elements in a form.
1535       *
1536       * This function also removes all previously defined rules of elements it freezes.
1537       *
1538       * @param    array   $elementList       array or string of element(s) not to be frozen
1539       * @since     1.0
1540       * @access   public
1541       * @throws   HTML_QuickForm_Error
1542       */
1543      function hardFreezeAllVisibleExcept($elementList)
1544      {
1545          $elementList = array_flip($elementList);
1546          foreach (array_keys($this->_elements) as $key) {
1547              $name = $this->_elements[$key]->getName();
1548              $type = $this->_elements[$key]->getType();
1549  
1550              if ($type == 'hidden'){
1551                  // leave hidden types as they are
1552              } elseif (!isset($elementList[$name])) {
1553                  $this->_elements[$key]->freeze();
1554                  $this->_elements[$key]->setPersistantFreeze(false);
1555  
1556                  // remove all rules
1557                  $this->_rules[$name] = array();
1558                  // if field is required, remove the rule
1559                  $unset = array_search($name, $this->_required);
1560                  if ($unset !== false) {
1561                      unset($this->_required[$unset]);
1562                  }
1563              }
1564          }
1565          return true;
1566      }
1567     /**
1568      * Tells whether the form was already submitted
1569      *
1570      * This is useful since the _submitFiles and _submitValues arrays
1571      * may be completely empty after the trackSubmit value is removed.
1572      *
1573      * @access public
1574      * @return bool
1575      */
1576      function isSubmitted()
1577      {
1578          return parent::isSubmitted() && (!$this->isFrozen());
1579      }
1580  }
1581  
1582  
1583  /**
1584   * A renderer for MoodleQuickForm that only uses XHTML and CSS and no
1585   * table tags, extends PEAR class HTML_QuickForm_Renderer_Tableless
1586   *
1587   * Stylesheet is part of standard theme and should be automatically included.
1588   *
1589   * @author      Jamie Pratt <me@jamiep.org>
1590   * @license    gpl license
1591   */
1592  class MoodleQuickForm_Renderer extends HTML_QuickForm_Renderer_Tableless{
1593  
1594      /**
1595      * Element template array
1596      * @var      array
1597      * @access   private
1598      */
1599      var $_elementTemplates;
1600      /**
1601      * Template used when opening a hidden fieldset
1602      * (i.e. a fieldset that is opened when there is no header element)
1603      * @var      string
1604      * @access   private
1605      */
1606      var $_openHiddenFieldsetTemplate = "\n\t<fieldset class=\"hidden\"><div>";
1607     /**
1608      * Header Template string
1609      * @var      string
1610      * @access   private
1611      */
1612      var $_headerTemplate =
1613         "\n\t\t<legend class=\"ftoggler\">{header}</legend>\n\t\t<div class=\"advancedbutton\">{advancedimg}{button}</div><div class=\"fcontainer clearfix\">\n\t\t";
1614  
1615     /**
1616      * Template used when opening a fieldset
1617      * @var      string
1618      * @access   private
1619      */
1620      var $_openFieldsetTemplate = "\n\t<fieldset class=\"clearfix\" {id}>";
1621  
1622      /**
1623      * Template used when closing a fieldset
1624      * @var      string
1625      * @access   private
1626      */
1627      var $_closeFieldsetTemplate = "\n\t\t</div></fieldset>";
1628  
1629     /**
1630      * Required Note template string
1631      * @var      string
1632      * @access   private
1633      */
1634      var $_requiredNoteTemplate =
1635          "\n\t\t<div class=\"fdescription required\">{requiredNote}</div>";
1636  
1637      var $_advancedElements = array();
1638  
1639      /**
1640       * Whether to display advanced elements (on page load)
1641       *
1642       * @var integer 1 means show 0 means hide
1643       */
1644      var $_showAdvanced;
1645  
1646      function MoodleQuickForm_Renderer(){
1647          // switch next two lines for ol li containers for form items.
1648          //        $this->_elementTemplates=array('default'=>"\n\t\t".'<li class="fitem"><label>{label}{help}<!-- BEGIN required -->{req}<!-- END required --></label><div class="qfelement<!-- BEGIN error --> error<!-- END error --> {type}"><!-- BEGIN error --><span class="error">{error}</span><br /><!-- END error -->{element}</div></li>');
1649          $this->_elementTemplates = array(
1650          'default'=>"\n\t\t".'<div class="fitem {advanced}<!-- BEGIN required --> required<!-- END required -->"><div class="fitemtitle"><label>{label}<!-- BEGIN required -->{req}<!-- END required -->{advancedimg} {help}</label></div><div class="felement {type}<!-- BEGIN error --> error<!-- END error -->"><!-- BEGIN error --><span class="error">{error}</span><br /><!-- END error -->{element}</div></div>',
1651  
1652          'fieldset'=>"\n\t\t".'<div class="fitem {advanced}<!-- BEGIN required --> required<!-- END required -->"><div class="fitemtitle"><div class="fgrouplabel"><label>{label}<!-- BEGIN required -->{req}<!-- END required -->{advancedimg} {help}</label></div></div><fieldset class="felement {type}<!-- BEGIN error --> error<!-- END error -->"><!-- BEGIN error --><span class="error">{error}</span><br /><!-- END error -->{element}</fieldset></div>',
1653  
1654          'static'=>"\n\t\t".'<div class="fitem {advanced}"><div class="fitemtitle"><div class="fstaticlabel"><label>{label}<!-- BEGIN required -->{req}<!-- END required -->{advancedimg} {help}</label></div></div><div class="felement fstatic <!-- BEGIN error --> error<!-- END error -->"><!-- BEGIN error --><span class="error">{error}</span><br /><!-- END error -->{element}&nbsp;</div></div>',
1655  
1656          'nodisplay'=>'');
1657  
1658          parent::HTML_QuickForm_Renderer_Tableless();
1659      }
1660  
1661      function setAdvancedElements($elements){
1662          $this->_advancedElements = $elements;
1663      }
1664  
1665      /**
1666       * What to do when starting the form
1667       *
1668       * @param MoodleQuickForm $form
1669       */
1670      function startForm(&$form){
1671          $this->_reqHTML = $form->getReqHTML();
1672          $this->_elementTemplates = str_replace('{req}', $this->_reqHTML, $this->_elementTemplates);
1673          $this->_advancedHTML = $form->getAdvancedHTML();
1674          $this->_showAdvanced = $form->getShowAdvanced();
1675          parent::startForm($form);
1676          if ($form->isFrozen()){
1677              $this->_formTemplate = "\n<div class=\"mform frozen\">\n{content}\n</div>";
1678          } else {
1679              $this->_hiddenHtml .= $form->_pageparams;
1680          }
1681  
1682  
1683      }
1684  
1685      function startGroup(&$group, $required, $error){
1686          if (method_exists($group, 'getElementTemplateType')){
1687              $html = $this->_elementTemplates[$group->getElementTemplateType()];
1688          }else{
1689              $html = $this->_elementTemplates['default'];
1690  
1691          }
1692          if ($this->_showAdvanced){
1693              $advclass = ' advanced';
1694          } else {
1695              $advclass = ' advanced hide';
1696          }
1697          if (isset($this->_advancedElements[$group->getName()])){
1698              $html =str_replace(' {advanced}', $advclass, $html);
1699              $html =str_replace('{advancedimg}', $this->_advancedHTML, $html);
1700          } else {
1701              $html =str_replace(' {advanced}', '', $html);
1702              $html =str_replace('{advancedimg}', '', $html);
1703          }
1704          if (method_exists($group, 'getHelpButton')){
1705              $html =str_replace('{help}', $group->getHelpButton(), $html);
1706          }else{
1707              $html =str_replace('{help}', '', $html);
1708          }
1709          $html =str_replace('{name}', $group->getName(), $html);
1710          $html =str_replace('{type}', 'fgroup', $html);
1711  
1712          $this->_templates[$group->getName()]=$html;
1713          // Fix for bug in tableless quickforms that didn't allow you to stop a
1714          // fieldset before a group of elements.
1715          // if the element name indicates the end of a fieldset, close the fieldset
1716          if (   in_array($group->getName(), $this->_stopFieldsetElements)
1717              && $this->_fieldsetsOpen > 0
1718             ) {
1719              $this->_html .= $this->_closeFieldsetTemplate;
1720              $this->_fieldsetsOpen--;
1721          }
1722          parent::startGroup($group, $required, $error);
1723      }
1724  
1725      function renderElement(&$element, $required, $error){
1726          //manipulate id of all elements before rendering
1727          if (!is_null($element->getAttribute('id'))) {
1728              $id = $element->getAttribute('id');
1729          } else {
1730              $id = $element->getName();
1731          }
1732          //strip qf_ prefix and replace '[' with '_' and strip ']'
1733          $id = preg_replace(array('/^qf_|\]/', '/\[/'), array('', '_'), $id);
1734          if (strpos($id, 'id_') !== 0){
1735              $element->updateAttributes(array('id'=>'id_'.$id));
1736          }
1737  
1738          //adding stuff to place holders in template
1739          if (method_exists($element, 'getElementTemplateType')){
1740              $html = $this->_elementTemplates[$element->getElementTemplateType()];
1741          }else{
1742              $html = $this->_elementTemplates['default'];
1743          }
1744          if ($this->_showAdvanced){
1745              $advclass = ' advanced';
1746          } else {
1747              $advclass = ' advanced hide';
1748          }
1749          if (isset($this->_advancedElements[$element->getName()])){
1750              $html =str_replace(' {advanced}', $advclass, $html);
1751          } else {
1752              $html =str_replace(' {advanced}', '', $html);
1753          }
1754          if (isset($this->_advancedElements[$element->getName()])||$element->getName() == 'mform_showadvanced'){
1755              $html =str_replace('{advancedimg}', $this->_advancedHTML, $html);
1756          } else {
1757              $html =str_replace('{advancedimg}', '', $html);
1758          }
1759          $html =str_replace('{type}', 'f'.$element->getType(), $html);
1760          $html =str_replace('{name}', $element->getName(), $html);
1761          if (method_exists($element, 'getHelpButton')){
1762              $html = str_replace('{help}', $element->getHelpButton(), $html);
1763          }else{
1764              $html = str_replace('{help}', '', $html);
1765  
1766          }
1767          if (!isset($this->_templates[$element->getName()])) {
1768              $this->_templates[$element->getName()] = $html;
1769          }
1770  
1771          parent::renderElement($element, $required, $error);
1772      }
1773  
1774      function finishForm(&$form){
1775          if ($form->isFrozen()){
1776              $this->_hiddenHtml = '';
1777          }
1778          parent::finishForm($form);
1779          if ((!$form->isFrozen()) && ('' != ($script = $form->getLockOptionEndScript()))) {
1780              // add a lockoptions script
1781              $this->_html = $this->_html . "\n" . $script;
1782          }
1783      }
1784     /**
1785      * Called when visiting a header element
1786      *
1787      * @param    object     An HTML_QuickForm_header element being visited
1788      * @access   public
1789      * @return   void
1790      */
1791      function renderHeader(&$header)    {
1792          $name = $header->getName();
1793  
1794          $id = empty($name) ? '' : ' id="' . $name . '"';
1795          $id = preg_replace(array('/\]/', '/\[/'), array('', '_'), $id);
1796          if (is_null($header->_text)) {
1797              $header_html = '';
1798          } elseif (!empty($name) && isset($this->_templates[$name])) {
1799              $header_html = str_replace('{header}', $header->toHtml(), $this->_templates[$name]);
1800          } else {
1801              $header_html = str_replace('{header}', $header->toHtml(), $this->_headerTemplate);
1802          }
1803  
1804          if (isset($this->_advancedElements[$name])){
1805              $header_html =str_replace('{advancedimg}', $this->_advancedHTML, $header_html);
1806          } else {
1807              $header_html =str_replace('{advancedimg}', '', $header_html);
1808          }
1809          $elementName='mform_showadvanced';
1810          if ($this->_showAdvanced==0){
1811              $buttonlabel = get_string('showadvanced', 'form');
1812          } else {
1813              $buttonlabel = get_string('hideadvanced', 'form');
1814          }
1815  
1816          if (isset($this->_advancedElements[$name])){
1817              require_js(array('yui_yahoo', 'yui_event'));
1818              // this is tricky - the first submit button on form is "clicked" if user presses enter
1819              // we do not want to "submit" using advanced button if javascript active
1820              $button_nojs = '<input name="'.$elementName.'" value="'.$buttonlabel.'" type="submit" />';
1821  
1822              $buttonlabel = addslashes_js($buttonlabel);
1823              $showtext = addslashes_js(get_string('showadvanced', 'form'));
1824              $hidetext = addslashes_js(get_string('hideadvanced', 'form'));
1825              $button = '<script id="' . $name . '_script" type="text/javascript">' . "
1826  showAdvancedInit('{$name}_script', '$elementName', '$buttonlabel', '$hidetext', '$showtext');
1827  " . '</script><noscript><div style="display:inline">'.$button_nojs.'</div></noscript>';  // the extra div should fix xhtml validation
1828  
1829              $header_html = str_replace('{button}', $button, $header_html);
1830          } else {
1831              $header_html = str_replace('{button}', '', $header_html);
1832          }
1833  
1834          if ($this->_fieldsetsOpen > 0) {
1835              $this->_html .= $this->_closeFieldsetTemplate;
1836              $this->_fieldsetsOpen--;
1837          }
1838  
1839          $openFieldsetTemplate = str_replace('{id}', $id, $this->_openFieldsetTemplate);
1840          if ($this->_showAdvanced){
1841              $advclass = ' class="advanced"';
1842          } else {
1843              $advclass = ' class="advanced hide"';
1844          }
1845          if (isset($this->_advancedElements[$name])){
1846              $openFieldsetTemplate = str_replace('{advancedclass}', $advclass, $openFieldsetTemplate);
1847          } else {
1848              $openFieldsetTemplate = str_replace('{advancedclass}', '', $openFieldsetTemplate);
1849          }
1850          $this->_html .= $openFieldsetTemplate . $header_html;
1851          $this->_fieldsetsOpen++;
1852      } // end func renderHeader
1853  
1854      function getStopFieldsetElements(){
1855          return $this->_stopFieldsetElements;
1856      }
1857  }
1858  
1859  
1860  $GLOBALS['_HTML_QuickForm_default_renderer'] =& new MoodleQuickForm_Renderer();
1861  
1862  MoodleQuickForm::registerElementType('checkbox', "$CFG->libdir/form/checkbox.php", 'MoodleQuickForm_checkbox');
1863  MoodleQuickForm::registerElementType('file', "$CFG->libdir/form/file.php", 'MoodleQuickForm_file');
1864  MoodleQuickForm::registerElementType('group', "$CFG->libdir/form/group.php", 'MoodleQuickForm_group');
1865  MoodleQuickForm::registerElementType('password', "$CFG->libdir/form/password.php", 'MoodleQuickForm_password');
1866  MoodleQuickForm::registerElementType('passwordunmask', "$CFG->libdir/form/passwordunmask.php", 'MoodleQuickForm_passwordunmask');
1867  MoodleQuickForm::registerElementType('radio', "$CFG->libdir/form/radio.php", 'MoodleQuickForm_radio');
1868  MoodleQuickForm::registerElementType('select', "$CFG->libdir/form/select.php", 'MoodleQuickForm_select');
1869  MoodleQuickForm::registerElementType('selectgroups', "$CFG->libdir/form/selectgroups.php", 'MoodleQuickForm_selectgroups');
1870  MoodleQuickForm::registerElementType('submitlink', "$CFG->libdir/form/submitlink.php", 'MoodleQuickForm_submitlink');
1871  MoodleQuickForm::registerElementType('text', "$CFG->libdir/form/text.php", 'MoodleQuickForm_text');
1872  MoodleQuickForm::registerElementType('textarea', "$CFG->libdir/form/textarea.php", 'MoodleQuickForm_textarea');
1873  MoodleQuickForm::registerElementType('date_selector', "$CFG->libdir/form/dateselector.php", 'MoodleQuickForm_date_selector');
1874  MoodleQuickForm::registerElementType('date_time_selector', "$CFG->libdir/form/datetimeselector.php", 'MoodleQuickForm_date_time_selector');
1875  MoodleQuickForm::registerElementType('htmleditor', "$CFG->libdir/form/htmleditor.php", 'MoodleQuickForm_htmleditor');
1876  MoodleQuickForm::registerElementType('format', "$CFG->libdir/form/format.php", 'MoodleQuickForm_format');
1877  MoodleQuickForm::registerElementType('static', "$CFG->libdir/form/static.php", 'MoodleQuickForm_static');
1878  MoodleQuickForm::registerElementType('hidden', "$CFG->libdir/form/hidden.php", 'MoodleQuickForm_hidden');
1879  MoodleQuickForm::registerElementType('modvisible', "$CFG->libdir/form/modvisible.php", 'MoodleQuickForm_modvisible');
1880  MoodleQuickForm::registerElementType('selectyesno', "$CFG->libdir/form/selectyesno.php", 'MoodleQuickForm_selectyesno');
1881  MoodleQuickForm::registerElementType('modgrade', "$CFG->libdir/form/modgrade.php", 'MoodleQuickForm_modgrade');
1882  MoodleQuickForm::registerElementType('cancel', "$CFG->libdir/form/cancel.php", 'MoodleQuickForm_cancel');
1883  MoodleQuickForm::registerElementType('button', "$CFG->libdir/form/button.php", 'MoodleQuickForm_button');
1884  MoodleQuickForm::registerElementType('choosecoursefile', "$CFG->libdir/form/choosecoursefile.php", 'MoodleQuickForm_choosecoursefile');
1885  MoodleQuickForm::registerElementType('choosecoursefileorimsrepo', "$CFG->libdir/form/choosecoursefileorimsrepo.php", 'MoodleQuickForm_choosecoursefileorimsrepo');
1886  MoodleQuickForm::registerElementType('header', "$CFG->libdir/form/header.php", 'MoodleQuickForm_header');
1887  MoodleQuickForm::registerElementType('submit', "$CFG->libdir/form/submit.php", 'MoodleQuickForm_submit');
1888  MoodleQuickForm::registerElementType('questioncategory', "$CFG->libdir/form/questioncategory.php", 'MoodleQuickForm_questioncategory');
1889  MoodleQuickForm::registerElementType('advcheckbox', "$CFG->libdir/form/advcheckbox.php", 'MoodleQuickForm_advcheckbox');
1890  MoodleQuickForm::registerElementType('recaptcha', "$CFG->libdir/form/recaptcha.php", 'MoodleQuickForm_recaptcha');
1891  ?>