MDL-55417 forms: Render form elements with a template
authorDamyon Wiese <damyon@moodle.com>
Thu, 4 Aug 2016 14:29:32 +0000 (22:29 +0800)
committerDan Poltawski <dan@moodle.com>
Fri, 23 Sep 2016 09:53:30 +0000 (10:53 +0100)
This change allows form elements to be overridden with a mustache template.
The template can even listen for form validation errors and supply the JS to
change the look of the form element when there is/isn't a validation error.

Initial support is for all core form elements including:

text, select, selectyesno and checkboxes, groups, dateselector, datetimeselector,
autocomplete, modvisible, advcheckbox, button, duration, filemanager, filepicker, editor, static, grading,
warning, textarea, password, url, submit, questioncategory, recaptcha.

Part of MDL-55071

69 files changed:
grade/edit/outcome/edit_form.php
grade/edit/tree/outcomeitem_form.php
lib/amd/build/event.min.js
lib/amd/src/event.js
lib/form/advcheckbox.php
lib/form/autocomplete.php
lib/form/button.php
lib/form/checkbox.php
lib/form/editor.php
lib/form/filemanager.php
lib/form/filepicker.php
lib/form/grading.php
lib/form/group.php
lib/form/modgrade.php
lib/form/password.php
lib/form/recaptcha.php
lib/form/select.php
lib/form/selectgroups.php
lib/form/selectwithlink.php
lib/form/static.php
lib/form/submit.php
lib/form/submitlink.php
lib/form/templates/editor_textarea.mustache [new file with mode: 0644]
lib/form/text.php
lib/form/textarea.php
lib/form/url.php
lib/formslib.php
lib/outputrenderers.php
theme/noname/scss/moodle/core.scss
theme/noname/scss/moodle/course.scss
theme/noname/scss/moodle/forms.scss
theme/noname/templates/core/form_autocomplete_input.mustache [new file with mode: 0644]
theme/noname/templates/core/form_autocomplete_selection.mustache [new file with mode: 0644]
theme/noname/templates/core_form/editor_textarea.mustache [new file with mode: 0644]
theme/noname/templates/core_form/element-advcheckbox-inline.mustache [new file with mode: 0644]
theme/noname/templates/core_form/element-advcheckbox.mustache [new file with mode: 0644]
theme/noname/templates/core_form/element-autocomplete.mustache [new file with mode: 0644]
theme/noname/templates/core_form/element-button-inline.mustache [new file with mode: 0644]
theme/noname/templates/core_form/element-button.mustache [new file with mode: 0644]
theme/noname/templates/core_form/element-checkbox-inline.mustache [new file with mode: 0644]
theme/noname/templates/core_form/element-checkbox.mustache [new file with mode: 0644]
theme/noname/templates/core_form/element-date_selector.mustache [new file with mode: 0644]
theme/noname/templates/core_form/element-date_time_selector.mustache [new file with mode: 0644]
theme/noname/templates/core_form/element-duration.mustache [new file with mode: 0644]
theme/noname/templates/core_form/element-editor.mustache [new file with mode: 0644]
theme/noname/templates/core_form/element-filemanager.mustache [new file with mode: 0644]
theme/noname/templates/core_form/element-filepicker.mustache [new file with mode: 0644]
theme/noname/templates/core_form/element-grading.mustache [new file with mode: 0644]
theme/noname/templates/core_form/element-group.mustache [new file with mode: 0644]
theme/noname/templates/core_form/element-modgrade.mustache [new file with mode: 0644]
theme/noname/templates/core_form/element-modvisible.mustache [new file with mode: 0644]
theme/noname/templates/core_form/element-password.mustache [new file with mode: 0644]
theme/noname/templates/core_form/element-questioncategory-inline.mustache [new file with mode: 0644]
theme/noname/templates/core_form/element-questioncategory.mustache [new file with mode: 0644]
theme/noname/templates/core_form/element-recaptcha.mustache [new file with mode: 0644]
theme/noname/templates/core_form/element-select-inline.mustache [new file with mode: 0644]
theme/noname/templates/core_form/element-select.mustache [new file with mode: 0644]
theme/noname/templates/core_form/element-selectgroups-inline.mustache [new file with mode: 0644]
theme/noname/templates/core_form/element-selectgroups.mustache [new file with mode: 0644]
theme/noname/templates/core_form/element-selectyesno-inline.mustache [new file with mode: 0644]
theme/noname/templates/core_form/element-selectyesno.mustache [new file with mode: 0644]
theme/noname/templates/core_form/element-static.mustache [new file with mode: 0644]
theme/noname/templates/core_form/element-submit-inline.mustache [new file with mode: 0644]
theme/noname/templates/core_form/element-submit.mustache [new file with mode: 0644]
theme/noname/templates/core_form/element-text-inline.mustache [new file with mode: 0644]
theme/noname/templates/core_form/element-text.mustache [new file with mode: 0644]
theme/noname/templates/core_form/element-textarea.mustache [new file with mode: 0644]
theme/noname/templates/core_form/element-url.mustache [new file with mode: 0644]
theme/noname/templates/core_form/element-warning.mustache [new file with mode: 0644]

index fd06079..ae4bacd 100644 (file)
@@ -49,8 +49,12 @@ class edit_outcome_form extends moodleform {
 
         $options = array();
 
-        $mform->addElement('selectwithlink', 'scaleid', get_string('scale'), $options, null,
-            array('link' => $CFG->wwwroot.'/grade/edit/scale/edit.php?courseid='.$COURSE->id, 'label' => get_string('scalescustomcreate')));
+        $mform->addElement('select', 'scaleid', get_string('scale'), $options);
+
+        $url = new moodle_url('/grade/edit/scale/edit.php', array('courseid' => $COURSE->id));
+        $label = get_string('scalescustomcreate');
+        $mform->addElement('static', 'scaleidlink', '', html_writer::link($url, $label));
+
         $mform->addHelpButton('scaleid', 'typescale', 'grades');
         $mform->addRule('scaleid', get_string('required'), 'required');
 
index 285acbe..5930ca7 100644 (file)
@@ -56,8 +56,11 @@ class edit_outcomeitem_form extends moodleform {
                 $options[$outcome->id] = $outcome->get_name();
             }
         }
-        $mform->addElement('selectwithlink', 'outcomeid', get_string('outcome', 'grades'), $options, null,
-            array('link' => $CFG->wwwroot.'/grade/edit/outcome/course.php?id='.$COURSE->id, 'label' => get_string('outcomeassigntocourse', 'grades')));
+        $url = new moodle_url('/grade/edit/outcome/course.php', array('id' => $COURSE->id));
+        $label = get_string('outcomeassigntocourse', 'grades');
+
+        $mform->addElement('select', 'outcomeid', get_string('outcome', 'grades'), $options);
+        $mform->addElement('static', 'outcomeidlink', '', html_writer::link($url, $label));
         $mform->addHelpButton('outcomeid', 'outcome', 'grades');
         $mform->addRule('outcomeid', get_string('required'), 'required');
 
index 39b9bb5..5f05de6 100644 (file)
Binary files a/lib/amd/build/event.min.js and b/lib/amd/build/event.min.js differ
index 0923668..568f4df 100644 (file)
@@ -27,7 +27,14 @@ define(['jquery', 'core/yui'],
        function($, Y) {
 
     return /** @alias module:core/event */ {
+
+
         // Public variables and functions.
+        // These are AMD only events - no backwards compatibility for new things.
+        Events: {
+            FORM_FIELD_VALIDATION: "core_form-field-validation"
+        },
+
         /**
          * Trigger an event using both JQuery and YUI
          *
index 97118d6..00bcb17 100644 (file)
@@ -26,6 +26,7 @@
  */
 
 require_once('HTML/QuickForm/advcheckbox.php');
+require_once(__DIR__ . '/../outputcomponents.php');
 
 /**
  * HTML class for an advcheckbox type element
@@ -38,7 +39,7 @@ require_once('HTML/QuickForm/advcheckbox.php');
  * @copyright 2007 Jamie Pratt <me@jamiep.org>
  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
-class MoodleQuickForm_advcheckbox extends HTML_QuickForm_advcheckbox{
+class MoodleQuickForm_advcheckbox extends HTML_QuickForm_advcheckbox implements templatable {
     /** @var string html for help button, if empty then no help will icon will be dispalyed. */
     var $_helpbutton='';
 
@@ -130,4 +131,13 @@ class MoodleQuickForm_advcheckbox extends HTML_QuickForm_advcheckbox{
         }
         return $output;
     }
+
+    public function export_for_template(renderer_base $output) {
+        $context = [];
+        $context['frozen'] = $this->_flagFrozen;
+        foreach ($this->getAttributes() as $name => $value) {
+            $context[$name] = $value;
+        }
+        return $context;
+    }
 }
index 1f2dac3..5d695ea 100644 (file)
@@ -204,4 +204,23 @@ class MoodleQuickForm_autocomplete extends MoodleQuickForm_select {
         }
         return parent::onQuickFormEvent($event, $arg, $caller);
     }
+
+    public function export_for_template(renderer_base $output) {
+        global $PAGE;
+
+        $this->_generateId();
+        $id = $this->getAttribute('id');
+        $PAGE->requires->js_call_amd('core/form-autocomplete', 'enhance', $params = array('#' . $id, $this->tags, $this->ajax,
+            $this->placeholder, $this->casesensitive, $this->showsuggestions, $this->noselectionstring));
+
+        $context = parent::export_for_template($output);
+        $context['tags'] = $this->tags;
+        $context['ajax'] = $this->ajax;
+        $context['placeholder'] = $this->placeholder;
+        $context['casesensitive'] = $this->casesensitive;
+        $context['showsuggestions'] = $this->showsuggestions;
+        $context['noselectionstring'] = $this->noselectionstring;
+
+        return $context;
+    }
 }
index a69e217..3546f67 100644 (file)
@@ -26,6 +26,7 @@
  */
 
 require_once("HTML/QuickForm/button.php");
+require_once(__DIR__ . '/../outputcomponents.php');
 
 /**
  * HTML class for a button type element
@@ -37,7 +38,7 @@ require_once("HTML/QuickForm/button.php");
  * @copyright 2007 Jamie Pratt <me@jamiep.org>
  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
-class MoodleQuickForm_button extends HTML_QuickForm_button
+class MoodleQuickForm_button extends HTML_QuickForm_button implements templatable
 {
     /** @var string html for help button, if empty then no help */
     var $_helpbutton='';
@@ -85,4 +86,13 @@ class MoodleQuickForm_button extends HTML_QuickForm_button
             return 'default';
         }
     }
+
+    public function export_for_template(renderer_base $output) {
+        $context = [];
+        $context['frozen'] = $this->_flagFrozen;
+        foreach ($this->getAttributes() as $name => $value) {
+            $context[$name] = $value;
+        }
+        return $context;
+    }
 }
index 8aaecdd..00ab240 100644 (file)
@@ -26,6 +26,7 @@
  */
 
 require_once('HTML/QuickForm/checkbox.php');
+require_once(__DIR__ . '/../outputcomponents.php');
 
 /**
  * HTML class for a checkbox type element
@@ -39,7 +40,7 @@ require_once('HTML/QuickForm/checkbox.php');
  * @copyright 2007 Jamie Pratt <me@jamiep.org>
  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
-class MoodleQuickForm_checkbox extends HTML_QuickForm_checkbox{
+class MoodleQuickForm_checkbox extends HTML_QuickForm_checkbox implements templatable {
     /** @var string html for help button, if empty then no help */
     var $_helpbutton='';
 
@@ -138,4 +139,13 @@ class MoodleQuickForm_checkbox extends HTML_QuickForm_checkbox{
         }
         return $output;
     }
+
+    public function export_for_template(renderer_base $output) {
+        $context = [];
+        $context['frozen'] = $this->_flagFrozen;
+        foreach ($this->getAttributes() as $name => $value) {
+            $context[$name] = $value;
+        }
+        return $context;
+    }
 }
index 4e06505..85d4f86 100644 (file)
@@ -30,6 +30,7 @@ global $CFG;
 require_once('HTML/QuickForm/element.php');
 require_once($CFG->dirroot.'/lib/filelib.php');
 require_once($CFG->dirroot.'/repository/lib.php');
+require_once($CFG->libdir.'/outputcomponents.php');
 
 /**
  * Editor element
@@ -43,7 +44,7 @@ require_once($CFG->dirroot.'/repository/lib.php');
  * @todo      MDL-29421 element Freezing
  * @todo      MDL-29426 ajax format conversion
  */
-class MoodleQuickForm_editor extends HTML_QuickForm_element {
+class MoodleQuickForm_editor extends HTML_QuickForm_element implements templatable {
     /** @var string html for help button, if empty then no help will icon will be dispalyed. */
     public $_helpbutton = '';
 
@@ -293,7 +294,7 @@ class MoodleQuickForm_editor extends HTML_QuickForm_element {
      * @return string
      */
     function toHtml() {
-        global $CFG, $PAGE;
+        global $CFG, $PAGE, $OUTPUT;
         require_once($CFG->dirroot.'/repository/lib.php');
 
         if ($this->_flagFrozen) {
@@ -394,24 +395,27 @@ class MoodleQuickForm_editor extends HTML_QuickForm_element {
         $cols = empty($this->_attributes['cols']) ? 80 : $this->_attributes['cols'];
 
         //Apply editor validation if required field
-        $editorrules = '';
-        if (!is_null($this->getAttribute('onblur')) && !is_null($this->getAttribute('onchange'))) {
-            $editorrules = ' onblur="'.htmlspecialchars($this->getAttribute('onblur')).'" onchange="'.htmlspecialchars($this->getAttribute('onchange')).'"';
+        $context = [];
+        $context['rows'] = $rows;
+        $context['cols'] = $cols;
+        $context['frozen'] = $this->_flagFrozen;
+        foreach ($this->getAttributes() as $name => $value) {
+            $context[$name] = $value;
+        }
+        $context['hasformats'] = count($formats) > 1;
+        $context['formats'] = [];
+        foreach ($formats as $value => $text) {
+            $context['formats'][] = ['value' => $value, 'text' => $text, 'selected' => ($value == $format)];
         }
-        $str .= '<div><textarea id="'.$id.'" name="'.$elname.'[text]" rows="'.$rows.'" cols="'.$cols.'" spellcheck="true"'.$editorrules.'>';
-        $str .= s($text);
-        $str .= '</textarea></div>';
+        $context['id'] = $id;
+        $context['value'] = $text;
+        $context['format'] = $format;
 
-        $str .= '<div>';
-        if (count($formats)>1) {
-            $str .= html_writer::label(get_string('format'), 'menu'. $elname. 'format', false, array('class' => 'accesshide'));
-            $str .= html_writer::select($formats, $elname.'[format]', $format, false, array('id' => 'menu'. $elname. 'format'));
-        } else {
-            $keys = array_keys($formats);
-            $str .= html_writer::empty_tag('input',
-                    array('name'=>$elname.'[format]', 'type'=> 'hidden', 'value' => array_pop($keys)));
+        $str .= $OUTPUT->render_from_template('core_form/editor_textarea', $context);
+
+        if (!is_null($this->getAttribute('onblur')) && !is_null($this->getAttribute('onchange'))) {
+            $context['changelistener'] = true;
         }
-        $str .= '</div>';
 
         // during moodle installation, user area doesn't exist
         // so we need to disable filepicker here.
@@ -446,6 +450,16 @@ class MoodleQuickForm_editor extends HTML_QuickForm_element {
         return $str;
     }
 
+    public function export_for_template(renderer_base $output) {
+        $context = [];
+        $context['frozen'] = $this->_flagFrozen;
+        foreach ($this->getAttributes() as $name => $value) {
+            $context[$name] = $value;
+        }
+        $context['html'] = $this->toHtml();
+        return $context;
+    }
+
     /**
      * What to display when element is frozen.
      *
index d525253..7cde5ef 100644 (file)
@@ -30,6 +30,7 @@ global $CFG;
 require_once('HTML/QuickForm/element.php');
 require_once($CFG->dirroot.'/lib/filelib.php');
 require_once($CFG->dirroot.'/repository/lib.php');
+require_once($CFG->libdir.'/outputcomponents.php');
 
 /**
  * Filemanager form element
@@ -40,7 +41,7 @@ require_once($CFG->dirroot.'/repository/lib.php');
  * @copyright 2009 Dongsheng Cai <dongsheng@moodle.com>
  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
-class MoodleQuickForm_filemanager extends HTML_QuickForm_element {
+class MoodleQuickForm_filemanager extends HTML_QuickForm_element implements templatable {
     /** @var string html for help button, if empty then no help will icon will be dispalyed. */
     public $_helpbutton = '';
 
@@ -297,6 +298,16 @@ class MoodleQuickForm_filemanager extends HTML_QuickForm_element {
 
         return $html;
     }
+
+    public function export_for_template(renderer_base $output) {
+        $context = [];
+        $context['frozen'] = $this->_flagFrozen;
+        foreach ($this->getAttributes() as $name => $value) {
+            $context[$name] = $value;
+        }
+        $context['html'] = $this->toHtml();
+        return $context;
+    }
 }
 
 /**
index 1634096..b81a631 100644 (file)
@@ -29,6 +29,7 @@ global $CFG;
 
 require_once("HTML/QuickForm/button.php");
 require_once($CFG->dirroot.'/repository/lib.php');
+require_once($CFG->libdir.'/outputcomponents.php');
 
 /**
  * Filepicker form element
@@ -40,7 +41,7 @@ require_once($CFG->dirroot.'/repository/lib.php');
  * @copyright 2009 Dongsheng Cai <dongsheng@moodle.com>
  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
-class MoodleQuickForm_filepicker extends HTML_QuickForm_input {
+class MoodleQuickForm_filepicker extends HTML_QuickForm_input implements templatable {
     /** @var string html for help button, if empty then no help will icon will be dispalyed. */
     public $_helpbutton = '';
 
@@ -220,4 +221,14 @@ class MoodleQuickForm_filepicker extends HTML_QuickForm_input {
 
         return $this->_prepareValue($draftitemid, true);
     }
+
+    public function export_for_template(renderer_base $output) {
+        $context = [];
+        $context['frozen'] = $this->_flagFrozen;
+        foreach ($this->getAttributes() as $name => $value) {
+            $context[$name] = $value;
+        }
+        $context['html'] = $this->toHtml();
+        return $context;
+    }
 }
index c47de8a..d2aadee 100644 (file)
@@ -28,6 +28,7 @@
 global $CFG;
 require_once("HTML/QuickForm/element.php");
 require_once($CFG->dirroot.'/grade/grading/form/lib.php');
+require_once($CFG->libdir.'/outputcomponents.php');
 
 if (class_exists('HTML_QuickForm')) {
     HTML_QuickForm::registerRule('gradingvalidated', 'callback', '_validate', 'MoodleQuickForm_grading');
@@ -47,7 +48,7 @@ if (class_exists('HTML_QuickForm')) {
  * @copyright 2011 Marina Glancy
  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
-class MoodleQuickForm_grading extends HTML_QuickForm_input{
+class MoodleQuickForm_grading extends HTML_QuickForm_input implements templatable {
     /** @var string html for help button, if empty then no help */
     var $_helpbutton='';
 
@@ -63,6 +64,7 @@ class MoodleQuickForm_grading extends HTML_QuickForm_input{
      */
     public function __construct($elementName=null, $elementLabel=null, $attributes=null) {
         parent::__construct($elementName, $elementLabel, $attributes);
+        $this->_type = 'grading';
         $this->gradingattributes = $attributes;
     }
 
@@ -158,4 +160,8 @@ class MoodleQuickForm_grading extends HTML_QuickForm_input{
         }
         return true;
     }
+
+    public function export_for_template(renderer_base $output) {
+        return $this->toHtml();
+    }
 }
index 6750d36..fbe4de8 100644 (file)
@@ -26,6 +26,7 @@
  */
 
 require_once("HTML/QuickForm/group.php");
+require_once(__DIR__ . '/../outputcomponents.php');
 
 /**
  * HTML class for a form element group
@@ -37,13 +38,15 @@ require_once("HTML/QuickForm/group.php");
  * @copyright 2007 Jamie Pratt <me@jamiep.org>
  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
-class MoodleQuickForm_group extends HTML_QuickForm_group{
+class MoodleQuickForm_group extends HTML_QuickForm_group implements templatable {
     /** @var string html for help button, if empty then no help */
     var $_helpbutton='';
 
     /** @var MoodleQuickForm */
     protected $_mform = null;
 
+    var $_renderedfromtemplate = false;
+
     /**
      * constructor
      *
@@ -147,4 +150,89 @@ class MoodleQuickForm_group extends HTML_QuickForm_group{
         }
         return call_user_func_array([$this->_mform, 'createElement'], func_get_args());
     }
+
+    public function export_for_template(renderer_base $output) {
+        global $OUTPUT;
+        $this->_renderedfromtemplate = true;
+
+        include_once('HTML/QuickForm/Renderer/Default.php');
+
+        $context = [];
+        $context['frozen'] = $this->_flagFrozen;
+        foreach ($this->getAttributes() as $name => $value) {
+            $context[$name] = $value;
+        }
+        $elements = [];
+        foreach ($this->_elements as $key => $element) {
+            $element->_generateId();
+            $name = $this->getName();
+            $elementname = '';
+            if ($this->_appendName) {
+                $elementname = $element->getName();
+                if (isset($elementname)) {
+                    $element->setName($name . '['. (strlen($elementname) ? $elementname : $key) .']');
+                } else {
+                    $element->setName($name);
+                }
+            }
+
+            $out = $OUTPUT->mform_element($element, false, '', true);
+
+            if (empty($out)) {
+                $renderer = new HTML_QuickForm_Renderer_Default();
+                $renderer->setElementTemplate('{element}');
+                $element->accept($renderer);
+                $out = $renderer->toHtml();
+            }
+            $elements[] = $out;
+            // Restore the element's name.
+            if ($this->_appendName) {
+                $element->setName($elementname);
+            }
+
+        }
+
+        $context['elements'] = $elements;
+        return $context;
+    }
+
+     /**
+    * Accepts a renderer
+    *
+    * @param object     An HTML_QuickForm_Renderer object
+    * @param bool       Whether a group is required
+    * @param string     An error message associated with a group
+    * @access public
+    * @return void
+    */
+    function accept(&$renderer, $required = false, $error = null) {
+        $this->_createElementsIfNotExist();
+        $renderer->startGroup($this, $required, $error);
+        if (!$this->_renderedfromtemplate) {
+            // Backwards compatible path - only do this if we didn't render the sub-elements already.
+            $name = $this->getName();
+            foreach (array_keys($this->_elements) as $key) {
+                $element =& $this->_elements[$key];
+
+                if ($this->_appendName) {
+                    $elementName = $element->getName();
+                    if (isset($elementName)) {
+                        $element->setName($name . '['. (strlen($elementName)? $elementName: $key) .']');
+                    } else {
+                        $element->setName($name);
+                    }
+                }
+
+                $required = !$element->isFrozen() && in_array($element->getName(), $this->_required);
+
+                $element->accept($renderer, $required);
+
+                // restore the element's name
+                if ($this->_appendName) {
+                    $element->setName($elementName);
+                }
+            }
+        }
+        $renderer->finishGroup($this);
+    }
 }
index 1be7cc8..efa3d69 100644 (file)
@@ -149,14 +149,14 @@ class MoodleQuickForm_modgrade extends MoodleQuickForm_group {
         $langscale = get_string('modgradetypescale', 'grades');
         $this->scaleformelement = $this->createFormElement('select', 'modgrade_scale', $langscale,
             $scales, $attributes);
-        $this->scaleformelement->setHiddenLabel = false;
+        $this->scaleformelement->setHiddenLabel(true);
         $scaleformelementid = $this->generate_modgrade_subelement_id('modgrade_scale');
         $this->scaleformelement->updateAttributes(array('id' => $scaleformelementid));
 
         // Maximum grade textbox.
         $langmaxgrade = get_string('modgrademaxgrade', 'grades');
         $this->maxgradeformelement = $this->createFormElement('text', 'modgrade_point', $langmaxgrade, array());
-        $this->maxgradeformelement->setHiddenLabel = false;
+        $this->maxgradeformelement->setHiddenLabel(true);
         $maxgradeformelementid = $this->generate_modgrade_subelement_id('modgrade_point');
         $this->maxgradeformelement->updateAttributes(array('id' => $maxgradeformelementid));
 
@@ -169,7 +169,7 @@ class MoodleQuickForm_modgrade extends MoodleQuickForm_group {
         $langtype = get_string('modgradetype', 'grades');
         $this->gradetypeformelement = $this->createFormElement('select', 'modgrade_type', $langtype, $gradetype,
             $attributes, true);
-        $this->gradetypeformelement->setHiddenLabel = false;
+        $this->gradetypeformelement->setHiddenLabel(true);
         $gradetypeformelementid = $this->generate_modgrade_subelement_id('modgrade_type');
         $this->gradetypeformelement->updateAttributes(array('id' => $gradetypeformelementid));
 
@@ -188,7 +188,7 @@ class MoodleQuickForm_modgrade extends MoodleQuickForm_group {
                     'modgrade_rescalegrades',
                     $langrescalegrades,
                     $choices);
-                $rescalegradesselect->setHiddenLabel = false;
+                $rescalegradesselect->setHiddenLabel(true);
                 $rescalegradesselectid = $this->generate_modgrade_subelement_id('modgrade_rescalegrades');
                 $rescalegradesselect->updateAttributes(array('id' => $rescalegradesselectid));
             }
index 82153bc..1e57a0a 100644 (file)
@@ -26,6 +26,7 @@
  */
 
 require_once('HTML/QuickForm/password.php');
+require_once(__DIR__ . '/../outputcomponents.php');
 
 /**
  * Password type form element
@@ -37,7 +38,7 @@ require_once('HTML/QuickForm/password.php');
  * @copyright 2006 Jamie Pratt <me@jamiep.org>
  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
-class MoodleQuickForm_password extends HTML_QuickForm_password{
+class MoodleQuickForm_password extends HTML_QuickForm_password implements templatable {
     /** @var string, html for help button, if empty then no help */
     var $_helpbutton='';
 
@@ -83,4 +84,13 @@ class MoodleQuickForm_password extends HTML_QuickForm_password{
     function getHelpButton(){
         return $this->_helpbutton;
     }
+
+    public function export_for_template(renderer_base $output) {
+        $context = [];
+        $context['frozen'] = $this->_flagFrozen;
+        foreach ($this->getAttributes() as $name => $value) {
+            $context[$name] = $value;
+        }
+        return $context;
+    }
 }
index f043353..f086f84 100644 (file)
@@ -26,6 +26,7 @@
  */
 
 require_once('HTML/QuickForm/input.php');
+require_once(__DIR__ . '/../outputcomponents.php');
 
 /**
  * recaptcha type form element
@@ -37,7 +38,7 @@ require_once('HTML/QuickForm/input.php');
  * @copyright 2008 Nicolas Connault <nicolasconnault@gmail.com>
  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
-class MoodleQuickForm_recaptcha extends HTML_QuickForm_input {
+class MoodleQuickForm_recaptcha extends HTML_QuickForm_input implements templatable {
 
     /** @var string html for help button, if empty then no help */
     var $_helpbutton='';
@@ -152,4 +153,14 @@ class MoodleQuickForm_recaptcha extends HTML_QuickForm_input {
         }
         return true;
     }
+
+    public function export_for_template(renderer_base $output) {
+        $context = [];
+        $context['frozen'] = $this->_flagFrozen;
+        foreach ($this->getAttributes() as $name => $value) {
+            $context[$name] = $value;
+        }
+        $context['html'] = $this->toHtml();
+        return $context;
+    }
 }
index aa888cb..57c8504 100644 (file)
@@ -26,6 +26,7 @@
  */
 
 require_once('HTML/QuickForm/select.php');
+require_once(__DIR__ . '/../outputcomponents.php');
 
 /**
  * select type form element
@@ -37,7 +38,7 @@ require_once('HTML/QuickForm/select.php');
  * @copyright 2006 Jamie Pratt <me@jamiep.org>
  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
-class MoodleQuickForm_select extends HTML_QuickForm_select{
+class MoodleQuickForm_select extends HTML_QuickForm_select implements templatable {
     /** @var string html for help button, if empty then no help */
     var $_helpbutton='';
 
@@ -189,4 +190,33 @@ class MoodleQuickForm_select extends HTML_QuickForm_select{
             return $this->_prepareValue($cleaned[0], $assoc);
         }
     }
+
+    public function export_for_template(renderer_base $output) {
+        $context = [];
+        $context['frozen'] = $this->_flagFrozen;
+        $context['attributes'] = [];
+        foreach ($this->getAttributes() as $name => $value) {
+            $context[$name] = $value;
+            if (!in_array($name, ['id', 'name', 'multiple'])) {
+                $context['attributes'][] = ['name' => $name, 'value' => $value];
+            }
+        }
+
+        $options = [];
+        foreach ($this->_options as $option) {
+            if (is_array($this->_values) && in_array( (string) $option['attr']['value'], $this->_values)) {
+                $this->_updateAttrArray($option['attr'], ['selected' => 'selected']);
+            }
+            $o = [
+                'text' => $option['text'],
+                'value' => $option['attr']['value'],
+                'selected' => !empty($option['attr']['selected'])
+            ];
+            $options[] = $o;
+        }
+        $context['options'] = $options;
+        $context['hideLabel'] = $this->_hiddenLabel;
+
+        return $context;
+    }
 }
index c942acb..b277138 100644 (file)
@@ -26,6 +26,7 @@
  */
 
 require_once('HTML/QuickForm/element.php');
+require_once(__DIR__ . '/../outputcomponents.php');
 
 /**
  * select type form element
@@ -37,7 +38,7 @@ require_once('HTML/QuickForm/element.php');
  * @copyright 2007 Jamie Pratt <me@jamiep.org>
  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
-class MoodleQuickForm_selectgroups extends HTML_QuickForm_element {
+class MoodleQuickForm_selectgroups extends HTML_QuickForm_element implements templatable {
 
     /** @var bool add choose option */
     var $showchoose = false;
@@ -507,4 +508,48 @@ class MoodleQuickForm_selectgroups extends HTML_QuickForm_element {
             return 'default';
         }
     }
+
+    public function export_for_template(renderer_base $output) {
+        $context = [];
+        $context['frozen'] = $this->_flagFrozen;
+        $context['attributes'] = [];
+        foreach ($this->getAttributes() as $name => $value) {
+            $context[$name] = $value;
+            if (!in_array($name, ['id', 'name', 'multiple'])) {
+                $context['attributes'][] = ['name' => $name, 'value' => $value];
+            }
+        }
+        $optiongroups = [];
+        if ($this->showchoose) {
+            $optionsgroups[] = [
+                'text' => get_string('choosedots')
+            ];
+        }
+
+        foreach ($this->_optGroups as $group) {
+            $options = [];
+
+            foreach ($group['options'] as $option) {
+                $o = [];
+                if (is_array($this->_values) && in_array((string)$option['attr']['value'], $this->_values)) {
+                    $o['selected'] = true;
+                } else {
+                    $o['selected'] = false;
+                }
+                $o['text'] = $option['text'];
+                $options[] = $o;
+            }
+
+            $og = [
+                'text' => $group['attr']['label'],
+                'options' => $options
+            ];
+
+            $optiongroups[] = $og;
+        }
+        $context['optiongroups'] = $optiongroups;
+        $context['hideLabel'] = $this->_hiddenLabel;
+
+        return $context;
+    }
 }
index 78832d9..eb2a0d0 100644 (file)
@@ -32,6 +32,7 @@ require_once('HTML/QuickForm/select.php');
  *
  * HTML class for a select type element with options containing link
  *
+ * @deprecated since 3.2
  * @package   core_form
  * @category  form
  * @copyright 2008 Nicolas Connault <nicolasconnault@gmail.com>
@@ -63,6 +64,7 @@ class MoodleQuickForm_selectwithlink extends HTML_QuickForm_select{
      * @param bool $linkdata data to be posted
      */
     public function __construct($elementName=null, $elementLabel=null, $options=null, $attributes=null, $linkdata=null) {
+        debugging('Element type selectwithlink is deprecated. Use 2 elements.', DEBUG_DEVELOPER);
         if (!empty($linkdata['link']) && !empty($linkdata['label'])) {
             $this->_link = $linkdata['link'];
             $this->_linklabel = $linkdata['label'];
index 391be8e..d31329f 100644 (file)
@@ -26,6 +26,7 @@
  */
 
 require_once("HTML/QuickForm/static.php");
+require_once(__DIR__ . '/../outputcomponents.php');
 
 /**
  * Text type element
@@ -37,7 +38,7 @@ require_once("HTML/QuickForm/static.php");
  * @copyright 2006 Jamie Pratt <me@jamiep.org>
  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
-class MoodleQuickForm_static extends HTML_QuickForm_static{
+class MoodleQuickForm_static extends HTML_QuickForm_static implements templatable {
     /** @var string Form element type */
     var $_elementTemplateType='static';
 
@@ -82,4 +83,14 @@ class MoodleQuickForm_static extends HTML_QuickForm_static{
     function getElementTemplateType(){
         return $this->_elementTemplateType;
     }
+
+    public function export_for_template(renderer_base $output) {
+        $context = [];
+        $context['frozen'] = $this->_flagFrozen;
+        foreach ($this->getAttributes() as $name => $value) {
+            $context[$name] = $value;
+        }
+        $context['html'] = $this->toHtml();
+        return $context;
+    }
 }
index c0e2d96..7ccd0cc 100644 (file)
@@ -26,6 +26,7 @@
  */
 
 require_once("HTML/QuickForm/submit.php");
+require_once(__DIR__ . '/../outputcomponents.php');
 
 /**
  * submit type form element
@@ -37,7 +38,7 @@ require_once("HTML/QuickForm/submit.php");
  * @copyright 2006 Jamie Pratt <me@jamiep.org>
  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
-class MoodleQuickForm_submit extends HTML_QuickForm_submit {
+class MoodleQuickForm_submit extends HTML_QuickForm_submit implements templatable {
     /**
      * constructor
      *
@@ -108,4 +109,15 @@ class MoodleQuickForm_submit extends HTML_QuickForm_submit {
         $this->_flagFrozen = true;
     }
 
+    public function export_for_template(renderer_base $output) {
+        $context = [];
+        $context['frozen'] = $this->_flagFrozen;
+        foreach ($this->getAttributes() as $name => $value) {
+            $context[$name] = $value;
+        }
+        if ($this->getName() == 'cancel') {
+            $context['iscancel'] = true;
+        }
+        return $context;
+    }
 }
index bf238a5..0cb270c 100644 (file)
@@ -20,6 +20,7 @@
  *
  * Contains HTML class for a submitting to link
  *
+ * @deprecated since 3.2
  * @package   core_form
  * @copyright 2006 Jamie Pratt <me@jamiep.org>
  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
@@ -52,6 +53,7 @@ class MoodleQuickForm_submitlink extends MoodleQuickForm_submit {
      * @param string $attributes (optional) Either a typical HTML attribute string or an associative array
      */
     public function __construct($elementName=null, $value=null, $attributes=null) {
+        debugging('Element type submitlink is deprecated.', DEBUG_DEVELOPER);
         parent::__construct($elementName, $value, $attributes);
     }
 
diff --git a/lib/form/templates/editor_textarea.mustache b/lib/form/templates/editor_textarea.mustache
new file mode 100644 (file)
index 0000000..fbec7d1
--- /dev/null
@@ -0,0 +1,16 @@
+<div>
+<textarea id="{{id}}" name="{{name}}[text]" rows="{{rows}}" cols="{{cols}}" spellcheck="true" {{#changelistener}} onblur="{{onblur}}"
+    onchange="{{onchange}}" {{/changelistener}}>{{text}}</textarea>
+</div>
+<div>
+    {{^onlyoneformat}}
+        <select name="{{name}}[format]" id="menu{{name}}format">
+        {{#formats}}
+            <option value="{{value}}" {{#selected}}selected{{/selected}}>{{text}}</option>
+        {{/formats}}
+        </select>
+    {{/onlyoneformat}}
+    {{#onlyoneformat}}
+        <input name="{{name}}[format]" type="hidden" value="{{format}}"/>
+    {{/onlyoneformat}}
+</div>
\ No newline at end of file
index 8b8c558..0264a99 100644 (file)
@@ -26,6 +26,7 @@
  */
 
 require_once("HTML/QuickForm/text.php");
+require_once(__DIR__ . '/../outputcomponents.php');
 
 /**
  * Text type form element
@@ -37,7 +38,8 @@ require_once("HTML/QuickForm/text.php");
  * @copyright 2006 Jamie Pratt <me@jamiep.org>
  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
-class MoodleQuickForm_text extends HTML_QuickForm_text{
+class MoodleQuickForm_text extends HTML_QuickForm_text implements templatable {
+
     /** @var string html for help button, if empty then no help */
     var $_helpbutton='';
 
@@ -124,4 +126,14 @@ class MoodleQuickForm_text extends HTML_QuickForm_text{
     function getHelpButton(){
         return $this->_helpbutton;
     }
+
+    public function export_for_template(renderer_base $output) {
+        $context = [];
+        $context['frozen'] = $this->_flagFrozen;
+        foreach ($this->getAttributes() as $name => $value) {
+            $context[$name] = $value;
+        }
+        $context['hideLabel'] = $this->_hiddenLabel;
+        return $context;
+    }
 }
index 273fff5..34ef9fe 100644 (file)
@@ -26,6 +26,7 @@
  */
 
 require_once('HTML/QuickForm/textarea.php');
+require_once(__DIR__ . '/../outputcomponents.php');
 
 /**
  * Textarea type form element
@@ -37,7 +38,7 @@ require_once('HTML/QuickForm/textarea.php');
  * @copyright 2006 Jamie Pratt <me@jamiep.org>
  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
-class MoodleQuickForm_textarea extends HTML_QuickForm_textarea{
+class MoodleQuickForm_textarea extends HTML_QuickForm_textarea implements templatable {
     /** @var string Need to store id of form as we may need it for helpbutton */
     var $_formid = '';
 
@@ -130,4 +131,14 @@ class MoodleQuickForm_textarea extends HTML_QuickForm_textarea{
             return 'default';
         }
     }
+
+    public function export_for_template(renderer_base $output) {
+        $context = [];
+        $context['frozen'] = $this->_flagFrozen;
+        foreach ($this->getAttributes() as $name => $value) {
+            $context[$name] = $value;
+        }
+        $context['hideLabel'] = $this->_hiddenLabel;
+        return $context;
+    }
 }
index eb54220..47dfa7a 100644 (file)
@@ -26,6 +26,7 @@
  */
 
 require_once("HTML/QuickForm/text.php");
+require_once(__DIR__ . '/../outputcomponents.php');
 
 /**
  * url type form element
@@ -36,7 +37,7 @@ require_once("HTML/QuickForm/text.php");
  * @copyright 2009 Dongsheng Cai <dongsheng@moodle.com>
  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
-class MoodleQuickForm_url extends HTML_QuickForm_text{
+class MoodleQuickForm_url extends HTML_QuickForm_text implements templatable {
     /** @var string html for help button, if empty then no help */
     var $_helpbutton='';
 
@@ -61,7 +62,9 @@ class MoodleQuickForm_url extends HTML_QuickForm_text{
         if (!isset($this->_options['usefilepicker'])) {
             $this->_options['usefilepicker'] = true;
         }
+
         parent::__construct($elementName, $elementLabel, $attributes);
+        $this->_type = 'url';
     }
 
     /**
@@ -89,7 +92,7 @@ class MoodleQuickForm_url extends HTML_QuickForm_text{
      * @return string
      */
     function toHtml(){
-        global $PAGE, $OUTPUT;
+
 
         $id     = $this->_attributes['id'];
         $elname = $this->_attributes['name'];
@@ -105,6 +108,16 @@ class MoodleQuickForm_url extends HTML_QuickForm_text{
             return $str;
         }
 
+        // print out file picker
+        $str .= $this->getFilePickerHTML();
+
+        return $str;
+    }
+
+    function getFilePickerHTML() {
+        global $PAGE, $OUTPUT;
+
+        $str = '';
         $client_id = uniqid();
 
         $args = new stdClass();
@@ -119,7 +132,7 @@ class MoodleQuickForm_url extends HTML_QuickForm_text{
         if (count($options->repositories) > 0) {
             $straddlink = get_string('choosealink', 'repository');
             $str .= <<<EOD
-<button id="filepicker-button-js-{$client_id}" class="visibleifjs">
+<button id="filepicker-button-js-{$client_id}" class="visibleifjs btn btn-secondary">
 $straddlink
 </button>
 EOD;
@@ -157,4 +170,14 @@ EOD;
             return 'default';
         }
     }
+
+    public function export_for_template(renderer_base $output) {
+        $context = [];
+        $context['frozen'] = $this->_flagFrozen;
+        foreach ($this->getAttributes() as $name => $value) {
+            $context[$name] = $value;
+        }
+        $context['filepickerhtml'] = $this->getFilePickerHTML();
+        return $context;
+    }
 }
index 5164ff3..51ca7b7 100644 (file)
@@ -2091,6 +2091,8 @@ class MoodleQuickForm extends HTML_QuickForm_DHTMLRulesTableless {
      */
     function getValidationScript()
     {
+        global $PAGE;
+
         if (empty($this->_rules) || $this->clientvalidation === false) {
             return '';
         }
@@ -2170,67 +2172,74 @@ class MoodleQuickForm extends HTML_QuickForm_DHTMLRulesTableless {
         unset($element);
 
         $js = '
-<script type="text/javascript">
-//<![CDATA[
 
 var skipClientValidation = false;
 
-(function() {
-
-    function qf_errorHandler(element, _qfMsg, escapedName) {
-      div = element.parentNode;
 
-      if ((div == undefined) || (element.name == undefined)) {
-        //no checking can be done for undefined elements so let server handle it.
-        return true;
-      }
+require(["core/event", "jquery"], function(Event, $) {
 
-      if (_qfMsg != \'\') {
-        var errorSpan = document.getElementById(\'id_error_\' + escapedName);
-        if (!errorSpan) {
-          errorSpan = document.createElement("span");
-          errorSpan.id = \'id_error_\' + escapedName;
-          errorSpan.className = "error";
-          element.parentNode.insertBefore(errorSpan, element.parentNode.firstChild);
-          document.getElementById(errorSpan.id).setAttribute(\'TabIndex\', \'0\');
-          document.getElementById(errorSpan.id).focus();
-        }
+    function qf_errorHandler(element, _qfMsg, escapedName) {
+        var event = $.Event(Event.Events.FORM_FIELD_VALIDATION);
+        $(element).trigger(event, _qfMsg);
+        if (event.isDefaultPrevented()) {
+            return _qfMsg == \'\';
+        } else {
+            // Legacy mforms.
+            var div = element.parentNode;
+
+            if ((div == undefined) || (element.name == undefined)) {
+                // No checking can be done for undefined elements so let server handle it.
+                return true;
+            }
+
+            if (_qfMsg != \'\') {
+                var errorSpan = document.getElementById(\'id_error_\' + escapedName);
+                if (!errorSpan) {
+                    errorSpan = document.createElement("span");
+                    errorSpan.id = \'id_error_\' + escapedName;
+                    errorSpan.className = "error";
+                    element.parentNode.insertBefore(errorSpan, element.parentNode.firstChild);
+                    document.getElementById(errorSpan.id).setAttribute(\'TabIndex\', \'0\');
+                    document.getElementById(errorSpan.id).focus();
+                }
 
-        while (errorSpan.firstChild) {
-          errorSpan.removeChild(errorSpan.firstChild);
-        }
+                while (errorSpan.firstChild) {
+                    errorSpan.removeChild(errorSpan.firstChild);
+                }
 
-        errorSpan.appendChild(document.createTextNode(_qfMsg.substring(3)));
+                errorSpan.appendChild(document.createTextNode(_qfMsg.substring(3)));
 
-        if (div.className.substr(div.className.length - 6, 6) != " error"
-          && div.className != "error") {
-            div.className += " error";
-            linebreak = document.createElement("br");
-            linebreak.className = "error";
-            linebreak.id = \'id_error_break_\' + escapedName;
-            errorSpan.parentNode.insertBefore(linebreak, errorSpan.nextSibling);
-        }
+                if (div.className.substr(div.className.length - 6, 6) != " error"
+                        && div.className != "error") {
+                    div.className += " error";
+                    linebreak = document.createElement("br");
+                    linebreak.className = "error";
+                    linebreak.id = \'id_error_break_\' + escapedName;
+                    errorSpan.parentNode.insertBefore(linebreak, errorSpan.nextSibling);
+                }
 
-        return false;
-      } else {
-        var errorSpan = document.getElementById(\'id_error_\' + escapedName);
-        if (errorSpan) {
-          errorSpan.parentNode.removeChild(errorSpan);
-        }
-        var linebreak = document.getElementById(\'id_error_break_\' + escapedName);
-        if (linebreak) {
-          linebreak.parentNode.removeChild(linebreak);
-        }
+                return false;
+            } else {
+                var errorSpan = document.getElementById(\'id_error_\' + escapedName);
+                if (errorSpan) {
+                    errorSpan.parentNode.removeChild(errorSpan);
+                }
+                var linebreak = document.getElementById(\'id_error_break_\' + escapedName);
+                if (linebreak) {
+                    linebreak.parentNode.removeChild(linebreak);
+                }
 
-        if (div.className.substr(div.className.length - 6, 6) == " error") {
-          div.className = div.className.substr(0, div.className.length - 6);
-        } else if (div.className == "error") {
-          div.className = "";
-        }
+                if (div.className.substr(div.className.length - 6, 6) == " error") {
+                    div.className = div.className.substr(0, div.className.length - 6);
+                } else if (div.className == "error") {
+                    div.className = "";
+                }
 
-        return true;
-      }
-    }';
+                return true;
+            } // End if.
+        } // End if.
+    } // End function.
+    ';
         $validateJS = '';
         foreach ($test as $elementName => $jsandelement) {
             // Fix for bug displaying errors for elements in a group
@@ -2323,10 +2332,12 @@ var skipClientValidation = false;
             ev.preventDefault();
         }
     });
-})();
-//]]>
-</script>';
-        return $js;
+
+});
+';
+
+        $PAGE->requires->js_amd_inline($js);
+        return '';
     } // end func getValidationScript
 
     /**
@@ -2753,6 +2764,8 @@ class MoodleQuickForm_Renderer extends HTML_QuickForm_Renderer_Tableless{
      * @param string $error error message to display
      */
     function startGroup(&$group, $required, $error){
+        global $OUTPUT;
+
         // Make sure the element has an id.
         $group->_generateId();
 
@@ -2762,47 +2775,50 @@ class MoodleQuickForm_Renderer extends HTML_QuickForm_Renderer_Tableless{
         // Update the ID.
         $group->updateAttributes(array('id' => $groupid));
 
-        if (method_exists($group, 'getElementTemplateType')){
-            $html = $this->_elementTemplates[$group->getElementTemplateType()];
-        }else{
-            $html = $this->_elementTemplates['default'];
+        $html = $OUTPUT->mform_element($group, $required, $error, false);
+        $fromtemplate = !empty($html);
+        if (!$fromtemplate) {
+            if (method_exists($group, 'getElementTemplateType')) {
+                $html = $this->_elementTemplates[$group->getElementTemplateType()];
+            } else {
+                $html = $this->_elementTemplates['default'];
+            }
 
+            if (isset($this->_advancedElements[$group->getName()])) {
+                $html = str_replace(' {advanced}', ' advanced', $html);
+                $html = str_replace('{advancedimg}', $this->_advancedHTML, $html);
+            } else {
+                $html = str_replace(' {advanced}', '', $html);
+                $html = str_replace('{advancedimg}', '', $html);
+            }
+            if (method_exists($group, 'getHelpButton')) {
+                $html = str_replace('{help}', $group->getHelpButton(), $html);
+            } else {
+                $html = str_replace('{help}', '', $html);
+            }
+            $html = str_replace('{id}', $group->getAttribute('id'), $html);
+            $html = str_replace('{name}', $group->getName(), $html);
+            $html = str_replace('{type}', 'fgroup', $html);
+            $html = str_replace('{class}', $group->getAttribute('class'), $html);
+            $emptylabel = '';
+            if ($group->getLabel() == '') {
+                $emptylabel = 'femptylabel';
+            }
+            $html = str_replace('{emptylabel}', $emptylabel, $html);
         }
-
-        if (isset($this->_advancedElements[$group->getName()])){
-            $html =str_replace(' {advanced}', ' advanced', $html);
-            $html =str_replace('{advancedimg}', $this->_advancedHTML, $html);
-        } else {
-            $html =str_replace(' {advanced}', '', $html);
-            $html =str_replace('{advancedimg}', '', $html);
-        }
-        if (method_exists($group, 'getHelpButton')){
-            $html =str_replace('{help}', $group->getHelpButton(), $html);
-        }else{
-            $html =str_replace('{help}', '', $html);
-        }
-        $html = str_replace('{id}', $group->getAttribute('id'), $html);
-        $html =str_replace('{name}', $group->getName(), $html);
-        $html =str_replace('{type}', 'fgroup', $html);
-        $html =str_replace('{fieldtype}', 'group', $html);
-        $html =str_replace('{class}', $group->getAttribute('class'), $html);
-        $emptylabel = '';
-        if ($group->getLabel() == '') {
-            $emptylabel = 'femptylabel';
-        }
-        $html = str_replace('{emptylabel}', $emptylabel, $html);
-
-        $this->_templates[$group->getName()]=$html;
+        $this->_templates[$group->getName()] = $html;
         // Fix for bug in tableless quickforms that didn't allow you to stop a
         // fieldset before a group of elements.
         // if the element name indicates the end of a fieldset, close the fieldset
-        if (   in_array($group->getName(), $this->_stopFieldsetElements)
-            && $this->_fieldsetsOpen > 0
-           ) {
+        if (in_array($group->getName(), $this->_stopFieldsetElements) && $this->_fieldsetsOpen > 0) {
             $this->_html .= $this->_closeFieldsetTemplate;
             $this->_fieldsetsOpen--;
         }
-        parent::startGroup($group, $required, $error);
+        if (!$fromtemplate) {
+            parent::startGroup($group, $required, $error);
+        } else {
+            $this->_html .= $html;
+        }
     }
 
     /**
@@ -2813,56 +2829,66 @@ class MoodleQuickForm_Renderer extends HTML_QuickForm_Renderer_Tableless{
      * @param string $error error message to display
      */
     function renderElement(&$element, $required, $error){
+        global $OUTPUT;
+
         // Make sure the element has an id.
         $element->_generateId();
 
-        //adding stuff to place holders in template
-        //check if this is a group element first
-        if (($this->_inGroup) and !empty($this->_groupElementTemplate)) {
-            // so it gets substitutions for *each* element
-            $html = $this->_groupElementTemplate;
-        }
-        elseif (method_exists($element, 'getElementTemplateType')){
-            $html = $this->_elementTemplates[$element->getElementTemplateType()];
-        }else{
-            $html = $this->_elementTemplates['default'];
-        }
-        if (isset($this->_advancedElements[$element->getName()])){
-            $html = str_replace(' {advanced}', ' advanced', $html);
-            $html = str_replace(' {aria-live}', ' aria-live="polite"', $html);
-        } else {
-            $html = str_replace(' {advanced}', '', $html);
-            $html = str_replace(' {aria-live}', '', $html);
-        }
-        if (isset($this->_advancedElements[$element->getName()])||$element->getName() == 'mform_showadvanced'){
-            $html =str_replace('{advancedimg}', $this->_advancedHTML, $html);
+        $html = $OUTPUT->mform_element($element, $required, $error, false);
+        $fromtemplate = !empty($html);
+        if (!$fromtemplate) {
+            // Adding stuff to place holders in template
+            // check if this is a group element first.
+            if (($this->_inGroup) and !empty($this->_groupElementTemplate)) {
+                // So it gets substitutions for *each* element.
+                $html = $this->_groupElementTemplate;
+            } else if (method_exists($element, 'getElementTemplateType')){
+                $html = $this->_elementTemplates[$element->getElementTemplateType()];
+            } else {
+                $html = $this->_elementTemplates['default'];
+            }
+            if (isset($this->_advancedElements[$element->getName()])){
+                $html = str_replace(' {advanced}', ' advanced', $html);
+                $html = str_replace(' {aria-live}', ' aria-live="polite"', $html);
+            } else {
+                $html = str_replace(' {advanced}', '', $html);
+                $html = str_replace(' {aria-live}', '', $html);
+            }
+            if (isset($this->_advancedElements[$element->getName()]) || $element->getName() == 'mform_showadvanced'){
+                $html = str_replace('{advancedimg}', $this->_advancedHTML, $html);
+            } else {
+                $html = str_replace('{advancedimg}', '', $html);
+            }
+            $html = str_replace('{id}', 'fitem_' . $element->getAttribute('id'), $html);
+            $html = str_replace('{type}', 'f' . $element->getType(), $html);
+            $html = str_replace('{name}', $element->getName(), $html);
+            $html = str_replace('{class}', $element->getAttribute('class'), $html);
+            $emptylabel = '';
+            if ($element->getLabel() == '') {
+                $emptylabel = 'femptylabel';
+            }
+            $html = str_replace('{emptylabel}', $emptylabel, $html);
+            if (method_exists($element, 'getHelpButton')) {
+                $html = str_replace('{help}', $element->getHelpButton(), $html);
+            } else {
+                $html = str_replace('{help}', '', $html);
+            }
         } else {
-            $html =str_replace('{advancedimg}', '', $html);
-        }
-        $html =str_replace('{id}', 'fitem_' . $element->getAttribute('id'), $html);
-        $html =str_replace('{type}', 'f'.$element->getType(), $html);
-        $html =str_replace('{fieldtype}', $element->getType(), $html);
-        $html =str_replace('{name}', $element->getName(), $html);
-        $html =str_replace('{class}', $element->getAttribute('class'), $html);
-        $emptylabel = '';
-        if ($element->getLabel() == '') {
-            $emptylabel = 'femptylabel';
-        }
-        $html = str_replace('{emptylabel}', $emptylabel, $html);
-        if (method_exists($element, 'getHelpButton')){
-            $html = str_replace('{help}', $element->getHelpButton(), $html);
-        }else{
-            $html = str_replace('{help}', '', $html);
-
+            if ($this->_inGroup) {
+                $this->_groupElementTemplate = $html;
+            }
         }
         if (($this->_inGroup) and !empty($this->_groupElementTemplate)) {
             $this->_groupElementTemplate = $html;
-        }
-        elseif (!isset($this->_templates[$element->getName()])) {
+        } else if (!isset($this->_templates[$element->getName()])) {
             $this->_templates[$element->getName()] = $html;
         }
 
-        parent::renderElement($element, $required, $error);
+        if (!$fromtemplate) {
+            parent::renderElement($element, $required, $error);
+        } else { // CRAZZY!
+            $this->_html .= $html;
+        }
     }
 
     /**
index 285c27b..d2fefd7 100644 (file)
@@ -4421,6 +4421,51 @@ EOD;
         return $this->render_from_template('core/login', $context);
     }
 
+    /**
+     * Renders an mform element from a template.
+     *
+     * @param HTML_QuickForm_element $element element
+     * @param bool $required if input is required field
+     * @param string $error error message to display
+     * @param bool $ingroup True if this element is rendered as part of a group
+     * @return mixed string|bool
+     */
+    public function mform_element($element, $required, $error, $ingroup) {
+        $templatename = 'core_form/element-' . $element->getType();
+        if ($ingroup) {
+            $templatename .= "-inline";
+        }
+        try {
+            // We call this to generate a file not found exception if there is no template.
+            // We don't want to call export_for_template if there is no template.
+            core\output\mustache_template_finder::get_template_filepath($templatename);
+
+            if ($element instanceof templatable) {
+                $elementcontext = $element->export_for_template($this);
+
+                $helpbutton = '';
+                if (method_exists($element, 'getHelpButton')) {
+                    $helpbutton = $element->getHelpButton();
+                }
+                $label = $element->getLabel();
+                if (method_exists($element, 'getText')) {
+                    $label .= ' ' . $element->getText();
+                }
+
+                $context = array(
+                    'element' => $elementcontext,
+                    'label' => $label,
+                    'required' => $required,
+                    'helpbutton' => $helpbutton,
+                    'error' => $error
+                );
+                return $this->render_from_template($templatename, $context);
+            }
+        } catch (Exception $e) {
+            // No template for this element.
+            return false;
+        }
+    }
 }
 
 /**
index 46e03b9..222fc3d 100644 (file)
@@ -157,10 +157,17 @@ a.autolink.glossary:hover {
 .pagingbar .thispage {
     font-weight: bold;
 }
+img.userpicture {
+    margin-right: 0.5rem;
+}
 img.resize {
     height: 1em;
     width: 1em;
 }
+.action-menu .dropdown-toggle {
+    margin-right: 0.5rem;
+    text-decoration: none;
+}
 .action-menu {
     white-space: nowrap;
 }
@@ -168,6 +175,8 @@ img.resize {
     width: 16px;
     height: 16px;
     box-sizing: content-box;
+}
+.action-menu .dropdown-menu img {
     padding-right: 0.5rem;
 }
 .action-menu .userpicture {
index 8d3b8be..75baa84 100644 (file)
@@ -1,5 +1,10 @@
 /* course.less */
 /* COURSE CONTENT */
+.section-modchooser-link img {
+    margin-right: 0.5rem;
+    width: 16px;
+    height: 16px;
+}
 .section_add_menus {
     text-align: right;
     clear: both;
index 846b385..77002fb 100644 (file)
@@ -5,25 +5,12 @@
  *
  */
 
-form {
-    margin: 0;
-}
-.mform fieldset .advancedbutton {
-    text-align: right;
-}
 .jsenabled .mform .containsadvancedelements .advanced {
     display: none;
 }
 .mform .containsadvancedelements .advanced.show {
     display: block;
 }
-.mform fieldset.group {
-    margin-bottom: 0
-}
-.mform fieldset.error {
-    border: 1px solid $state-danger-text;
-}
-.mform span.error,
 #adminsettings span.error {
     display: inline-block;
     border: 1px solid $state-danger-border;
@@ -47,37 +34,9 @@ form {
 .jsenabled .mform .collapsed .fcontainer {
     display: none;
 }
-
-.mform .fitem .fitemtitle div {
-    display: inline;
-}
-#adminsettings .error,
-.mform .error {
+#adminsettings .error {
     color: $state-danger-text;
 }
-.mform .fdescription.required {
-    margin-left: $display1-size;
-}
-.mform .fpassword .unmask {
-    display: inline-block;
-    margin-left: 0.5em;
-    & > input {
-        margin: 0;
-    }
-    & > label {
-        display: inline-block;
-    }
-}
-.mform label {
-    display: inline-block;
-}
-
-.mform .iconhelp {
-    margin-left: 4px;
-}
-.mform .ftextarea #id_alltext {
-    width: 100%;
-}
 .mform ul.file-list {
     padding: 0;
     margin: 0;
@@ -87,39 +46,6 @@ form {
 .mform label .adv {
     cursor: help;
 }
-.mform .fcheckbox input {
-    margin-left: 0;
-}
-.mform .fcheckbox > span,
-.mform .fradio > span,
-.mform .fgroup > span {
-    display: inline-block;
-    margin-top: 5px;
-}
-.mform .fitem fieldset.fgroup label,
-.mform fieldset.fdate_selector label {
-    display: inline;
-    float: none;
-    width: auto;
-}
-.mform .ftags label.accesshide {
-    display: block;
-    position: static;
-}
-.mform .ftags select {
-    margin-bottom: 0.7em;
-    min-width: 22em;
-}
-.mform .helplink img {
-    margin: 0 .45em;
-    padding: 0;
-}
-.mform legend .helplink img {
-    margin: 0 .2em;
-}
-.singleselect label {
-    margin-right: .3em;
-}
 /*rtl:ignore*/
 input#id_externalurl {
     direction: ltr;
@@ -129,8 +55,7 @@ input#id_externalurl {
 }
 
 // Copying in Bootstrap styles.
-.form-item,
-.mform .fitem {
+.form-item {
     margin-bottom: 10px;
     // Theres's a mysterious extra 10px inside this item,
     // so reduce margin by 10px from 20px standard to compensate.
@@ -145,7 +70,6 @@ input#id_externalurl {
 }
 .form-item .form-setting,
 .form-item .form-description,
-.mform .fitem .felement,
 #page-mod-forum-search .c1 .form-horizontal {
     @extend .controls;
 }
@@ -159,14 +83,14 @@ input#id_externalurl {
 // uneditable looks ugly, styling both as form help is fairly
 // subtle in it's impact. Going for the latter as the best option.
 .form-item .form-description,
-.felement.fstatic {
+.no-felement.fstatic {
     @extend .text-muted;
     padding-top: 5px;
 }
 .form-item .form-description {
     padding-top: 0;
 }
-.fitem .fstaticlabel {
+.no-fitem .fstaticlabel {
     font-weight: bold;
 }
 
@@ -204,11 +128,6 @@ div.backup-section + form,
     border: 0;
     border-bottom: 1px solid #e5e5e5;
 }
-.mform legend a,
-.mform legend a:hover {
-    color: $body-color;
-    text-decoration: none;
-}
 
 // I think this could be avoided (or at least tidied up) ifr
 // we used HTML5 input types like url, phone, email, number etc.
@@ -306,32 +225,9 @@ div.backup-section + form,
     display: inline-block;
 }
 
-// Checkbox labels. Bootstrap puts the associated checkbox inside the label.
-// Moodle puts it beside the label, so we need to make it inline-block
-// to keep it on the same horizontal level.
-input[type="radio"] + label,
-input[type="checkbox"] + label {
-    display: inline;
-    padding-left: 0.2em;
-}
-input[type="radio"],
-input[type="checkbox"] {
-    margin-top: -4px; // Dodgy hack, must be better way.
-    margin-right: 7px;
-}
-.singleselect {
-    display: inline-block;
-    form,
-    select{
-        margin: 0;
-    }
-}
 .form-item .form-label label {
     margin-bottom: 0;
 }
-.felement.ffilepicker {
-    margin-top: 5px;
-}
 div#dateselector-calendar-panel {
     z-index: 3100; /* Set higher than the z-index of the filemanager - see MDL-39047. */
 }
@@ -343,89 +239,8 @@ fieldset.coursesearchbox label {
 /**
  * Show the labels above text editors and file managers except on wide screens.
  */
-#region-main .mform:not(.unresponsive) .fitem .fitemtitle label {
-    font-weight: bold;
-}
-@mixin makeFormsVertical() {
-    #region-main .mform:not(.unresponsive) {
-        .fitem {
-            .fitemtitle {
-                display: block;
-                margin-top: 4px;
-                margin-bottom: 4px;
-                text-align: left;
-                width: 100%;
-            }
-            .felement {
-                margin-left: 0;
-                width: 100%;
-                float: left;
-                padding-left: 0;
-                padding-right: 0;
-            }
-            .fstatic:empty {
-                display: none;
-            }
-            .fcheckbox > span,
-            .fradio > span,
-            .fgroup > span {
-                margin-top: 4px;
-            }
-
-        }
-        .femptylabel {
-            .fitemtitle {
-                display: inline-block;
-                width: auto;
-                margin-right: 8px;
-            }
-            .felement {
-                display: inline-block;
-                margin-top: 4px;
-                padding-top: 5px;
-                width: auto;
-            }
-        }
-        .fitem_fcheckbox{
-            .fitemtitle,
-            .felement {
-                display: inline-block;
-                width: auto;
-            }
-            .felement {
-                padding: 6px;
-            }
-        }
-    }
-}
-
-/**
- * Make forms vertical when the screen is less than 1200px;
- */
-@media (max-width: 1199px) {
-    body {
-        @include makeFormsVertical();
-    }
-}
-
 
 
-/**
- * Make forms vertical when the screen is less than 1474px AND both side-pre and side-post contain blocks.
- * This is an extra special media rule.
- * It causes forms to show vertically when the screen size is calculated as:
- * 1199px + (1199px * 23%)
- * Where 23% is the width of span3
- * Full calculation is:
- *   $maxWidthForVerticalForms: 1199px * (unit((($fluidGridColumnWidth1200 * 3) + ($fluidGridGutterWidth * (3 - 1)))) / 100) + 1199px;
- */
-$maxWidthForVerticalForms: 1474px;
-@media (max-width: $maxWidthForVerticalForms) {
-    .used-region-side-pre.used-region-side-post {
-        @include makeFormsVertical();
-    }
-}
-
 /* Section and module editing forms contain special JS components for the
    availability system (if enabled). */
 #fitem_id_availabilityconditionsjson {
@@ -538,13 +353,6 @@ $maxWidthForVerticalForms: 1474px;
     }
 }
 
-/* Revert to the non-fixed width where a textarea has the number of columns
-   specified, or an input has it's size specified. */
-textarea[cols],
-input[size] {
-    width: auto;
-}
-
 /* Custom styles for autocomplete form element */
 .form-autocomplete-selection {
     margin: 0.2em;
@@ -564,7 +372,7 @@ input[size] {
     overflow: auto;
     margin: 0px;
     padding: 0px;
-    margin-top: -0.2em;
+    margin-top: 0.4em;
     z-index: 1;
 }
 .form-autocomplete-suggestions li {
@@ -586,7 +394,7 @@ input[size] {
 .form-autocomplete-downarrow {
     color: $body-color;
     position: relative;
-    top: -0.3em;
+    top: 0.2em;
     left: -1.5em;
     cursor: pointer;
 }
@@ -598,3 +406,7 @@ input[size] {
     padding: 0.5em;
     font-size: large;
 }
+
+select[multiple] {
+    overflow: auto;
+}
diff --git a/theme/noname/templates/core/form_autocomplete_input.mustache b/theme/noname/templates/core/form_autocomplete_input.mustache
new file mode 100644 (file)
index 0000000..89bb1a4
--- /dev/null
@@ -0,0 +1,43 @@
+{{!
+    This file is part of Moodle - http://moodle.org/
+
+    Moodle is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    Moodle is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+}}
+{{!
+    @template core/form_autocomplete_input
+
+    Moodle template for the input field in an autocomplate form element.
+
+    Classes required for JS:
+    * none
+
+    Data attributes required for JS:
+    * none
+
+    Context variables required for this template:
+    * inputId The dom id of this input field.
+    * suggestionsId The dom id of the suggestions list.
+    * selectionId The dom id of the current selection list.
+    * downArrowId The dom id of the down arrow to open the suggestions.
+    * placeholder The place holder text when the field is empty
+
+    Example context (json):
+    { "inputID": 1, "suggestionsId": 2, "selectionId": 3, "downArrowId": 4, "placeholder": "Select something" }
+}}
+{{#showSuggestions}}
+<input type="text" id="{{inputId}}" class="form-control col-md-4" list="{{suggestionsId}}" placeholder="{{placeholder}}" role="combobox" aria-expanded="false" autocomplete="off" autocorrect="off" autocapitalize="off" aria-autocomplete="list" aria-owns="{{suggestionsId}} {{selectionId}}"/><span class="form-autocomplete-downarrow" id="{{downArrowId}}">&#x25BC;</span>
+{{/showSuggestions}}
+{{^showSuggestions}}
+<input type="text" id="{{inputId}}" placeholder="{{placeholder}}" role="textbox" aria-owns="{{selectionId}}"/>
+{{/showSuggestions}}
diff --git a/theme/noname/templates/core/form_autocomplete_selection.mustache b/theme/noname/templates/core/form_autocomplete_selection.mustache
new file mode 100644 (file)
index 0000000..16f5010
--- /dev/null
@@ -0,0 +1,51 @@
+{{!
+    This file is part of Moodle - http://moodle.org/
+
+    Moodle is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    Moodle is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+}}
+{{!
+    @template core/form_autocomplete_selection
+
+    Moodle template for the currently selected items in an autocomplate form element.
+
+    Classes required for JS:
+    * none
+
+    Data attributes required for JS:
+    * none
+
+    Context variables required for this template:
+    * multiple True if this field allows multiple selections
+    * selectionId The dom id of the current selection list.
+    * items List of items with label and value fields.
+    * noSelectionString String to use when no items are selected
+
+    Example context (json):
+    { "multiple": true, "selectionId": 1, "items": [
+        { "label": "Item label with <strong>tags</strong>", "value": "5" },
+        { "label": "Another item label with <strong>tags</strong>", "value": "4" }
+    ], "noSelectionString": "No selection" }
+}}
+<div class="form-autocomplete-selection {{#multiple}}form-autocomplete-multiple{{/multiple}}" id="{{selectionId}}" role="list" aria-atomic="true" {{#multiple}}tabindex="0" aria-multiselectable="true"{{/multiple}}>
+<span class="accesshide">{{#str}}selecteditems, form{{/str}}</span>
+    {{#items}}
+        <span role="listitem" data-value="{{value}}" aria-selected="true" class="tag tag-info">
+            {{#multiple}}<span aria-hidden="true">× </span>{{/multiple}}{{{label}}}
+        </span>
+    {{/items}}
+    {{^items}}
+        <span>{{noSelectionString}}</span>
+    {{/items}}
+</div>
+</div>
diff --git a/theme/noname/templates/core_form/editor_textarea.mustache b/theme/noname/templates/core_form/editor_textarea.mustache
new file mode 100644 (file)
index 0000000..b256f9d
--- /dev/null
@@ -0,0 +1,16 @@
+<div>
+<textarea id="{{id}}" name="{{name}}[text]" class="form-control" rows="{{rows}}" cols="{{cols}}" spellcheck="true" {{#changelistener}} onblur="{{onblur}}"
+    onchange="{{onchange}}" {{/changelistener}}>{{text}}</textarea>
+</div>
+<div>
+    {{^onlyoneformat}}
+        <select name="{{name}}[format]" id="menu{{name}}format" class="custom-select">
+        {{#formats}}
+            <option value="{{value}}" {{#selected}}selected{{/selected}}>{{text}}</option>
+        {{/formats}}
+        </select>
+    {{/onlyoneformat}}
+    {{#onlyoneformat}}
+        <input name="{{name}}[format]" id="menu{{name}}format" type="hidden" value="{{format}}"/>
+    {{/onlyoneformat}}
+</div>
\ No newline at end of file
diff --git a/theme/noname/templates/core_form/element-advcheckbox-inline.mustache b/theme/noname/templates/core_form/element-advcheckbox-inline.mustache
new file mode 100644 (file)
index 0000000..67ed18e
--- /dev/null
@@ -0,0 +1,3 @@
+<input type="hidden" name="{{element.name}}"
+            value="{{element.value}}">
+{{> core_form/element-checkbox-inline }}
\ No newline at end of file
diff --git a/theme/noname/templates/core_form/element-advcheckbox.mustache b/theme/noname/templates/core_form/element-advcheckbox.mustache
new file mode 100644 (file)
index 0000000..2b64950
--- /dev/null
@@ -0,0 +1,3 @@
+<input type="hidden" name="{{element.name}}"
+            value="{{element.value}}">
+{{> core_form/element-checkbox }}
\ No newline at end of file
diff --git a/theme/noname/templates/core_form/element-autocomplete.mustache b/theme/noname/templates/core_form/element-autocomplete.mustache
new file mode 100644 (file)
index 0000000..03ae368
--- /dev/null
@@ -0,0 +1,47 @@
+<div class="form-group row {{#error}}has-danger{{/error}}">
+    <label class="col-md-3 col-form-label" for="{{element.id}}">
+        {{{label}}} {{{helpbutton}}}
+    </label>
+    <div class="col-md-9 form-inline">
+        <select class="custom-select {{#error}}form-control-danger{{/error}}" name="{{element.name}}"
+            id="{{element.id}}"
+            {{#element.multiple}}multiple{{/element.multiple}}
+            {{#element.frozen}}readonly disabled{{/element.frozen}}
+            {{#error}}
+                autofocus aria-describedby="id_error_{{element.name}}"
+            {{/error}}
+            {{#element.attributes}}
+                {{name}}={{#quote}}{{value}}{{/quote}} ddd
+            {{/element.attributes}}
+            >
+            {{#element.options}}
+            <option value="{{value}}" {{#selected}}selected{{/selected}}>{{text}}</option>
+            {{/element.options}}
+        </select>
+        <div class="form-control-feedback" id="id_error_{{element.name}}" {{^error}} style="display: none;"{{/error}}>
+            {{{error}}}
+        </div>
+    </div>
+</div>
+{{#js}}
+require(['jquery', 'core/event'], function($, Event) {
+    var element = document.getElementById('{{element.id}}');
+    $(element).on(Event.Events.FORM_FIELD_VALIDATION, function(event, msg) {
+        event.preventDefault();
+        var parent = $(element).closest('.form-group');
+        var feedback = parent.find('.form-control-feedback');
+        if (msg !== '') {
+            parent.addClass('has-danger');
+            $(element).addClass('form-control-danger');
+            $(element).attr('aria-describedby', $(element).attr('id') + '-feedback');
+            feedback.html(msg);
+            feedback.show();
+        } else {
+            parent.removeClass('has-danger');
+            $(element).removeClass('form-control-danger');
+            $(element).attr('aria-describedby', '');
+            feedback.hide();
+        }
+    });
+});
+{{/js}}
\ No newline at end of file
diff --git a/theme/noname/templates/core_form/element-button-inline.mustache b/theme/noname/templates/core_form/element-button-inline.mustache
new file mode 100644 (file)
index 0000000..96abc13
--- /dev/null
@@ -0,0 +1,11 @@
+    <div class="form-group">
+        <label class="col-md-3 col-form-label {{#element.hideLabel}}sr-only{{/element.hideLabel}}" for="{{element.id}}">
+            {{{label}}} {{{helpbutton}}}
+        </label>
+        <button {{#element.frozen}}readonly{{/element.frozen}}
+                class="btn btn-secondary"
+                name="{{element.name}}"
+                id="{{element.id}}" >
+                {{{element.value}}}
+        </button>
+    </div>
\ No newline at end of file
diff --git a/theme/noname/templates/core_form/element-button.mustache b/theme/noname/templates/core_form/element-button.mustache
new file mode 100644 (file)
index 0000000..996a3cb
--- /dev/null
@@ -0,0 +1,16 @@
+<div class="form-group row">
+    <label class="col-md-3 col-form-label {{#element.hideLabel}}sr-only{{/element.hideLabel}}" for="{{element.id}}">
+        {{{label}}} {{{helpbutton}}}
+    </label>
+    <div class="col-md-9 form-inline">
+        <button {{#element.frozen}}readonly{{/element.frozen}}
+                class="btn btn-secondary m-l-0"
+                name="{{element.name}}"
+                id="{{element.id}}"
+                {{#error}}
+                    autofocus aria-describedby="id_error_{{element.name}}"
+                {{/error}} >
+                {{{element.value}}}
+                </button>
+    </div>
+</div>
diff --git a/theme/noname/templates/core_form/element-checkbox-inline.mustache b/theme/noname/templates/core_form/element-checkbox-inline.mustache
new file mode 100644 (file)
index 0000000..5400d74
--- /dev/null
@@ -0,0 +1,32 @@
+<label class="form-check-inline {{#error}}has-danger{{/error}}">
+<input type="checkbox" name="{{element.name}}"
+    id="{{element.id}}" value="{{element.value}}" size="{{element.size}}"
+    {{#element.frozen}}readonly disabled{{/element.frozen}}
+    {{#error}}
+        autofocus aria-describedby="id_error_{{element.name}}"
+    {{/error}} >
+    {{{label}}} {{{helpbutton}}}
+</label>
+<span class="form-control-feedback" id="id_error_{{element.name}}" {{^error}} style="display: none;"{{/error}}>
+    {{{error}}}
+</span>
+{{#js}}
+require(['jquery', 'core/event'], function($, Event) {
+    var element = document.getElementById('{{element.id}}');
+    $(element).on(Event.Events.FORM_FIELD_VALIDATION, function(event, msg) {
+        event.preventDefault();
+        var parent = $(element).closest('.form-group');
+        var feedback = parent.find('.form-control-feedback');
+        if (msg !== '') {
+            parent.addClass('has-danger');
+            $(element).attr('aria-describedby', $(element).attr('id') + '-feedback');
+            feedback.html(msg);
+            feedback.show();
+        } else {
+            parent.removeClass('has-danger');
+            $(element).attr('aria-describedby', '');
+            feedback.hide();
+        }
+    });
+});
+{{/js}}
\ No newline at end of file
diff --git a/theme/noname/templates/core_form/element-checkbox.mustache b/theme/noname/templates/core_form/element-checkbox.mustache
new file mode 100644 (file)
index 0000000..5122bd9
--- /dev/null
@@ -0,0 +1,36 @@
+<div class="form-group row {{#error}}has-danger{{/error}}">
+    <div class="col-md-9 push-md-3 checkbox">
+        <label>
+        <input type="checkbox" name="{{element.name}}"
+            id="{{element.id}}" value="{{element.value}}" size="{{element.size}}"
+            {{#element.frozen}}readonly disabled{{/element.frozen}}
+            {{#error}}
+                autofocus aria-describedby="id_error_{{element.name}}"
+            {{/error}} >
+            {{{label}}} {{{helpbutton}}}
+        </label>
+        <div class="form-control-feedback" id="id_error_{{element.name}}" {{^error}} style="display: none;"{{/error}}>
+            {{{error}}}
+        </div>
+    </div>
+</div>
+{{#js}}
+require(['jquery', 'core/event'], function($, Event) {
+    var element = document.getElementById('{{element.id}}');
+    $(element).on(Event.Events.FORM_FIELD_VALIDATION, function(event, msg) {
+        event.preventDefault();
+        var parent = $(element).closest('.form-group');
+        var feedback = parent.find('.form-control-feedback');
+        if (msg !== '') {
+            parent.addClass('has-danger');
+            $(element).attr('aria-describedby', $(element).attr('id') + '-feedback');
+            feedback.html(msg);
+            feedback.show();
+        } else {
+            parent.removeClass('has-danger');
+            $(element).attr('aria-describedby', '');
+            feedback.hide();
+        }
+    });
+});
+{{/js}}
\ No newline at end of file
diff --git a/theme/noname/templates/core_form/element-date_selector.mustache b/theme/noname/templates/core_form/element-date_selector.mustache
new file mode 100644 (file)
index 0000000..645b084
--- /dev/null
@@ -0,0 +1,10 @@
+<div class="form-group row {{#error}}has-danger{{/error}}">
+    <label class="col-md-3 col-form-label" for="{{element.id}}">
+        {{{label}}} {{{helpbutton}}}
+    </label>
+    <div class="col-md-9 form-inline fdate_selector">
+        {{#element.elements}}
+            {{{.}}}
+        {{/element.elements}}
+    </div>
+</div>
\ No newline at end of file
diff --git a/theme/noname/templates/core_form/element-date_time_selector.mustache b/theme/noname/templates/core_form/element-date_time_selector.mustache
new file mode 100644 (file)
index 0000000..be63490
--- /dev/null
@@ -0,0 +1,13 @@
+<div class="form-group row {{#error}}has-danger{{/error}}">
+    <label class="col-md-3 col-form-label" for="{{element.id}}">
+        {{{label}}} {{{helpbutton}}}
+    </label>
+    <div class="col-md-9 form-inline fdate_time_selector">
+        {{#element.elements}}
+            {{{.}}}
+        {{/element.elements}}
+    </div>
+    <div class="form-control-feedback" id="id_error_{{element.name}}" {{^error}} style="display: none;"{{/error}}>
+        {{{error}}}
+    </div>
+</div>
\ No newline at end of file
diff --git a/theme/noname/templates/core_form/element-duration.mustache b/theme/noname/templates/core_form/element-duration.mustache
new file mode 100644 (file)
index 0000000..7e804bb
--- /dev/null
@@ -0,0 +1 @@
+{{> core_form/element-group }}
\ No newline at end of file
diff --git a/theme/noname/templates/core_form/element-editor.mustache b/theme/noname/templates/core_form/element-editor.mustache
new file mode 100644 (file)
index 0000000..be56779
--- /dev/null
@@ -0,0 +1,33 @@
+<div class="form-group row {{#error}}has-danger{{/error}}">
+    <label class="col-md-3 col-form-label {{#element.hideLabel}}sr-only{{/element.hideLabel}}" for="{{element.id}}">
+        {{{label}}} {{{helpbutton}}}
+    </label>
+    <div class="col-md-9">
+        {{{element.html}}}
+        <div class="form-control-feedback" id="id_error_{{element.name}}_5btext_5d" {{^error}} style="display: none;"{{/error}}>
+            {{{error}}}
+        </div>
+    </div>
+</div>
+{{#js}}
+require(['jquery', 'core/event'], function($, Event) {
+    var element = document.getElementById('{{element.id}}');
+    $(element).on(Event.Events.FORM_FIELD_VALIDATION, function(event, msg) {
+        event.preventDefault();
+        var parent = $(element).closest('.form-group');
+        var feedback = parent.find('.form-control-feedback');
+        if (msg !== '') {
+            parent.addClass('has-danger');
+            $(element).addClass('form-control-danger');
+            $(element).attr('aria-describedby', $(element).attr('id') + '-feedback');
+            feedback.html(msg);
+            feedback.show();
+        } else {
+            parent.removeClass('has-danger');
+            $(element).removeClass('form-control-danger');
+            $(element).attr('aria-describedby', '');
+            feedback.hide();
+        }
+    });
+});
+{{/js}}
\ No newline at end of file
diff --git a/theme/noname/templates/core_form/element-filemanager.mustache b/theme/noname/templates/core_form/element-filemanager.mustache
new file mode 100644 (file)
index 0000000..02403ac
--- /dev/null
@@ -0,0 +1,33 @@
+<div class="form-group row {{#error}}has-danger{{/error}}">
+    <label class="col-md-3 col-form-label {{#element.hideLabel}}sr-only{{/element.hideLabel}}" for="{{element.id}}">
+        {{{label}}} {{{helpbutton}}}
+    </label>
+    <div class="col-md-9">
+        {{{element.html}}}
+        <div class="form-control-feedback" id="id_error_{{element.name}}" {{^error}} style="display: none;"{{/error}}>
+            {{{error}}}
+        </div>
+    </div>
+</div>
+{{#js}}
+require(['jquery', 'core/event'], function($, Event) {
+    var element = document.getElementById('{{element.id}}');
+    $(element).on(Event.Events.FORM_FIELD_VALIDATION, function(event, msg) {
+        event.preventDefault();
+        var parent = $(element).closest('.form-group');
+        var feedback = parent.find('.form-control-feedback');
+        if (msg !== '') {
+            parent.addClass('has-danger');
+            $(element).addClass('form-control-danger');
+            $(element).attr('aria-describedby', $(element).attr('id') + '-feedback');
+            feedback.html(msg);
+            feedback.show();
+        } else {
+            parent.removeClass('has-danger');
+            $(element).removeClass('form-control-danger');
+            $(element).attr('aria-describedby', '');
+            feedback.hide();
+        }
+    });
+});
+{{/js}}
\ No newline at end of file
diff --git a/theme/noname/templates/core_form/element-filepicker.mustache b/theme/noname/templates/core_form/element-filepicker.mustache
new file mode 100644 (file)
index 0000000..02403ac
--- /dev/null
@@ -0,0 +1,33 @@
+<div class="form-group row {{#error}}has-danger{{/error}}">
+    <label class="col-md-3 col-form-label {{#element.hideLabel}}sr-only{{/element.hideLabel}}" for="{{element.id}}">
+        {{{label}}} {{{helpbutton}}}
+    </label>
+    <div class="col-md-9">
+        {{{element.html}}}
+        <div class="form-control-feedback" id="id_error_{{element.name}}" {{^error}} style="display: none;"{{/error}}>
+            {{{error}}}
+        </div>
+    </div>
+</div>
+{{#js}}
+require(['jquery', 'core/event'], function($, Event) {
+    var element = document.getElementById('{{element.id}}');
+    $(element).on(Event.Events.FORM_FIELD_VALIDATION, function(event, msg) {
+        event.preventDefault();
+        var parent = $(element).closest('.form-group');
+        var feedback = parent.find('.form-control-feedback');
+        if (msg !== '') {
+            parent.addClass('has-danger');
+            $(element).addClass('form-control-danger');
+            $(element).attr('aria-describedby', $(element).attr('id') + '-feedback');
+            feedback.html(msg);
+            feedback.show();
+        } else {
+            parent.removeClass('has-danger');
+            $(element).removeClass('form-control-danger');
+            $(element).attr('aria-describedby', '');
+            feedback.hide();
+        }
+    });
+});
+{{/js}}
\ No newline at end of file
diff --git a/theme/noname/templates/core_form/element-grading.mustache b/theme/noname/templates/core_form/element-grading.mustache
new file mode 100644 (file)
index 0000000..9c4e7b7
--- /dev/null
@@ -0,0 +1,33 @@
+<div class="form-group row {{#error}}has-danger{{/error}}">
+    <label class="col-md-3 col-form-label {{#element.hideLabel}}sr-only{{/element.hideLabel}}" for="{{element.id}}">
+        {{{label}}} {{{helpbutton}}}
+    </label>
+    <div class="col-md-9">
+        {{{element}}}
+        <div class="form-control-feedback" id="id_error_{{element.name}}" {{^error}} style="display: none;"{{/error}}>
+            {{{error}}}
+        </div>
+    </div>
+</div>
+{{#js}}
+require(['jquery', 'core/event'], function($, Event) {
+    var element = document.getElementById('{{element.id}}');
+    $(element).on(Event.Events.FORM_FIELD_VALIDATION, function(event, msg) {
+        event.preventDefault();
+        var parent = $(element).closest('.form-group');
+        var feedback = parent.find('.form-control-feedback');
+        if (msg !== '') {
+            parent.addClass('has-danger');
+            $(element).addClass('form-control-danger');
+            $(element).attr('aria-describedby', $(element).attr('id') + '-feedback');
+            feedback.html(msg);
+            feedback.show();
+        } else {
+            parent.removeClass('has-danger');
+            $(element).removeClass('form-control-danger');
+            $(element).attr('aria-describedby', '');
+            feedback.hide();
+        }
+    });
+});
+{{/js}}
\ No newline at end of file
diff --git a/theme/noname/templates/core_form/element-group.mustache b/theme/noname/templates/core_form/element-group.mustache
new file mode 100644 (file)
index 0000000..d93f000
--- /dev/null
@@ -0,0 +1,10 @@
+<div class="form-group row {{#error}}has-danger{{/error}}">
+    <label class="col-md-3 col-form-label" for="{{element.id}}">
+        {{{label}}} {{{helpbutton}}}
+    </label>
+    <div class="col-md-9 form-inline">
+        {{#element.elements}}
+            {{{.}}}
+        {{/element.elements}}
+    </div>
+</div>
\ No newline at end of file
diff --git a/theme/noname/templates/core_form/element-modgrade.mustache b/theme/noname/templates/core_form/element-modgrade.mustache
new file mode 100644 (file)
index 0000000..7e804bb
--- /dev/null
@@ -0,0 +1 @@
+{{> core_form/element-group }}
\ No newline at end of file
diff --git a/theme/noname/templates/core_form/element-modvisible.mustache b/theme/noname/templates/core_form/element-modvisible.mustache
new file mode 100644 (file)
index 0000000..5878ca9
--- /dev/null
@@ -0,0 +1 @@
+{{> core_form/element-select }}
\ No newline at end of file
diff --git a/theme/noname/templates/core_form/element-password.mustache b/theme/noname/templates/core_form/element-password.mustache
new file mode 100644 (file)
index 0000000..8811260
--- /dev/null
@@ -0,0 +1,49 @@
+<div class="form-group row {{#error}}has-danger{{/error}}">
+    <label class="col-md-3 col-form-label {{#element.hideLabel}}sr-only{{/element.hideLabel}}" for="{{element.id}}">
+        {{{label}}} {{{helpbutton}}}
+    </label>
+    <div class="col-md-9">
+        <input type="password"
+                {{#element.frozen}}readonly{{/element.frozen}}
+                class="form-control {{#error}}form-control-danger{{/error}}"
+                name="{{element.name}}"
+                id="{{element.id}}"
+                value="{{element.value}}"
+                size="{{element.size}}"
+                {{#error}}
+                    autofocus aria-describedby="id_error_{{element.name}}"
+                {{/error}} >
+        <div class="form-control-feedback" id="id_error_{{element.name}}" {{^error}} style="display: none;"{{/error}}>
+            {{{error}}}
+        </div>
+    </div>
+</div>
+{{#js}}
+require(['core/yui'], function(Y) {
+    Y.use('moodle-form-passwordunmask', function() {
+        M.form.passwordunmask({ formid: {{#quote}}{{element.id}}{{/quote}},
+                                checkboxlabel: '{{#str}}unmaskpassword, form{{/str}}',
+                                checkboxname: {{#quote}}{{element.name}}{{/quote}} });
+    });
+});
+require(['jquery', 'core/event'], function($, Event) {
+    var element = document.getElementById('{{element.id}}');
+    $(element).on(Event.Events.FORM_FIELD_VALIDATION, function(event, msg) {
+        event.preventDefault();
+        var parent = $(element).closest('.form-group');
+        var feedback = parent.find('.form-control-feedback');
+        if (msg !== '') {
+            parent.addClass('has-danger');
+            $(element).addClass('form-control-danger');
+            $(element).attr('aria-describedby', $(element).attr('id') + '-feedback');
+            feedback.html(msg);
+            feedback.show();
+        } else {
+            parent.removeClass('has-danger');
+            $(element).removeClass('form-control-danger');
+            $(element).attr('aria-describedby', '');
+            feedback.hide();
+        }
+    });
+});
+{{/js}}
\ No newline at end of file
diff --git a/theme/noname/templates/core_form/element-questioncategory-inline.mustache b/theme/noname/templates/core_form/element-questioncategory-inline.mustache
new file mode 100644 (file)
index 0000000..b46bf08
--- /dev/null
@@ -0,0 +1 @@
+{{> core_form/element-selectgroups-inline }}
\ No newline at end of file
diff --git a/theme/noname/templates/core_form/element-questioncategory.mustache b/theme/noname/templates/core_form/element-questioncategory.mustache
new file mode 100644 (file)
index 0000000..b98d62a
--- /dev/null
@@ -0,0 +1 @@
+{{> core_form/element-selectgroups }}
\ No newline at end of file
diff --git a/theme/noname/templates/core_form/element-recaptcha.mustache b/theme/noname/templates/core_form/element-recaptcha.mustache
new file mode 100644 (file)
index 0000000..02403ac
--- /dev/null
@@ -0,0 +1,33 @@
+<div class="form-group row {{#error}}has-danger{{/error}}">
+    <label class="col-md-3 col-form-label {{#element.hideLabel}}sr-only{{/element.hideLabel}}" for="{{element.id}}">
+        {{{label}}} {{{helpbutton}}}
+    </label>
+    <div class="col-md-9">
+        {{{element.html}}}
+        <div class="form-control-feedback" id="id_error_{{element.name}}" {{^error}} style="display: none;"{{/error}}>
+            {{{error}}}
+        </div>
+    </div>
+</div>
+{{#js}}
+require(['jquery', 'core/event'], function($, Event) {
+    var element = document.getElementById('{{element.id}}');
+    $(element).on(Event.Events.FORM_FIELD_VALIDATION, function(event, msg) {
+        event.preventDefault();
+        var parent = $(element).closest('.form-group');
+        var feedback = parent.find('.form-control-feedback');
+        if (msg !== '') {
+            parent.addClass('has-danger');
+            $(element).addClass('form-control-danger');
+            $(element).attr('aria-describedby', $(element).attr('id') + '-feedback');
+            feedback.html(msg);
+            feedback.show();
+        } else {
+            parent.removeClass('has-danger');
+            $(element).removeClass('form-control-danger');
+            $(element).attr('aria-describedby', '');
+            feedback.hide();
+        }
+    });
+});
+{{/js}}
\ No newline at end of file
diff --git a/theme/noname/templates/core_form/element-select-inline.mustache b/theme/noname/templates/core_form/element-select-inline.mustache
new file mode 100644 (file)
index 0000000..97432a6
--- /dev/null
@@ -0,0 +1,44 @@
+<div class="form-group {{#error}}has-danger{{/error}}">
+    <label for="{{element.id}}" class="{{#element.hideLabel}}sr-only{{/element.hideLabel}}">
+        {{{label}}} {{{helpbutton}}}
+    </label>
+    <select class="custom-select {{#error}}form-control-danger{{/error}}" name="{{element.name}}"
+        id="{{element.id}}"
+        {{#element.multiple}}multiple{{/element.multiple}}
+        {{#element.frozen}}readonly disabled{{/element.frozen}}
+        {{#error}}
+            autofocus aria-describedby="id_error_{{element.name}}"
+        {{/error}}
+        {{#element.attributes}}
+            {{name}}={{#quote}}{{value}}{{/quote}}
+        {{/element.attributes}}>
+        {{#element.options}}
+        <option value="{{value}}" {{#selected}}selected{{/selected}}>{{text}}</option>
+        {{/element.options}}
+    </select>
+    <div class="form-control-feedback" id="id_error_{{element.name}}" {{^error}} style="display: none;"{{/error}}>
+        {{{error}}}
+    </div>
+</div>
+{{#js}}
+require(['jquery', 'core/event'], function($, Event) {
+    var element = document.getElementById('{{element.id}}');
+    $(element).on(Event.Events.FORM_FIELD_VALIDATION, function(event, msg) {
+        event.preventDefault();
+        var parent = $(element).closest('.form-group');
+        var feedback = parent.find('.form-control-feedback');
+        if (msg !== '') {
+            parent.addClass('has-danger');
+            $(element).addClass('form-control-danger');
+            $(element).attr('aria-describedby', $(element).attr('id') + '-feedback');
+            feedback.html(msg);
+            feedback.show();
+        } else {
+            parent.removeClass('has-danger');
+            $(element).removeClass('form-control-danger');
+            $(element).attr('aria-describedby', '');
+            feedback.hide();
+        }
+    });
+});
+{{/js}}
\ No newline at end of file
diff --git a/theme/noname/templates/core_form/element-select.mustache b/theme/noname/templates/core_form/element-select.mustache
new file mode 100644 (file)
index 0000000..003d811
--- /dev/null
@@ -0,0 +1,47 @@
+<div class="form-group row {{#error}}has-danger{{/error}}">
+    <label class="col-md-3 col-form-label" for="{{element.id}}">
+        {{{label}}} {{{helpbutton}}}
+    </label>
+    <div class="col-md-9">
+        <select class="custom-select {{#error}}form-control-danger{{/error}}" name="{{element.name}}"
+            id="{{element.id}}"
+            {{#element.multiple}}multiple{{/element.multiple}}
+            {{#element.frozen}}readonly disabled{{/element.frozen}}
+            {{#error}}
+                autofocus aria-describedby="id_error_{{element.name}}"
+            {{/error}}
+            {{#element.attributes}}
+                {{name}}={{#quote}}{{value}}{{/quote}} ddd
+            {{/element.attributes}}
+            >
+            {{#element.options}}
+            <option value="{{value}}" {{#selected}}selected{{/selected}}>{{text}}</option>
+            {{/element.options}}
+        </select>
+        <div class="form-control-feedback" id="id_error_{{element.name}}" {{^error}} style="display: none;"{{/error}}>
+            {{{error}}}
+        </div>
+    </div>
+</div>
+{{#js}}
+require(['jquery', 'core/event'], function($, Event) {
+    var element = document.getElementById('{{element.id}}');
+    $(element).on(Event.Events.FORM_FIELD_VALIDATION, function(event, msg) {
+        event.preventDefault();
+        var parent = $(element).closest('.form-group');
+        var feedback = parent.find('.form-control-feedback');
+        if (msg !== '') {
+            parent.addClass('has-danger');
+            $(element).addClass('form-control-danger');
+            $(element).attr('aria-describedby', $(element).attr('id') + '-feedback');
+            feedback.html(msg);
+            feedback.show();
+        } else {
+            parent.removeClass('has-danger');
+            $(element).removeClass('form-control-danger');
+            $(element).attr('aria-describedby', '');
+            feedback.hide();
+        }
+    });
+});
+{{/js}}
\ No newline at end of file
diff --git a/theme/noname/templates/core_form/element-selectgroups-inline.mustache b/theme/noname/templates/core_form/element-selectgroups-inline.mustache
new file mode 100644 (file)
index 0000000..f57bfe7
--- /dev/null
@@ -0,0 +1,44 @@
+<div class="form-group {{#error}}has-danger{{/error}}">
+    <label for="{{element.id}}" class="{{#element.hideLabel}}sr-only{{/element.hideLabel}}">
+        {{{label}}} {{{helpbutton}}}
+    </label>
+    <select class="form-control {{#error}}form-control-danger{{/error}}" name="{{element.name}}"
+        id="{{element.id}}"
+        {{#element.multiple}}multiple{{/element.multiple}}
+        {{#element.frozen}}readonly disabled{{/element.frozen}}
+        {{#error}}
+            autofocus aria-describedby="id_error_{{element.name}}"
+        {{/error}}
+        {{#element.attributes}}
+            {{name}}={{#quote}}{{value}}{{/quote}}
+        {{/element.attributes}}>
+        {{#element.options}}
+        <option value="{{value}}" {{#selected}}selected{{/selected}}>{{{text}}}</option>
+        {{/element.options}}
+    </select>
+    <div class="form-control-feedback" id="id_error_{{element.name}}" {{^error}} style="display: none;"{{/error}}>
+        {{{error}}}
+    </div>
+</div>
+{{#js}}
+require(['jquery', 'core/event'], function($, Event) {
+    var element = document.getElementById('{{element.id}}');
+    $(element).on(Event.Events.FORM_FIELD_VALIDATION, function(event, msg) {
+        event.preventDefault();
+        var parent = $(element).closest('.form-group');
+        var feedback = parent.find('.form-control-feedback');
+        if (msg !== '') {
+            parent.addClass('has-danger');
+            $(element).addClass('form-control-danger');
+            $(element).attr('aria-describedby', $(element).attr('id') + '-feedback');
+            feedback.html(msg);
+            feedback.show();
+        } else {
+            parent.removeClass('has-danger');
+            $(element).removeClass('form-control-danger');
+            $(element).attr('aria-describedby', '');
+            feedback.hide();
+        }
+    });
+});
+{{/js}}
\ No newline at end of file
diff --git a/theme/noname/templates/core_form/element-selectgroups.mustache b/theme/noname/templates/core_form/element-selectgroups.mustache
new file mode 100644 (file)
index 0000000..756f1e8
--- /dev/null
@@ -0,0 +1,51 @@
+<div class="form-group row {{#error}}has-danger{{/error}}">
+    <label class="col-md-3 col-form-label" for="{{element.id}}">
+        {{{label}}} {{{helpbutton}}}
+    </label>
+    <div class="col-md-9">
+        <select class="form-control {{#error}}form-control-danger{{/error}}" name="{{element.name}}"
+            id="{{element.id}}"
+            {{#element.multiple}}multiple{{/element.multiple}}
+            {{#element.frozen}}readonly disabled{{/element.frozen}}
+            {{#error}}
+                autofocus aria-describedby="id_error_{{element.name}}"
+            {{/error}}
+            {{#element.attributes}}
+                {{name}}={{#quote}}{{value}}{{/quote}}
+            {{/element.attributes}}
+            >
+            {{#element.optiongroups}}
+            <optgroup label="{{text}}">
+            {{#options}}
+            <option value="{{value}}" {{#selected}}selected{{/selected}}>{{{text}}}</option>
+            {{/options}}
+            {{/element.optiongroups}}
+            </optgroup>
+        </select>
+        <div class="form-control-feedback" id="id_error_{{element.name}}" {{^error}} style="display: none;"{{/error}}>
+            {{{error}}}
+        </div>
+    </div>
+</div>
+{{#js}}
+require(['jquery', 'core/event'], function($, Event) {
+    var element = document.getElementById('{{element.id}}');
+    $(element).on(Event.Events.FORM_FIELD_VALIDATION, function(event, msg) {
+        event.preventDefault();
+        var parent = $(element).closest('.form-group');
+        var feedback = parent.find('.form-control-feedback');
+        if (msg !== '') {
+            parent.addClass('has-danger');
+            $(element).addClass('form-control-danger');
+            $(element).attr('aria-describedby', $(element).attr('id') + '-feedback');
+            feedback.html(msg);
+            feedback.show();
+        } else {
+            parent.removeClass('has-danger');
+            $(element).removeClass('form-control-danger');
+            $(element).attr('aria-describedby', '');
+            feedback.hide();
+        }
+    });
+});
+{{/js}}
\ No newline at end of file
diff --git a/theme/noname/templates/core_form/element-selectyesno-inline.mustache b/theme/noname/templates/core_form/element-selectyesno-inline.mustache
new file mode 100644 (file)
index 0000000..de47ade
--- /dev/null
@@ -0,0 +1 @@
+{{> core_form/element-select-inline }}
\ No newline at end of file
diff --git a/theme/noname/templates/core_form/element-selectyesno.mustache b/theme/noname/templates/core_form/element-selectyesno.mustache
new file mode 100644 (file)
index 0000000..5878ca9
--- /dev/null
@@ -0,0 +1 @@
+{{> core_form/element-select }}
\ No newline at end of file
diff --git a/theme/noname/templates/core_form/element-static.mustache b/theme/noname/templates/core_form/element-static.mustache
new file mode 100644 (file)
index 0000000..7963d86
--- /dev/null
@@ -0,0 +1,33 @@
+<div class="form-group row fitem {{#error}}has-danger{{/error}}">
+    <label class="col-md-3 col-form-label {{#element.hideLabel}}sr-only{{/element.hideLabel}}" for="{{element.id}}">
+        {{{label}}} {{{helpbutton}}}
+    </label>
+    <div class="col-md-9">
+        {{{element.html}}}
+        <div class="form-control-feedback" id="id_error_{{element.name}}" {{^error}} style="display: none;"{{/error}}>
+            {{{error}}}
+        </div>
+    </div>
+</div>
+{{#js}}
+require(['jquery', 'core/event'], function($, Event) {
+    var element = document.getElementById('{{element.id}}');
+    $(element).on(Event.Events.FORM_FIELD_VALIDATION, function(event, msg) {
+        event.preventDefault();
+        var parent = $(element).closest('.form-group');
+        var feedback = parent.find('.form-control-feedback');
+        if (msg !== '') {
+            parent.addClass('has-danger');
+            $(element).addClass('form-control-danger');
+            $(element).attr('aria-describedby', $(element).attr('id') + '-feedback');
+            feedback.html(msg);
+            feedback.show();
+        } else {
+            parent.removeClass('has-danger');
+            $(element).removeClass('form-control-danger');
+            $(element).attr('aria-describedby', '');
+            feedback.hide();
+        }
+    });
+});
+{{/js}}
\ No newline at end of file
diff --git a/theme/noname/templates/core_form/element-submit-inline.mustache b/theme/noname/templates/core_form/element-submit-inline.mustache
new file mode 100644 (file)
index 0000000..425a5f9
--- /dev/null
@@ -0,0 +1,10 @@
+    <div class="form-group">
+        <label class="col-form-label {{#element.hideLabel}}sr-only{{/element.hideLabel}}" for="{{element.id}}">
+            {{{label}}} {{{helpbutton}}}
+        </label>
+        <input type="submit" {{#element.frozen}}readonly{{/element.frozen}}
+                class="btn btn-primary"
+                name="{{element.name}}"
+                id="{{element.id}}"
+                value="{{element.value}}" >
+    </div>
\ No newline at end of file
diff --git a/theme/noname/templates/core_form/element-submit.mustache b/theme/noname/templates/core_form/element-submit.mustache
new file mode 100644 (file)
index 0000000..f6bb32e
--- /dev/null
@@ -0,0 +1,15 @@
+<div class="form-group row">
+    <label class="col-md-3 col-form-label {{#element.hideLabel}}sr-only{{/element.hideLabel}}" for="{{element.id}}">
+        {{{label}}} {{{helpbutton}}}
+    </label>
+    <div class="col-md-9 form-inline">
+        <input type="submit" {{#element.frozen}}readonly{{/element.frozen}}
+                class="btn {{^iscancel}}btn-primary{{/iscancel}}{{#iscancel}}btn-secondary{{/iscancel}} m-l-0"
+                name="{{element.name}}"
+                id="{{element.id}}"
+                {{#error}}
+                    autofocus aria-describedby="id_error_{{element.name}}"
+                {{/error}}
+                value="{{element.value}}">
+    </div>
+</div>
diff --git a/theme/noname/templates/core_form/element-text-inline.mustache b/theme/noname/templates/core_form/element-text-inline.mustache
new file mode 100644 (file)
index 0000000..e18121d
--- /dev/null
@@ -0,0 +1,40 @@
+<div class="form-group {{#error}}has-danger{{/error}}">
+    <label class="{{#element.hideLabel}}sr-only{{/element.hideLabel}}" for="{{element.id}}">
+        {{{label}}} {{{helpbutton}}}
+    </label>
+    <input type="text"
+            {{#element.frozen}}readonly{{/element.frozen}}
+            class="form-control {{#error}}form-control-danger{{/error}}"
+            name="{{element.name}}"
+            id="{{element.id}}"
+            value="{{element.value}}"
+            size="{{element.size}}"
+            {{#error}}
+                autofocus aria-describedby="id_error_{{element.name}}"
+            {{/error}} >
+    <div class="form-control-feedback" id="id_error_{{element.name}}" {{^error}} style="display: none;"{{/error}}>
+        {{{error}}}
+    </div>
+</div>
+{{#js}}
+require(['jquery', 'core/event'], function($, Event) {
+    var element = document.getElementById('{{element.id}}');
+    $(element).on(Event.Events.FORM_FIELD_VALIDATION, function(event, msg) {
+        event.preventDefault();
+        var parent = $(element).closest('.form-group');
+        var feedback = parent.find('.form-control-feedback');
+        if (msg !== '') {
+            parent.addClass('has-danger');
+            $(element).addClass('form-control-danger');
+            $(element).attr('aria-describedby', $(element).attr('id') + '-feedback');
+            feedback.html(msg);
+            feedback.show();
+        } else {
+            parent.removeClass('has-danger');
+            $(element).removeClass('form-control-danger');
+            $(element).attr('aria-describedby', '');
+            feedback.hide();
+        }
+    });
+});
+{{/js}}
\ No newline at end of file
diff --git a/theme/noname/templates/core_form/element-text.mustache b/theme/noname/templates/core_form/element-text.mustache
new file mode 100644 (file)
index 0000000..2dc6233
--- /dev/null
@@ -0,0 +1,42 @@
+<div class="form-group row {{#error}}has-danger{{/error}}">
+    <label class="col-md-3 col-form-label {{#element.hideLabel}}sr-only{{/element.hideLabel}}" for="{{element.id}}">
+        {{{label}}} {{{helpbutton}}}
+    </label>
+    <div class="col-md-9">
+        <input type="text"
+                {{#element.frozen}}readonly{{/element.frozen}}
+                class="form-control {{#error}}form-control-danger{{/error}}"
+                name="{{element.name}}"
+                id="{{element.id}}"
+                value="{{element.value}}"
+                size="{{element.size}}"
+                {{#error}}
+                    autofocus aria-describedby="id_error_{{element.name}}"
+                {{/error}} >
+        <div class="form-control-feedback" id="id_error_{{element.name}}" {{^error}} style="display: none;"{{/error}}>
+            {{{error}}}
+        </div>
+    </div>
+</div>
+{{#js}}
+require(['jquery', 'core/event'], function($, Event) {
+    var element = document.getElementById('{{element.id}}');
+    $(element).on(Event.Events.FORM_FIELD_VALIDATION, function(event, msg) {
+        event.preventDefault();
+        var parent = $(element).closest('.form-group');
+        var feedback = parent.find('.form-control-feedback');
+        if (msg !== '') {
+            parent.addClass('has-danger');
+            $(element).addClass('form-control-danger');
+            $(element).attr('aria-describedby', $(element).attr('id') + '-feedback');
+            feedback.html(msg);
+            feedback.show();
+        } else {
+            parent.removeClass('has-danger');
+            $(element).removeClass('form-control-danger');
+            $(element).attr('aria-describedby', '');
+            feedback.hide();
+        }
+    });
+});
+{{/js}}
\ No newline at end of file
diff --git a/theme/noname/templates/core_form/element-textarea.mustache b/theme/noname/templates/core_form/element-textarea.mustache
new file mode 100644 (file)
index 0000000..ac463e9
--- /dev/null
@@ -0,0 +1,33 @@
+<div class="form-group row fitem {{#error}}has-danger{{/error}}" id="fitem_{{element.id}}">
+    <label class="col-md-3 col-form-label {{#element.hideLabel}}sr-only{{/element.hideLabel}}" for="{{element.id}}">
+        {{{label}}} {{{helpbutton}}}
+    </label>
+    <div class="col-md-9">
+        <textarea name="{{element.name}}" id="{{element.id}}">{{element.value}}</textarea>
+        <div class="form-control-feedback" id="id_error_{{element.name}}" {{^error}} style="display: none;"{{/error}}>
+            {{{error}}}
+        </div>
+    </div>
+</div>
+{{#js}}
+require(['jquery', 'core/event'], function($, Event) {
+    var element = document.getElementById('{{element.id}}');
+    $(element).on(Event.Events.FORM_FIELD_VALIDATION, function(event, msg) {
+        event.preventDefault();
+        var parent = $(element).closest('.form-group');
+        var feedback = parent.find('.form-control-feedback');
+        if (msg !== '') {
+            parent.addClass('has-danger');
+            $(element).addClass('form-control-danger');
+            $(element).attr('aria-describedby', $(element).attr('id') + '-feedback');
+            feedback.html(msg);
+            feedback.show();
+        } else {
+            parent.removeClass('has-danger');
+            $(element).removeClass('form-control-danger');
+            $(element).attr('aria-describedby', '');
+            feedback.hide();
+        }
+    });
+});
+{{/js}}
\ No newline at end of file
diff --git a/theme/noname/templates/core_form/element-url.mustache b/theme/noname/templates/core_form/element-url.mustache
new file mode 100644 (file)
index 0000000..88c6f01
--- /dev/null
@@ -0,0 +1,43 @@
+<div class="form-group row {{#error}}has-danger{{/error}}">
+    <label class="col-md-3 col-form-label {{#element.hideLabel}}sr-only{{/element.hideLabel}}" for="{{element.id}}">
+        {{{label}}} {{{helpbutton}}}
+    </label>
+    <div class="col-md-9">
+        <input type="url"
+                {{#element.frozen}}readonly{{/element.frozen}}
+                class="form-control {{#error}}form-control-danger{{/error}}"
+                name="{{element.name}}"
+                id="{{element.id}}"
+                value="{{element.value}}"
+                size="{{element.size}}"
+                {{#error}}
+                    autofocus aria-describedby="id_error_{{element.name}}"
+                {{/error}} >
+                {{{element.filepickerhtml}}}
+        <div class="form-control-feedback" id="id_error_{{element.name}}" {{^error}} style="display: none;"{{/error}}>
+            {{{error}}}
+        </div>
+    </div>
+</div>
+{{#js}}
+require(['jquery', 'core/event'], function($, Event) {
+    var element = document.getElementById('{{element.id}}');
+    $(element).on(Event.Events.FORM_FIELD_VALIDATION, function(event, msg) {
+        event.preventDefault();
+        var parent = $(element).closest('.form-group');
+        var feedback = parent.find('.form-control-feedback');
+        if (msg !== '') {
+            parent.addClass('has-danger');
+            $(element).addClass('form-control-danger');
+            $(element).attr('aria-describedby', $(element).attr('id') + '-feedback');
+            feedback.html(msg);
+            feedback.show();
+        } else {
+            parent.removeClass('has-danger');
+            $(element).removeClass('form-control-danger');
+            $(element).attr('aria-describedby', '');
+            feedback.hide();
+        }
+    });
+});
+{{/js}}
\ No newline at end of file
diff --git a/theme/noname/templates/core_form/element-warning.mustache b/theme/noname/templates/core_form/element-warning.mustache
new file mode 100644 (file)
index 0000000..368b019
--- /dev/null
@@ -0,0 +1,10 @@
+<div class="form-group row fitem has-danger">
+    <label class="col-md-3 col-form-label {{#element.hideLabel}}sr-only{{/element.hideLabel}}" for="{{element.id}}">
+        {{{label}}} {{{helpbutton}}}
+    </label>
+    <div class="col-md-9">
+        <div class="form-control-feedback" id="id_error_{{element.name}}">
+            {{{element.html}}}
+        </div>
+    </div>
+</div>
\ No newline at end of file