Merge branch 'wip-MDL-57464-master' of git://github.com/abgreeve/moodle
authorDan Poltawski <dan@moodle.com>
Mon, 2 Jan 2017 11:16:27 +0000 (11:16 +0000)
committerDan Poltawski <dan@moodle.com>
Mon, 2 Jan 2017 11:16:27 +0000 (11:16 +0000)
78 files changed:
admin/environment.xml
admin/tool/lpimportcsv/classes/framework_importer.php
admin/tool/lpimportcsv/continue.php [deleted file]
admin/tool/lpimportcsv/index.php
admin/tool/lpimportcsv/lang/en/tool_lpimportcsv.php
auth/classes/output/login.php
auth/tests/behat/rememberusername.feature [new file with mode: 0644]
calendar/renderer.php
course/externallib.php
course/tests/externallib_test.php
lang/en/admin.php
lib/adminlib.php
lib/classes/output/mustache_template_finder.php
lib/db/access.php
lib/db/upgrade.php
lib/formslib.php
lib/javascript-static.js
lib/navigationlib.php
lib/outputrenderers.php
lib/phpmailer/moodle_phpmailer.php
lib/templates/action_menu.mustache
lib/templates/action_menu_item.mustache
lib/templates/action_menu_link.mustache
lib/templates/action_menu_trigger.mustache
lib/templates/actions.mustache
lib/templates/chart.mustache
lib/templates/chooser.mustache
lib/templates/chooser_item.mustache
lib/templates/dataformat_selector.mustache
lib/templates/email_fromname.mustache
lib/templates/email_html.mustache
lib/templates/email_subject.mustache
lib/templates/email_text.mustache
lib/templates/help_icon.mustache
lib/templates/hover_tooltip.mustache
lib/templates/loading.mustache
lib/templates/login.mustache
lib/templates/pix_icon.mustache
lib/templates/progress_bar.mustache
lib/templates/select_time.mustache
lib/templates/signup_form_layout.mustache
lib/templates/skip_links.mustache
lib/upgrade.txt
lib/upgradelib.php
message/amd/build/message_area_messages.min.js
message/amd/src/message_area_messages.js
message/templates/add_contact_button.mustache
message/templates/remove_contact_button.mustache
mod/lesson/locallib.php
mod/quiz/lib.php
mod/scorm/version.php
mod/scorm/view.js
report/log/classes/renderable.php
report/log/classes/table_log.php
report/log/lang/en/report_log.php
theme/boost/scss/moodle/core.scss
theme/boost/scss/moodle/filemanager.scss
theme/boost/settings.php
theme/boost/templates/admin_setting_tabs.mustache
theme/boost/templates/core/action_menu.mustache
theme/boost/templates/core/action_menu_item.mustache
theme/boost/templates/core/action_menu_link.mustache
theme/boost/templates/core/chooser.mustache
theme/boost/templates/core/dataformat_selector.mustache
theme/boost/templates/core/help_icon.mustache
theme/boost/templates/core/progress_bar.mustache
theme/boost/templates/core/select_time.mustache
theme/boost/templates/core/signup_form_layout.mustache
theme/boost/templates/core/skip_links.mustache
theme/boost/templates/core_form/element-advcheckbox-inline.mustache
theme/boost/templates/core_form/element-advcheckbox.mustache
theme/boost/templates/core_form/element-checkbox-inline.mustache
theme/boost/templates/core_form/element-checkbox.mustache
theme/boost/templates/nav-drawer.mustache
theme/bootstrapbase/less/moodle/core.less
theme/bootstrapbase/style/moodle.css
theme/bootstrapbase/templates/theme_boost/admin_setting_tabs.mustache
version.php

index 8c35fb6..55502ee 100644 (file)
           <ON_CHECK message="unoconvwarning" />
         </FEEDBACK>
       </CUSTOM_CHECK>
-      <CUSTOM_CHECK file="lib/upgradelib.php" function="check_tls_libraries" level="optional">
-        <FEEDBACK>
-          <ON_CHECK message="tlswarning" />
-        </FEEDBACK>
-      </CUSTOM_CHECK>
       <CUSTOM_CHECK file="lib/upgradelib.php" function="check_libcurl_version" level="optional">
         <FEEDBACK>
           <ON_CHECK message="libcurlwarning" />
index e784814..82b3516 100644 (file)
@@ -53,6 +53,10 @@ class framework_importer {
     protected $importer = null;
     protected $foundheaders = array();
     protected $scalecache = array();
+    /** @var bool $useprogressbar Control whether importing should use progress bars or not. */
+    protected $useprogressbar = false;
+    /** @var \core\progress\display_if_slow|null $progress The progress bar instance. */
+    protected $progress = null;
 
     /**
      * Store an error message for display later
@@ -164,8 +168,11 @@ class framework_importer {
      * @param string delimiter The specified delimiter for the file.
      * @param string importid The id of the csv import.
      * @param array mappingdata The mapping data from the import form.
+     * @param bool $useprogressbar Whether progress bar should be displayed, to avoid html output on CLI.
      */
-    public function __construct($text = null, $encoding = null, $delimiter = null, $importid = 0, $mappingdata = null) {
+    public function __construct($text = null, $encoding = null, $delimiter = null, $importid = 0, $mappingdata = null,
+            $useprogressbar = false) {
+
         global $CFG;
 
         // The format of our records is:
@@ -204,7 +211,7 @@ class framework_importer {
         }
 
         $this->foundheaders = $this->importer->get_columns();
-
+        $this->useprogressbar = $useprogressbar;
         $domainid = 1;
 
         $flat = array();
@@ -264,8 +271,19 @@ class framework_importer {
             $this->fail(get_string('invalidimportfile', 'tool_lpimportcsv'));
             return;
         } else {
+            // We are calling from browser, display progress bar.
+            if ($this->useprogressbar === true) {
+                $this->progress = new \core\progress\display_if_slow(get_string('processingfile', 'tool_lpimportcsv'));
+                $this->progress->start_html();
+            } else {
+                // Avoid html output on CLI scripts.
+                $this->progress = new \core\progress\none();
+            }
+            $this->progress->start_progress('', count($this->flat));
             // Build a tree from this flat list.
+            raise_memory_limit(MEMORY_EXTRA);
             $this->add_children($this->framework, '');
+            $this->progress->end_progress();
         }
     }
 
@@ -278,6 +296,7 @@ class framework_importer {
     public function add_children(& $node, $parentidnumber) {
         foreach ($this->flat as $competency) {
             if ($competency->parentidnumber == $parentidnumber) {
+                $this->progress->increment_progress();
                 $node->children[] = $competency;
                 $this->add_children($competency, $competency->idnumber);
             }
@@ -443,17 +462,28 @@ class framework_importer {
         $record->contextid = context_system::instance()->id;
 
         $framework = api::create_framework($record);
+        if ($this->useprogressbar === true) {
+            $this->progress = new \core\progress\display_if_slow(get_string('importingfile', 'tool_lpimportcsv'));
+            $this->progress->start_html();
+        } else {
+            $this->progress = new \core\progress\none();
+        }
 
+        $this->progress->start_progress('', (count($this->framework->children) * 2));
+        raise_memory_limit(MEMORY_EXTRA);
         // Now all the children.
         foreach ($this->framework->children as $comp) {
+            $this->progress->increment_progress();
             $this->create_competency($comp, null, $framework);
         }
 
         // Now create the rules.
         foreach ($this->framework->children as $record) {
+            $this->progress->increment_progress();
             $this->set_rules($record);
             $this->set_related($record);
         }
+        $this->progress->end_progress();
 
         $this->importer->cleanup();
         return $framework;
diff --git a/admin/tool/lpimportcsv/continue.php b/admin/tool/lpimportcsv/continue.php
deleted file mode 100644 (file)
index ef98a48..0000000
+++ /dev/null
@@ -1,47 +0,0 @@
-<?php
-// 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/>.
-
-/**
- * Page to continue after an action.
- *
- * @package    tool_lpimportcsv
- * @copyright  2015 Damyon Wiese
- * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-
-require_once(__DIR__ . '/../../../config.php');
-require_once($CFG->libdir.'/adminlib.php');
-
-$pagetitle = get_string('pluginname', 'tool_lpimportcsv');
-
-$context = context_system::instance();
-
-$id = required_param('id', PARAM_INT);
-$url = new moodle_url("/admin/tool/lpimportcsv/index.php");
-$PAGE->set_context($context);
-$PAGE->set_url($url);
-$PAGE->set_title($pagetitle);
-$PAGE->set_pagelayout('admin');
-$PAGE->set_heading($pagetitle);
-
-echo $OUTPUT->header();
-echo $OUTPUT->heading($pagetitle);
-$urlparams = ['competencyframeworkid' => $id, 'pagecontextid' => $context->id];
-$frameworksurl = new moodle_url('/admin/tool/lp/competencies.php', $urlparams);
-echo $OUTPUT->notification(get_string('competencyframeworkcreated', 'tool_lp'), 'notifysuccess');
-echo $OUTPUT->continue_button($frameworksurl);
-
-echo $OUTPUT->footer();
index a2fc6cb..63d5a2c 100644 (file)
@@ -37,6 +37,7 @@ $PAGE->set_pagelayout('admin');
 $PAGE->set_heading($pagetitle);
 
 $form = null;
+echo $OUTPUT->header();
 if (optional_param('needsconfirm', 0, PARAM_BOOL)) {
     $form = new \tool_lpimportcsv\form\import($url->out(false));
 } else if (optional_param('confirm', 0, PARAM_BOOL)) {
@@ -53,7 +54,7 @@ if ($form->is_cancelled()) {
 
     if ($data->confirm) {
         $importid = $data->importid;
-        $importer = new \tool_lpimportcsv\framework_importer(null, null, null, $importid, $data);
+        $importer = new \tool_lpimportcsv\framework_importer(null, null, null, $importid, $data, true);
 
         $error = $importer->get_error();
         if ($error) {
@@ -61,21 +62,23 @@ if ($form->is_cancelled()) {
             $form->set_import_error($error);
         } else {
             $framework = $importer->import();
-            redirect(new moodle_url('continue.php', array('id' => $framework->get_id())));
+            $urlparams = ['competencyframeworkid' => $framework->get_id(), 'pagecontextid' => $context->id];
+            $frameworksurl = new moodle_url('/admin/tool/lp/competencies.php', $urlparams);
+            echo $OUTPUT->notification(get_string('competencyframeworkcreated', 'tool_lp'), 'notifysuccess');
+            echo $OUTPUT->continue_button($frameworksurl);
             die();
         }
     } else {
         $text = $form->get_file_content('importfile');
         $encoding = $data->encoding;
         $delimiter = $data->delimiter_name;
-        $importer = new \tool_lpimportcsv\framework_importer($text, $encoding, $delimiter);
+        $importer = new \tool_lpimportcsv\framework_importer($text, $encoding, $delimiter, 0, null, true);
         $confirmform = new \tool_lpimportcsv\form\import_confirm(null, $importer);
         $form = $confirmform;
         $pagetitle = get_string('confirmcolumnmappings', 'tool_lpimportcsv');
     }
 }
 
-echo $OUTPUT->header();
 echo $OUTPUT->heading($pagetitle);
 
 $form->display();
index 8c10b04..fd8d9cf 100644 (file)
@@ -38,11 +38,13 @@ $string['importfile'] = 'CSV framework description file';
 $string['importfile_help'] = 'A competency framework may be imported via text file. The format of the file can be determined by creating a new competency framework on the site and then exporting it.';
 $string['importfile_link'] = 'admin/tool/lpimportcsv';
 $string['import'] = 'Import';
+$string['importingfile'] = 'Importing file data';
 $string['invalidimportfile'] = 'File format is invalid.';
 $string['isframework'] = 'Is framework';
 $string['noframeworks'] = 'No competency frameworks have been created yet';
 $string['parentidnumber'] = 'Parent ID number';
 $string['pluginname'] = 'Import competency framework';
+$string['processingfile'] = 'Processing file';
 $string['relatedidnumbers'] = 'Cross-referenced competency ID numbers';
 $string['ruleconfig'] = 'Rule config (optional)';
 $string['ruleoutcome'] = 'Rule outcome (optional)';
index e5e4e7d..5b924bb 100644 (file)
@@ -151,6 +151,7 @@ class login implements renderable, templatable {
         $data->rememberusername = $this->rememberusername;
         $data->passwordautocomplete = $this->passwordautocomplete;
         $data->signupurl = $this->signupurl->out(false);
+        $data->username = $this->username;
 
         return $data;
     }
diff --git a/auth/tests/behat/rememberusername.feature b/auth/tests/behat/rememberusername.feature
new file mode 100644 (file)
index 0000000..754ce8f
--- /dev/null
@@ -0,0 +1,50 @@
+@core @core_auth
+Feature: Test the 'remember username' feature works.
+  In order to see my saved username on the login form
+  As a user
+  I need to have logged in once before and clicked 'Remember username'
+
+  Background:
+    Given the following "users" exist:
+      | username |
+      | teacher1 |
+
+  # Given the user has logged in and selected 'Remember username', when they log in again, then their username should be remembered.
+  Scenario: Check that 'remember username' works without javascript for teachers.
+    # Log in the first time and check the 'remember username' box.
+    Given I am on homepage
+    And I click on "Log in" "link" in the ".logininfo" "css_element"
+    And I set the field "Username" to "teacher1"
+    And I set the field "Password" to "teacher1"
+    And I set the field "Remember username" to "1"
+    And I press "Log in"
+    And I log out
+    # Log out and check that the username was remembered.
+    When I am on homepage
+    And I click on "Log in" "link" in the ".logininfo" "css_element"
+    Then the field "username" matches value "teacher1"
+    And the field "Remember username" matches value "1"
+
+  # Given the user has logged in before and selected 'Remember username', when they log in again and unset 'Remember username', then
+  # their username should be forgotten for future log in attempts.
+  Scenario: Check that 'remember username' unsetting works without javascript for teachers.
+    # Log in the first time and check the 'remember username' box.
+    Given I am on homepage
+    And I click on "Log in" "link" in the ".logininfo" "css_element"
+    And I set the field "Username" to "teacher1"
+    And I set the field "Password" to "teacher1"
+    And I set the field "Remember username" to "1"
+    And I press "Log in"
+    And I log out
+    # Log in again, unsetting the 'remember username' field.
+    When I am on homepage
+    And I click on "Log in" "link" in the ".logininfo" "css_element"
+    And I set the field "Password" to "teacher1"
+    And I set the field "Remember username" to "0"
+    And I press "Log in"
+    And I log out
+    # Check username has been forgotten.
+    Then I am on homepage
+    And I click on "Log in" "link" in the ".logininfo" "css_element"
+    Then the field "username" matches value ""
+    And the field "Remember username" matches value "0"
index d7cecd1..40530a1 100644 (file)
@@ -675,7 +675,7 @@ class core_calendar_renderer extends plugin_renderer_base {
         } else {
             // Assemble pollinterval control.
             $html .= html_writer::start_tag('div', array('style' => 'float:left;'));
-            $html .= html_writer::start_tag('select', array('name' => 'pollinterval'));
+            $html .= html_writer::start_tag('select', array('name' => 'pollinterval', 'class' => 'custom-select'));
             foreach (calendar_get_pollinterval_choices() as $k => $v) {
                 $attributes = array();
                 if ($k == $subscription->pollinterval) {
@@ -687,15 +687,17 @@ class core_calendar_renderer extends plugin_renderer_base {
             $html .= html_writer::end_tag('select');
             $html .= html_writer::end_tag('div');
         }
-        $html .= html_writer::start_tag('div', array('style' => 'float:right;'));
         $html .= html_writer::empty_tag('input', array('type' => 'hidden', 'name' => 'sesskey', 'value' => sesskey()));
         $html .= html_writer::empty_tag('input', array('type' => 'hidden', 'name' => 'course', 'value' => $courseid));
         $html .= html_writer::empty_tag('input', array('type' => 'hidden', 'name' => 'id', 'value' => $subscription->id));
+        $html .= html_writer::start_tag('div', array('class' => 'btn-group pull-right'));
         if (!empty($subscription->url)) {
             $html .= html_writer::tag('button', get_string('update'), array('type'  => 'submit', 'name' => 'action',
+                                                                            'class' => 'btn btn-secondary',
                                                                             'value' => CALENDAR_SUBSCRIPTION_UPDATE));
         }
         $html .= html_writer::tag('button', get_string('remove'), array('type'  => 'submit', 'name' => 'action',
+                                                                        'class' => 'btn btn-secondary',
                                                                         'value' => CALENDAR_SUBSCRIPTION_REMOVE));
         $html .= html_writer::end_tag('div');
         $html .= html_writer::end_tag('form');
index 3221469..d19a5df 100644 (file)
@@ -1644,12 +1644,6 @@ class core_course_external extends external_api {
                 }
             }
 
-            // Check category depth is <= maxdepth (do not check for user who can manage categories).
-            if ((!empty($CFG->maxcategorydepth) && count($parents) > $CFG->maxcategorydepth)
-                    and !has_capability('moodle/category:manage', $context)) {
-                $excludedcats[$category->id] = 'depth';
-            }
-
             // Check the user can use the category context.
             $context = context_coursecat::instance($category->id);
             try {
index 083e558..aebdf87 100644 (file)
@@ -278,8 +278,19 @@ class core_course_externallib_testcase extends externallib_advanced_testcase {
 
         $this->assertEquals($DB->count_records('course_categories'), count($categories));
 
-        // Call without required capability (it will fail cause of the search on idnumber).
         $this->unassignUserCapability('moodle/category:manage', $context->id, $roleid);
+
+        // Ensure maxdepthcategory is 2 and retrieve all categories without category:manage capability. It should retrieve all
+        // visible categories as well.
+        set_config('maxcategorydepth', 2);
+        $categories = core_course_external::get_categories();
+
+        // We need to execute the return values cleaning process to simulate the web service server.
+        $categories = external_api::clean_returnvalue(core_course_external::get_categories_returns(), $categories);
+
+        $this->assertEquals($DB->count_records('course_categories', array('visible' => 1)), count($categories));
+
+        // Call without required capability (it will fail cause of the search on idnumber).
         $this->expectException('moodle_exception');
         $categories = core_course_external::get_categories(array(
             array('key' => 'id', 'value' => $category1->id),
index ca87cd7..256abff 100644 (file)
@@ -969,6 +969,7 @@ $string['roleswithexceptions'] = '{$a->roles}, with {$a->exceptions}';
 $string['rssglobaldisabled'] = 'Disabled at server level';
 $string['save'] = 'Save';
 $string['savechanges'] = 'Save changes';
+$string['scssinvalid'] = 'SCSS code is not valid, fails with: {$a}';
 $string['search'] = 'Search';
 $string['searchalldeleted'] = 'All indexed contents have been deleted';
 $string['searchareaenabled'] = 'Search area enabled';
index faa3b64..aca0b82 100644 (file)
@@ -10180,3 +10180,41 @@ class admin_setting_searchsetupinfo extends admin_setting {
     }
 
 }
+
+/**
+ * Used to validate the contents of SCSS code and ensuring they are parsable.
+ *
+ * It does not attempt to detect undefined SCSS variables because it is designed
+ * to be used without knowledge of other config/scss included.
+ *
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @copyright 2016 Dan Poltawski <dan@moodle.com>
+ */
+class admin_setting_scsscode extends admin_setting_configtextarea {
+
+    /**
+     * Validate the contents of the SCSS to ensure its parsable. Does not
+     * attempt to detect undefined scss variables.
+     *
+     * @param string $data The scss code from text field.
+     * @return mixed bool true for success or string:error on failure.
+     */
+    public function validate($data) {
+        if (empty($data)) {
+            return true;
+        }
+
+        $scss = new core_scss();
+        try {
+            $scss->compile($data);
+        } catch (Leafo\ScssPhp\Exception\ParserException $e) {
+            return get_string('scssinvalid', 'admin', $e->getMessage());
+        } catch (Leafo\ScssPhp\Exception\CompilerException $e) {
+            // Silently ignore this - it could be a scss variable defined from somewhere
+            // else which we are not examining here.
+            return true;
+        }
+
+        return true;
+    }
+}
index baaa605..397fa76 100644 (file)
@@ -76,10 +76,16 @@ class mustache_template_finder {
 
         // First check the theme.
         $dirs[] = $CFG->dirroot . '/theme/' . $themename . '/templates/' . $component . '/';
+        if (isset($CFG->themedir)) {
+            $dirs[] = $CFG->themedir . '/' . $themename . '/templates/' . $component . '/';
+        }
         // Now check the parent themes.
         // Search each of the parent themes second.
         foreach ($parents as $parent) {
             $dirs[] = $CFG->dirroot . '/theme/' . $parent . '/templates/' . $component . '/';
+            if (isset($CFG->themedir)) {
+                $dirs[] = $CFG->themedir . '/' . $parent . '/templates/' . $component . '/';
+            }
         }
 
         $dirs[] = $compdirectory . '/templates/';
index 10242db..8154cf2 100644 (file)
@@ -69,6 +69,7 @@ $capabilities = array(
         'contextlevel' => CONTEXT_SYSTEM,
         'archetypes' => array(
             'manager' => CAP_ALLOW,
+            'coursecreator' => CAP_ALLOW,
         )
     ),
 
index da20823..20e304f 100644 (file)
@@ -2436,5 +2436,34 @@ function xmldb_main_upgrade($oldversion) {
     // Automatically generated Moodle v3.2.0 release upgrade line.
     // Put any upgrade step following this.
 
+    if ($oldversion < 2016122800.00) {
+        // Find all roles with the coursecreator archetype.
+        $coursecreatorroleids = $DB->get_records('role', array('archetype' => 'coursecreator'), '', 'id');
+
+        $context = context_system::instance();
+        $capability = 'moodle/site:configview';
+
+        foreach ($coursecreatorroleids as $roleid => $notused) {
+
+            // Check that the capability has not already been assigned. If it has then it's either already set
+            // to allow or specifically set to prohibit or prevent.
+            if (!$DB->record_exists('role_capabilities', array('roleid' => $roleid, 'capability' => $capability))) {
+                // Assign the capability.
+                $cap = new stdClass();
+                $cap->contextid    = $context->id;
+                $cap->roleid       = $roleid;
+                $cap->capability   = $capability;
+                $cap->permission   = CAP_ALLOW;
+                $cap->timemodified = time();
+                $cap->modifierid   = 0;
+
+                $DB->insert_record('role_capabilities', $cap);
+            }
+        }
+
+        // Main savepoint reached.
+        upgrade_main_savepoint(true, 2016122800.00);
+    }
+
     return true;
 }
index de7dfca..2c8a3ea 100644 (file)
@@ -1240,31 +1240,15 @@ abstract class moodleform {
      *                      $enhancement = 'smartselect';
      *                      $options = array('selectablecategories' => true|false)
      *
-     * @since Moodle 2.0
      * @param string|element $element form element for which Javascript needs to be initalized
      * @param string $enhancement which init function should be called
      * @param array $options options passed to javascript
      * @param array $strings strings for javascript
+     * @deprecated since Moodle 3.3 MDL-57471
      */
     function init_javascript_enhancement($element, $enhancement, array $options=array(), array $strings=null) {
-        global $PAGE;
-        if (is_string($element)) {
-            $element = $this->_form->getElement($element);
-        }
-        if (is_object($element)) {
-            $element->_generateId();
-            $elementid = $element->getAttribute('id');
-            $PAGE->requires->js_init_call('M.form.init_'.$enhancement, array($elementid, $options));
-            if (is_array($strings)) {
-                foreach ($strings as $string) {
-                    if (is_array($string)) {
-                        call_user_func_array(array($PAGE->requires, 'string_for_js'), $string);
-                    } else {
-                        $PAGE->requires->string_for_js($string, 'moodle');
-                    }
-                }
-            }
-        }
+        debugging('$mform->init_javascript_enhancement() is deprecated and no longer does anything. '.
+            'smartselect uses should be converted to the searchableselector form element.', DEBUG_DEVELOPER);
     }
 
     /**
index 4320ae2..9c7de46 100644 (file)
@@ -1063,43 +1063,18 @@ function filterByParent(elCollection, parentFinder) {
     return filteredCollection;
 }
 
-/*
-    All this is here just so that IE gets to handle oversized blocks
-    in a visually pleasing manner. It does a browser detect. So sue me.
-*/
-
+/**
+ * @deprecated since Moodle 3.3, but shouldn't be used in earlier versions either.
+ */
 function fix_column_widths() {
-    var agt = navigator.userAgent.toLowerCase();
-    if ((agt.indexOf("msie") != -1) && (agt.indexOf("opera") == -1)) {
-        fix_column_width('left-column');
-        fix_column_width('right-column');
-    }
+    Y.log('fix_column_widths() no longer does anything. Please remove it from your code.', 'warn', 'javascript-static.js');
 }
 
+/**
+ * @deprecated since Moodle 3.3, but shouldn't be used in earlier versions either.
+ */
 function fix_column_width(colName) {
-    if(column = document.getElementById(colName)) {
-        if(!column.offsetWidth) {
-            setTimeout("fix_column_width('" + colName + "')", 20);
-            return;
-        }
-
-        var width = 0;
-        var nodes = column.childNodes;
-
-        for(i = 0; i < nodes.length; ++i) {
-            if(nodes[i].className.indexOf("block") != -1 ) {
-                if(width < nodes[i].offsetWidth) {
-                    width = nodes[i].offsetWidth;
-                }
-            }
-        }
-
-        for(i = 0; i < nodes.length; ++i) {
-            if(nodes[i].className.indexOf("block") != -1 ) {
-                nodes[i].style.width = width + 'px';
-            }
-        }
-    }
+    Y.log('fix_column_width() no longer does anything. Please remove it from your code.', 'warn', 'javascript-static.js');
 }
 
 
@@ -1462,209 +1437,11 @@ M.form = M.form || {};
 
 /**
  * Converts a nbsp indented select box into a multi drop down custom control much
- * like the custom menu. It also selectable categories on or off.
- *
- * $form->init_javascript_enhancement('elementname','smartselect', array('selectablecategories'=>true|false, 'mode'=>'compact'|'spanning'));
- *
- * @param {YUI} Y
- * @param {string} id
- * @param {Array} options
+ * like the custom menu. Can no longer be used.
+ * @deprecated since Moodle 3.3
  */
-M.form.init_smartselect = function(Y, id, options) {
-    if (!id.match(/^id_/)) {
-        id = 'id_'+id;
-    }
-    var select = Y.one('select#'+id);
-    if (!select) {
-        return false;
-    }
-    Y.use('event-delegate',function(){
-        var smartselect = {
-            id : id,
-            structure : [],
-            options : [],
-            submenucount : 0,
-            currentvalue : null,
-            currenttext : null,
-            shownevent : null,
-            cfg : {
-                selectablecategories : true,
-                mode : null
-            },
-            nodes : {
-                select : null,
-                loading : null,
-                menu : null
-            },
-            init : function(Y, id, args, nodes) {
-                if (typeof(args)=='object') {
-                    for (var i in this.cfg) {
-                        if (args[i] || args[i]===false) {
-                            this.cfg[i] = args[i];
-                        }
-                    }
-                }
-
-                // Display a loading message first up
-                this.nodes.select = nodes.select;
-
-                this.currentvalue = this.nodes.select.get('selectedIndex');
-                this.currenttext = this.nodes.select.all('option').item(this.currentvalue).get('innerHTML');
-
-                var options = Array();
-                options[''] = {text:this.currenttext,value:'',depth:0,children:[]};
-                this.nodes.select.all('option').each(function(option, index) {
-                    var rawtext = option.get('innerHTML');
-                    var text = rawtext.replace(/^(&nbsp;)*/, '');
-                    if (rawtext === text) {
-                        text = rawtext.replace(/^(\s)*/, '');
-                        var depth = (rawtext.length - text.length ) + 1;
-                    } else {
-                        var depth = ((rawtext.length - text.length )/12)+1;
-                    }
-                    option.set('innerHTML', text);
-                    options['i'+index] = {text:text,depth:depth,index:index,children:[]};
-                }, this);
-
-                this.structure = [];
-                var structcount = 0;
-                for (var i in options) {
-                    var o = options[i];
-                    if (o.depth == 0) {
-                        this.structure.push(o);
-                        structcount++;
-                    } else {
-                        var d = o.depth;
-                        var current = this.structure[structcount-1];
-                        for (var j = 0; j < o.depth-1;j++) {
-                            if (current && current.children) {
-                                current = current.children[current.children.length-1];
-                            }
-                        }
-                        if (current && current.children) {
-                            current.children.push(o);
-                        }
-                    }
-                }
-
-                this.nodes.menu = Y.Node.create(this.generate_menu_content());
-                this.nodes.menu.one('.smartselect_mask').setStyle('opacity', 0.01);
-                this.nodes.menu.one('.smartselect_mask').setStyle('width', (this.nodes.select.get('offsetWidth')+5)+'px');
-                this.nodes.menu.one('.smartselect_mask').setStyle('height', (this.nodes.select.get('offsetHeight'))+'px');
-
-                if (this.cfg.mode == null) {
-                    var formwidth = this.nodes.select.ancestor('form').get('offsetWidth');
-                    if (formwidth < 400 || this.nodes.menu.get('offsetWidth') < formwidth*2) {
-                        this.cfg.mode = 'compact';
-                    } else {
-                        this.cfg.mode = 'spanning';
-                    }
-                }
-
-                if (this.cfg.mode == 'compact') {
-                    this.nodes.menu.addClass('compactmenu');
-                } else {
-                    this.nodes.menu.addClass('spanningmenu');
-                    this.nodes.menu.delegate('mouseover', this.show_sub_menu, '.smartselect_submenuitem', this);
-                }
-
-                Y.one(document.body).append(this.nodes.menu);
-                var pos = this.nodes.select.getXY();
-                pos[0] += 1;
-                this.nodes.menu.setXY(pos);
-                this.nodes.menu.on('click', this.handle_click, this);
-
-                Y.one(window).on('resize', function(){
-                     var pos = this.nodes.select.getXY();
-                    pos[0] += 1;
-                    this.nodes.menu.setXY(pos);
-                 }, this);
-            },
-            generate_menu_content : function() {
-                var content = '<div id="'+this.id+'_smart_select" class="smartselect">';
-                content += this.generate_submenu_content(this.structure[0], true);
-                content += '</ul></div>';
-                return content;
-            },
-            generate_submenu_content : function(item, rootelement) {
-                this.submenucount++;
-                var content = '';
-                if (item.children.length > 0) {
-                    if (rootelement) {
-                        content += '<div class="smartselect_mask" href="#ss_submenu'+this.submenucount+'">&nbsp;</div>';
-                        content += '<div id="ss_submenu'+this.submenucount+'" class="smartselect_menu">';
-                        content += '<div class="smartselect_menu_content">';
-                    } else {
-                        content += '<li class="smartselect_submenuitem">';
-                        var categoryclass = (this.cfg.selectablecategories)?'selectable':'notselectable';
-                        content += '<a class="smartselect_menuitem_label '+categoryclass+'" href="#ss_submenu'+this.submenucount+'" value="'+item.index+'">'+item.text+'</a>';
-                        content += '<div id="ss_submenu'+this.submenucount+'" class="smartselect_submenu">';
-                        content += '<div class="smartselect_submenu_content">';
-                    }
-                    content += '<ul>';
-                    for (var i in item.children) {
-                        content += this.generate_submenu_content(item.children[i],false);
-                    }
-                    content += '</ul>';
-                    content += '</div>';
-                    content += '</div>';
-                    if (rootelement) {
-                    } else {
-                        content += '</li>';
-                    }
-                } else {
-                    content += '<li class="smartselect_menuitem">';
-                    content += '<a class="smartselect_menuitem_content selectable" href="#" value="'+item.index+'">'+item.text+'</a>';
-                    content += '</li>';
-                }
-                return content;
-            },
-            select : function(e) {
-                var t = e.target;
-                e.halt();
-                this.currenttext = t.get('innerHTML');
-                this.currentvalue = t.getAttribute('value');
-                this.nodes.select.set('selectedIndex', this.currentvalue);
-                this.hide_menu();
-            },
-            handle_click : function(e) {
-                var target = e.target;
-                if (target.hasClass('smartselect_mask')) {
-                    this.show_menu(e);
-                } else if (target.hasClass('selectable') || target.hasClass('smartselect_menuitem')) {
-                    this.select(e);
-                } else if (target.hasClass('smartselect_menuitem_label') || target.hasClass('smartselect_submenuitem')) {
-                    this.show_sub_menu(e);
-                }
-            },
-            show_menu : function(e) {
-                e.halt();
-                var menu = e.target.ancestor().one('.smartselect_menu');
-                menu.addClass('visible');
-                this.shownevent = Y.one(document.body).on('click', this.hide_menu, this);
-            },
-            show_sub_menu : function(e) {
-                e.halt();
-                var target = e.target;
-                if (!target.hasClass('smartselect_submenuitem')) {
-                    target = target.ancestor('.smartselect_submenuitem');
-                }
-                if (this.cfg.mode == 'compact' && target.one('.smartselect_submenu').hasClass('visible')) {
-                    target.ancestor('ul').all('.smartselect_submenu.visible').removeClass('visible');
-                    return;
-                }
-                target.ancestor('ul').all('.smartselect_submenu.visible').removeClass('visible');
-                target.one('.smartselect_submenu').addClass('visible');
-            },
-            hide_menu : function() {
-                this.nodes.menu.all('.visible').removeClass('visible');
-                if (this.shownevent) {
-                    this.shownevent.detach();
-                }
-            }
-        };
-        smartselect.init(Y, id, options, {select:select});
-    });
+M.form.init_smartselect = function() {
+    throw new Error('M.form.init_smartselect can not be used any more.');
 };
 
 /**
index 1af75d2..8d1859c 100644 (file)
@@ -2104,7 +2104,7 @@ class global_navigation extends navigation_node {
                 $onclick = htmlspecialchars_decode($activity->onclick, ENT_QUOTES);
                 // Build the JS function the click event will call
                 $jscode = "function {$functionname}(e) { $propogrationhandler $onclick }";
-                $this->page->requires->js_init_code($jscode);
+                $this->page->requires->js_amd_inline($jscode);
                 // Override the default url with the new action link
                 $action = new action_link($action, $activityname, new component_action('click', $functionname));
             }
@@ -3913,34 +3913,38 @@ class settings_navigation extends navigation_node {
         if (isloggedin() && !isguestuser() && (!isset($SESSION->load_navigation_admin) || $SESSION->load_navigation_admin)) {
             $isadminpage = $this->is_admin_tree_needed();
 
-            if (has_capability('moodle/site:config', context_system::instance())) {
-                // Make sure this works even if config capability changes on the fly
-                // and also make it fast for admin right after login.
-                $SESSION->load_navigation_admin = 1;
-                if ($isadminpage) {
-                    $adminsettings = $this->load_administration_settings();
-                }
-
-            } else if (!isset($SESSION->load_navigation_admin)) {
-                $adminsettings = $this->load_administration_settings();
-                $SESSION->load_navigation_admin = (int)($adminsettings->children->count() > 0);
+            if (has_capability('moodle/site:configview', context_system::instance())) {
+                if (has_capability('moodle/site:config', context_system::instance())) {
+                    // Make sure this works even if config capability changes on the fly
+                    // and also make it fast for admin right after login.
+                    $SESSION->load_navigation_admin = 1;
+                    if ($isadminpage) {
+                        $adminsettings = $this->load_administration_settings();
+                    }
 
-            } else if ($SESSION->load_navigation_admin) {
-                if ($isadminpage) {
+                } else if (!isset($SESSION->load_navigation_admin)) {
                     $adminsettings = $this->load_administration_settings();
+                    $SESSION->load_navigation_admin = (int)($adminsettings->children->count() > 0);
+
+                } else if ($SESSION->load_navigation_admin) {
+                    if ($isadminpage) {
+                        $adminsettings = $this->load_administration_settings();
+                    }
                 }
-            }
 
-            // Print empty navigation node, if needed.
-            if ($SESSION->load_navigation_admin && !$isadminpage) {
-                if ($adminsettings) {
-                    // Do not print settings tree on pages that do not need it, this helps with performance.
-                    $adminsettings->remove();
-                    $adminsettings = false;
+                // Print empty navigation node, if needed.
+                if ($SESSION->load_navigation_admin && !$isadminpage) {
+                    if ($adminsettings) {
+                        // Do not print settings tree on pages that do not need it, this helps with performance.
+                        $adminsettings->remove();
+                        $adminsettings = false;
+                    }
+                    $siteadminnode = $this->add(get_string('administrationsite'), new moodle_url('/admin/search.php'),
+                            self::TYPE_SITE_ADMIN, null, 'siteadministration');
+                    $siteadminnode->id = 'expandable_branch_' . $siteadminnode->type . '_' .
+                            clean_param($siteadminnode->key, PARAM_ALPHANUMEXT);
+                    $siteadminnode->requiresajaxloading = 'true';
                 }
-                $siteadminnode = $this->add(get_string('administrationsite'), new moodle_url('/admin/search.php'), self::TYPE_SITE_ADMIN, null, 'siteadministration');
-                $siteadminnode->id = 'expandable_branch_'.$siteadminnode->type.'_'.clean_param($siteadminnode->key, PARAM_ALPHANUMEXT);
-                $siteadminnode->requiresajaxloading = 'true';
             }
         }
 
index 6c81aa7..21f40ff 100644 (file)
@@ -576,8 +576,6 @@ class core_renderer extends renderer_base {
             'loadinghelp',
         ), 'moodle');
 
-        $this->page->requires->js_function_call('setTimeout', array('fix_column_widths()', 20));
-
         $focus = $this->page->focuscontrol;
         if (!empty($focus)) {
             if (preg_match("#forms\['([a-zA-Z0-9]+)'\].elements\['([a-zA-Z0-9]+)'\]#", $focus, $matches)) {
index a25b992..63abdeb 100644 (file)
@@ -74,10 +74,10 @@ class moodle_phpmailer extends PHPMailer {
      */
     public function addCustomHeader($custom_header, $value = null) {
         if ($value === null and preg_match('/message-id:(.*)/i', $custom_header, $matches)) {
-            $this->MessageID = $matches[1];
+            $this->MessageID = trim($matches[1]);
             return true;
         } else if ($value !== null and strcasecmp($custom_header, 'message-id') === 0) {
-            $this->MessageID = $value;
+            $this->MessageID = trim($value);
             return true;
         } else {
             return parent::addCustomHeader($custom_header, $value);
index 6445e77..a6dc243 100644 (file)
     along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
 }}
 {{!
+    @template core/action_menu
+
     Action menu.
+
+    Example context (json):
+    {
+        "classes": "",
+        "primary": {
+            "items": [{"rawhtml": "<p>Item in primary menu</p>"}]
+        },
+        "secondary": {
+            "items": [{"rawhtml": "<p>Item in secondary menu</p>"}]
+        }
+    }
 }}
 <div class="{{classes}}" {{#attributes}}{{name}}="{{value}}"{{/attributes}}>
     {{#primary}}
@@ -37,8 +50,6 @@
             {{#items}}<li role="presentation">{{> core/action_menu_item }}</li>{{/items}}
         </ul>
     {{/secondary}}
-
-    </span>
 </div>
 {{#js}}
 require(['core/yui'], function(Y) {
index d00323a..bbbf864 100644 (file)
     along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
 }}
 {{!
+    @template core/action_menu_item
+
     Action menu item.
+
+    Example context (json):
+    {
+        "rawhtml": "<p>[rawhtml]</p>"
+    }
 }}
 {{#actionmenulink}}{{> core/action_menu_link }}{{/actionmenulink}}
 {{#actionmenufiller}}<span class="filler">&nbsp;</span>{{/actionmenufiller}}
index 3c41ad8..054df8b 100644 (file)
     along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
 }}
 {{!
+    @template core/action_menu_link
+
     Action menu link.
+
+    Example context (json):
+    {
+        "text": "Example link text",
+        "showtext": true,
+        "url": "http://example.com/link",
+        "classes": "menu-action",
+        "instance": "1"
+    }
 }}
 {{^disabled}}
     <a href="{{url}}" class="{{classes}}" {{#attributes}}{{name}}={{#quote}}{{value}}{{/quote}}{{/attributes}} {{#showtext}}aria-labelledby="actionmenuaction-{{instance}}"{{/showtext}}>{{#icon}}{{>core/pix_icon}}{{/icon}}{{#showtext}}<span class="menu-action-text" id="actionmenuaction-{{instance}}">{{{text}}}</span>{{/showtext}}</a>
index 977d382..2068373 100644 (file)
     along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
 }}
 {{!
+    @template core/action_menu_trigger
+
     Action menu trigger.
+
+    Example context (json):
+    {
+        "instance": "1",
+        "title": "Trigger me!",
+        "menutrigger": true
+    }
 }}
 <a href="#" class="toggle-display {{#menutrigger}}textmenu{{/menutrigger}}" id="action-menu-toggle-{{instance}}" title="{{title}}" role="menuitem">{{{actiontext}}}{{{menutrigger}}}{{#icon}}{{> core/pix_icon}}{{/icon}}{{#rawicon}}{{{.}}}{{/rawicon}}{{#menutrigger}}<b class="caret"></b>{{/menutrigger}}</a>
\ No newline at end of file
index 3d76246..40b2b69 100644 (file)
     along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
 }}
 {{!
+    @template core/actions
+
     Actions.
+
+    Example context (json):
+    {
+        "actions": [{"event": "event", "jsfunction": "Y.log", "id": "id"}]
+    }
 }}
 {{#js}}
     require(['core/yui'], function(Y) {
index 3486735..fdb9a94 100644 (file)
     along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
 }}
 {{!
+    @template core/chart
+
     Chart rendering.
+
+    Example context (json):
+    {
+        "withtable": true,
+        "chartdata": "null"
+    }
 }}
 <div class="chart-area" id="chart-area-{{uniqid}}">
-    <div class="chart-image" role="decoration" aria-describedby="chart-table-data-{{uniqid}}"></div>
+    <div class="chart-image" role="presentation" aria-describedby="chart-table-data-{{uniqid}}"></div>
     <div class="chart-table {{^withtable}}accesshide{{/withtable}}">
         <p class="chart-table-expand">
             <a href="#" aria-controls="chart-table-data-{{uniqid}}" role="button">
                 {{#str}}showchartdata, moodle{{/str}}
             </a>
         </p>
-        <div class="chart-table-data" id="chart-table-data-{{uniqid}}" {{#withtable}}aria-expanded="false"{{/withtable}}></div>
+        <div class="chart-table-data" id="chart-table-data-{{uniqid}}" {{#withtable}}role="complementary" aria-expanded="false"{{/withtable}}></div>
     </div>
 </div>
 
index eea553c..166cf5d 100644 (file)
     along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
 }}
 {{!
+    @template core/chooser
+
     Chooser.
+
+    Example context (json):
+    {
+        "title": "Chooser title",
+        "method": "post",
+        "actionurl": "http://example.org/test",
+        "instructions": "Choose one:",
+        "paramname": "param",
+        "sections": [{
+            "id": "section-1",
+            "label": "Section one",
+            "items": [{
+                "label": "item one",
+                "description": "description one"
+            }]
+        }]
+    }
 }}
 <div class="hd choosertitle">
     {{title}}
index cfdea06..722b2f5 100644 (file)
     along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
 }}
 {{!
+    @template core/chooser_item
+
     Chooser item.
+
+    Example context (json):
+    {
+        "id": "1",
+        "paramname": "param",
+        "value": "1",
+        "label": "item one",
+        "description": "description one"
+    }
 }}
 <div class="option">
     <label for="item_{{id}}">
index e961ae1..6f51101 100644 (file)
@@ -17,8 +17,7 @@
 {{!
     @template core/dataformat_selector
 
-    Template for all html emails. Note that it may wrap content formatted
-    elsewhere in another a module template.
+    Template for dataformat selection and download form.
 
     Context variables required for this template:
     * label
     * options
     * sesskey
     * submit
+
+    Example context (json):
+    {
+        "base": "http://example.org/",
+        "name": "test",
+        "label": "Download table data as",
+        "params": false,
+        "options": [{"label": "CSV", "name": "csv"}, {"label": "Excel", "name": "excel"}],
+        "submit": "Download"
+    }
 }}
 <form method="get" action="{{base}}" class="dataformatselector">
     <div class="mdl-align">
index ec43023..8bb9803 100644 (file)
@@ -15,7 +15,7 @@
     along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
 }}
 {{!
-    @template core/email_html
+    @template core/email_fromname
 
     Template for all email subjects.
 
     * fromname
     * replyto
     * replytoname
+
+    Example context (json):
+    {
+        "fromname": "Joe Bloggs"
+    }
 }}
 {{{fromname}}}
index e8c1aaa..a8d93d9 100644 (file)
     * replyto
     * replytoname
     * body
+
+    Example context (json):
+    {
+        "body": "Email body"
+    }
 }}
 {{{body}}}
index c67afdd..8454983 100644 (file)
@@ -15,7 +15,7 @@
     along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
 }}
 {{!
-    @template core/email_html
+    @template core/email_subject
 
     Template for all email subjects.
 
     * fromname
     * replyto
     * replytoname
+
+    Example context (json):
+    {
+        "subject": "Email subject"
+    }
 }}
 {{{subject}}}
index 8ffc85a..7e537c1 100644 (file)
     * replyto
     * replytoname
     * body
+
+    Example context (json):
+    {
+        "body": "Email body"
+    }
 }}
 {{{body}}}
index d73c8c3..72b2dcd 100644 (file)
     along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
 }}
 {{!
+    @template core/help_icon
+
     Help icon.
+
+    Example context (json):
+    {
+        "title": "Help with something",
+        "url": "http://example.org/help",
+        "linktext": "",
+        "icon":{
+            "attributes": [
+                {"name": "class", "value": "iconhelp"},
+                {"name": "src", "value": "../../../pix/help.svg"},
+                {"name": "alt", "value": "Help icon"}
+            ]
+        }
+    }
 }}
 <span class="helptooltip">
     <a href="{{url}}" title={{#quote}}{{title}}{{/quote}} aria-haspopup="true" target="_blank">{{#icon}}{{>core/pix_icon}}{{/icon}}{{#linktext}}{{.}}{{/linktext}}</a>
index b15e58a..0a85c16 100644 (file)
 
     Context variables required for this template:
     * none
+
+    Example context (json):
+    {
+    }
 }}
 <div class="hover-tooltip-container">
     {{$anchor}}{{/anchor}}
index 1c55525..d8199b9 100644 (file)
@@ -29,5 +29,8 @@
 
     Context variables required for this template:
     *
+
+    Example context (json):
+    {}
 }}
 <span class="loading-icon">{{#pix}} y/loading, core, {{#str}} loading {{/str}} {{/pix}}</span>
index 0096a07..3ee5e2a 100644 (file)
     @template core/login
 
     Moodle template for the login page.
+
+    Example context (json):
+    {
+        "autofocusform": false,
+        "canloginasguest": true,
+        "canloginbyemail": true,
+        "cansignup": true,
+        "error": "testerror",
+        "errorformatted": "Test error formatted",
+        "forgotpasswordurl": "http://example.com/login/forgot_password.php",
+        "hasidentityproviders": false,
+        "hasinstructions": true,
+        "instructions": "For full access to this site, you first need to create an account.",
+        "loginurl": "http://example.com/stable_master/login/index.php",
+        "rememberusername": true,
+        "passwordautocomplete": false,
+        "signupurl": "http://localhost/stable_master/login/signup.php",
+        "cookieshelpiconformatted": "",
+        "username": ""
+    }
 }}
 {{#hasinstructions}}
 <div class="loginbox clearfix twocolumns">
             });
         {{/autofocusform}}
     {{/error}}
-    })
+    });
 {{/js}}
index e986757..403735c 100644 (file)
@@ -33,8 +33,9 @@
     Example context (json):
     {
         "attributes": [
-            { "name": "src", "value": "http://moodle.com/wp-content/themes/moodle/images/logo-hat2.png" },
-            { "name": "class", "value": "iconsmall" }
+            { "name": "src", "value": "https://moodle.org/logo/moodle-logo.svg" },
+            { "name": "class", "value": "iconsmall" },
+            {"name": "alt", "value": "Alt text for icon"}
         ]
     }
 
index 7a2653e..ae8ac48 100644 (file)
     along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
 }}
 {{!
+    @template core/progress_bar
+
     Progress bar.
 
     Example context (json):
     {
-        id: 'progressbar_test',
-        width: '500'
+        "id": "progressbar_test",
+        "width": "500"
     }
 }}
 <div class="progressbar_container" style="width: {{width}}px;" id="{{id}}">
index 1661f33..e2954cd 100644 (file)
     along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
 }}
 {{!
+    @template core/select_time
+
     Select time.
+
+    Example context (json):
+    {
+        "id": "test-id",
+        "name": "test-name",
+        "label": "Test label",
+        "options": [
+            {"name": "Option 1", "value": "1"},
+            {"name": "Option 2", "value": "2"}
+        ]
+    }
 }}
 <label for="{{id}}" class="accesshide">{{label}}</label>
 <select name="{{name}}" id="{{id}}" {{#attributes}} {{name}}="{{value}}"{{/attributes}}>
index 80bd360..dee3e2d 100644 (file)
@@ -1,2 +1,10 @@
+{{!
+    @template core/signup_form_layout
+
+    Example context (json):
+    {
+        "formhtml": "<p>(Form html would go here)</p>"
+    }
+}}
 <h3>{{#str}}newaccount{{/str}}</h3>
 {{{formhtml}}}
index 8a1ef0d..6e17fa5 100644 (file)
@@ -1,3 +1,14 @@
+{{!
+    @template core/skip_links
+
+    Example context (json):
+    {
+        "links": [
+            {"url": "http://example.com/link1", "text": "Link 1"},
+            {"url": "http://example.com/link2", "text": "Link 2"}
+        ]
+    }
+}}
 <div class="skiplinks">
 {{#links}}
     <a href="#{{{url}}}" class="skip">{{{text}}}</a>
index 2ab1711..ba7e311 100644 (file)
@@ -4,6 +4,8 @@ information provided here is intended especially for developers.
 === 3.3 ===
 * YUI module moodle-core-formautosubmit has been removed, use jquery .change() instead (see lib/templates/url_select.mustache for
   an example)
+* $mform->init_javascript_enhancement() is deprecated and no longer does anything. Existing uses of smartselect enhancement
+  should be switched to the searchableselector form element or other solutions.
 
 === 3.2 ===
 
index de6e6b4..0a61cfa 100644 (file)
@@ -2325,7 +2325,7 @@ function check_unoconv_version(environment_results $result) {
 }
 
 /**
- * Checks for up-to-date TLS libraries.
+ * Checks for up-to-date TLS libraries. NOTE: this is not currently used, see MDL-57262.
  *
  * @param environment_results $result object to update, if relevant.
  * @return environment_results|null updated results or null if unoconv path is not executable.
index c62ff2c..c47cd2e 100644 (file)
Binary files a/message/amd/build/message_area_messages.min.js and b/message/amd/build/message_area_messages.min.js differ
index 3188100..9682d82 100644 (file)
@@ -75,6 +75,9 @@ define(['jquery', 'core/ajax', 'core/templates', 'core/notification', 'core/cust
         /** @type {int} the number of messagess displayed */
         Messages.prototype._numMessagesDisplayed = 0;
 
+        /** @type {array} the messages displayed or about to be displayed on the page */
+        Messages.prototype._messageQueue = [];
+
         /** @type {int} the number of messages to retrieve */
         Messages.prototype._numMessagesToRetrieve = 20;
 
@@ -294,36 +297,8 @@ define(['jquery', 'core/ajax', 'core/templates', 'core/notification', 'core/cust
             }
 
             // Keep track of the number of messages received.
-            var numberreceived = 0;
             return this._getMessages(this._getUserId(), true).then(function(data) {
-                // Filter out any messages already rendered.
-                var messagesArea = this.messageArea.find(SELECTORS.MESSAGES);
-                data.messages = data.messages.filter(function(message) {
-                    var id = "" + message.id + message.isread;
-                    var result = messagesArea.find(SELECTORS.MESSAGE + '[data-id="' + id + '"]');
-                    return !result.length;
-                });
-                numberreceived = data.messages.length;
-                // We have the data - lets render the template with it.
-                return Templates.render('core_message/message_area_messages', data);
-            }.bind(this)).then(function(html, js) {
-                // Check if we got something to do.
-                if (numberreceived > 0) {
-                    var newHtml = $('<div>' + html + '</div>');
-                    if (this._hasMatchingBlockTime(this.messageArea.node, newHtml, false)) {
-                        newHtml.find(SELECTORS.BLOCKTIME + ':first').remove();
-                    }
-                    // Show the new content.
-                    Templates.appendNodeContents(this.messageArea.find(SELECTORS.MESSAGES), newHtml, js);
-                    // Scroll the new message into view.
-                    if (shouldScrollBottom) {
-                        this._scrollBottom();
-                    }
-                    // Increment the number of messages displayed.
-                    this._numMessagesDisplayed += numberreceived;
-                    // Reset the poll timer because the user may be active.
-                    this._backoffTimer.restart();
-                }
+                return this._addMessagesToDom(data.messages, shouldScrollBottom);
             }.bind(this)).always(function() {
                 // Mark that we are no longer busy loading data.
                 this._isLoadingMessages = false;
@@ -435,7 +410,7 @@ define(['jquery', 'core/ajax', 'core/templates', 'core/notification', 'core/cust
                 // Fire an event to say the message was sent.
                 this.messageArea.trigger(Events.MESSAGESENT, [this._getUserId(), text]);
                 // Update the messaging area.
-                return this._addMessageToDom();
+                return this._addLastMessageToDom();
             }.bind(this)).then(function() {
                 // Ok, we are no longer sending a message.
                 this._isSendingMessage = false;
@@ -624,10 +599,58 @@ define(['jquery', 'core/ajax', 'core/templates', 'core/notification', 'core/cust
         /**
          * Handles adding messages to the DOM.
          *
+         * @param {array} messages An array of messages to be added to the DOM.
+         * @param {boolean} shouldScrollBottom True will scroll to the bottom of the message window and show the new messages.
+         * @return {Promise} The promise resolved when the messages have been added to the DOM.
+         * @private
+         */
+        Messages.prototype._addMessagesToDom = function(messages, shouldScrollBottom) {
+            var numberreceived = 0;
+            var messagesArea = this.messageArea.find(SELECTORS.MESSAGES);
+            messages = messages.filter(function(message) {
+                var id = "" + message.id + message.isread;
+                // If the message is already queued to be rendered, remove from the list of messages.
+                if (this._messageQueue[id]) {
+                    return false;
+                }
+                // Filter out any messages already rendered.
+                var result = messagesArea.find(SELECTORS.MESSAGE + '[data-id="' + id + '"]');
+                // Any message we are rendering should go in the messageQueue.
+                if (!result.length) {
+                    this._messageQueue[id] = true;
+                }
+                return !result.length;
+            }.bind(this));
+            numberreceived = messages.length;
+            // We have the data - lets render the template with it.
+            return Templates.render('core_message/message_area_messages', {messages: messages}).then(function(html, js) {
+                // Check if we got something to do.
+                if (numberreceived > 0) {
+                    var newHtml = $('<div>' + html + '</div>');
+                    if (this._hasMatchingBlockTime(this.messageArea.node, newHtml, false)) {
+                        newHtml.find(SELECTORS.BLOCKTIME + ':first').remove();
+                    }
+                    // Show the new content.
+                    Templates.appendNodeContents(this.messageArea.find(SELECTORS.MESSAGES), newHtml, js);
+                    // Scroll the new message into view.
+                    if (shouldScrollBottom) {
+                        this._scrollBottom();
+                    }
+                    // Increment the number of messages displayed.
+                    this._numMessagesDisplayed += numberreceived;
+                    // Reset the poll timer because the user may be active.
+                    this._backoffTimer.restart();
+                }
+            }.bind(this));
+        };
+
+        /**
+         * Handles adding the last message to the DOM.
+         *
          * @return {Promise} The promise resolved when the message has been added to the DOM.
          * @private
          */
-        Messages.prototype._addMessageToDom = function() {
+        Messages.prototype._addLastMessageToDom = function() {
             // Call the web service to return how the message should look.
             var promises = Ajax.call([{
                 methodname: 'core_message_data_for_messagearea_get_most_recent_message',
@@ -639,13 +662,10 @@ define(['jquery', 'core/ajax', 'core/templates', 'core/notification', 'core/cust
 
             // Add the message.
             return promises[0].then(function(data) {
-                return Templates.render('core_message/message_area_message', data);
-            }).then(function(html, js) {
-                Templates.appendNodeContents(this.messageArea.find(SELECTORS.MESSAGES), html, js);
-                // Empty the response text area.
+                return this._addMessagesToDom([data], true);
+            }.bind(this)).always(function() {
+                // Empty the response text area.text
                 this.messageArea.find(SELECTORS.SENDMESSAGETEXT).val('').trigger('input');
-                // Scroll down.
-                this._scrollBottom();
             }.bind(this)).fail(Notification.exception);
         };
 
index f944289..c06d661 100644 (file)
@@ -15,7 +15,7 @@
     along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
 }}
 {{!
-    @template core/add_contact_button
+    @template core_message/add_contact_button
 
     Template for the contents of the add contact button on the user's profile page.
 
index 6d8fec2..2f542f5 100644 (file)
     along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
 }}
 {{!
-    @template core/add_contact_button
+    @template core_message/add_contact_button
 
     Template for the contents of the add contact button on the user's profile page.
 
     Context variables required for this template:
-    *
+    * none
+
+    Example context (json):
+    {
+    }
 }}
 <span>
     {{#pix}} t/removecontact, core {{/pix}}
index 160d80f..4e02e44 100644 (file)
@@ -2955,7 +2955,7 @@ abstract class lesson_page extends lesson_base {
         for ($i = 0; $i < $this->lesson->maxanswers; $i++) {
             $answer = clone($newanswer);
 
-            if (!empty($properties->answer_editor[$i])) {
+            if (isset($properties->answer_editor[$i])) {
                 if (is_array($properties->answer_editor[$i])) {
                     // Multichoice and true/false pages have an HTML editor.
                     $answer->answer = $properties->answer_editor[$i]['text'];
index fd2b579..21b158f 100644 (file)
@@ -1194,8 +1194,11 @@ function quiz_update_events($quiz, $override = null) {
                    'instance'=>$quiz->id);
     if (!empty($override)) {
         // Only load events for this override.
-        $conds['groupid'] = isset($override->groupid)?  $override->groupid : 0;
-        $conds['userid'] = isset($override->userid)?  $override->userid : 0;
+        if (isset($override->userid)) {
+            $conds['userid'] = $override->userid;
+        } else {
+            $conds['groupid'] = $override->groupid;
+        }
     }
     $oldevents = $DB->get_records('event', $conds);
 
index 56d6b74..3adf3dd 100644 (file)
@@ -24,7 +24,7 @@
 
 defined('MOODLE_INTERNAL') || die();
 
-$plugin->version   = 2016120500;    // The current module version (Date: YYYYMMDDXX).
+$plugin->version   = 2016122000;    // The current module version (Date: YYYYMMDDXX).
 $plugin->requires  = 2016112900;    // Requires this Moodle version.
 $plugin->component = 'mod_scorm';   // Full name of the plugin (used for diagnostics).
 $plugin->cron      = 300;
index 22ab9c6..a80ddd0 100644 (file)
@@ -46,8 +46,7 @@ M.mod_scormform.init = function(Y) {
         // Onunload is called multiple times in the SCORM window - we only want to handle when it is actually closed.
         setTimeout(function() {
             if (winobj.closed) {
-                // Redirect the parent window to the course homepage.
-                parent.window.location = course_url;
+                window.location = course_url;
             }
         }, 800)
     }
index f5f12fa..56ae1a6 100644 (file)
@@ -442,15 +442,13 @@ class report_log_renderable implements renderable {
      * @return array list of origins.
      */
     public function get_origin_options() {
-        global $DB;
-        $origins = $DB->get_records_sql('select distinct origin from {logstore_standard_log} order by origin ASC');
         $ret = array();
         $ret[''] = get_string('allsources', 'report_log');
-        foreach ($origins as $origin) {
-            if (!empty($origin->origin)) {
-                $ret[$origin->origin] = get_string($origin->origin, 'report_log');
-            }
-        }
+        $ret['cli'] = get_string('cli', 'report_log');
+        $ret['restore'] = get_string('restore', 'report_log');
+        $ret['web'] = get_string('web', 'report_log');
+        $ret['ws'] = get_string('ws', 'report_log');
+        $ret['---'] = get_string('other', 'report_log');
         return $ret;
     }
 
index c1f3ff9..ad9e44a 100644 (file)
@@ -486,10 +486,20 @@ class report_log_table_log extends table_sql {
             $joins[] = "edulevel ".$edulevelsql;
             $params = array_merge($params, $edulevelparams);
         }
+
         // Origin.
         if (isset($this->filterparams->origin) && ($this->filterparams->origin != '')) {
-            $joins[] = "origin = :origin";
-            $params['origin'] = $this->filterparams->origin;
+            if ($this->filterparams->origin !== '---') {
+                // Filter by a single origin.
+                $joins[] = "origin = :origin";
+                $params['origin'] = $this->filterparams->origin;
+            } else {
+                // Filter by everything else.
+                list($originsql, $originparams) = $DB->get_in_or_equal(array('cli', 'restore', 'ws', 'web'),
+                    SQL_PARAMS_NAMED, 'origin', false);
+                $joins[] = "origin " . $originsql;
+                $params = array_merge($params, $originparams);
+            }
         }
 
         if (!($this->filterparams->logreader instanceof logstore_legacy\log\store)) {
index d764bdd..f37d48f 100644 (file)
@@ -39,6 +39,7 @@ $string['page'] = 'Page {$a}';
 $string['logsformat'] = 'Logs format';
 $string['nologreaderenabled'] = 'No log reader enabled';
 $string['origin'] = 'Source';
+$string['other'] = 'Other';
 $string['page-report-log-x'] = 'Any log report';
 $string['page-report-log-index'] = 'Course log report';
 $string['page-report-log-user'] = 'User course log report';
index d97e73d..c669075 100644 (file)
@@ -886,106 +886,6 @@ tr.flagged-tag a {
     text-align: left;
     border: 0 solid black;
 }
-/**
-* Smart Select Element
-*/
-.smartselect {
-    position: absolute;
-}
-
-.smartselect .smartselect_mask {
-    background-color: #fff;
-}
-
-.smartselect ul {
-    padding: 0;
-    margin: 0;
-}
-
-.smartselect ul li {
-    list-style: none;
-}
-
-.smartselect .smartselect_menu {
-    margin-right: 5px;
-}
-
-.safari .smartselect .smartselect_menu {
-    margin-left: 2px;
-}
-
-.smartselect .smartselect_menu,
-.smartselect .smartselect_submenu {
-    border: 1px solid #000;
-    background-color: #fff;
-    display: none;
-}
-
-.smartselect .smartselect_menu.visible,
-.smartselect .smartselect_submenu.visible {
-    display: block;
-}
-
-.smartselect .smartselect_menu_content ul li {
-    position: relative;
-    padding: 2px 5px;
-}
-
-.smartselect .smartselect_menu_content ul li a {
-    color: #333;
-    text-decoration: none;
-}
-
-.smartselect .smartselect_menu_content ul li a.selectable {
-    color: inherit;
-}
-
-.smartselect .smartselect_submenuitem {
-    background-image: url([[pix:moodle|t/collapsed]]);
-    background-repeat: no-repeat;
-    background-position: 100%;
-}
-/** Spanning mode */
-.smartselect.spanningmenu .smartselect_submenu {
-    position: absolute;
-    top: -1px;
-    left: 100%;
-}
-
-.smartselect.spanningmenu .smartselect_submenu a {
-    white-space: nowrap;
-    padding-right: 16px;
-}
-
-.smartselect.spanningmenu .smartselect_menu_content ul li a.selectable:hover {
-    text-decoration: underline;
-}
-/** Compact mode */
-.smartselect.compactmenu .smartselect_submenu {
-    position: relative;
-    margin: 2px -3px;
-    margin-left: 10px;
-    display: none;
-    border-width: 0;
-    z-index: 1010;
-}
-
-.smartselect.compactmenu .smartselect_submenu.visible {
-    display: block;
-}
-
-.smartselect.compactmenu .smartselect_menu {
-    z-index: 1000;
-    overflow: hidden;
-}
-
-.smartselect.compactmenu .smartselect_submenu .smartselect_submenu {
-    z-index: 1020;
-}
-
-.smartselect.compactmenu .smartselect_submenuitem:hover > .smartselect_menuitem_label {
-    font-weight: bold;
-}
 
 /**
 * Enrol
index 6c6b75f..f9f02f0 100644 (file)
 .file-picker .ygtvtn,
 .filemanager .ygtvtn {
     /*rtl:remove*/
-    background: url('[[pix:moodle|y/tn]]') 0 10px no-repeat;
+    background: url('[[pix:moodle|y/tn]]') 0 0 no-repeat;
     /*rtl:raw:
-    background: url('[[pix:moodle|y/tn_rtl]]') 2px 10px no-repeat;
+    background: url('[[pix:moodle|y/tn_rtl]]') 0 0 no-repeat;
     */
-    width: 17px;
-    height: 22px;
+    width: 19px;
+    height: 32px;
 }
 // first or middle sibling, collapsable
 .file-picker .ygtvtm,
 .file-picker .ygtvln,
 .filemanager .ygtvln {
     /*rtl:remove*/
-    background: url('[[pix:moodle|y/ln]]') 0 10px no-repeat;
+    background: url('[[pix:moodle|y/ln]]') 0 0 no-repeat;
     /*rtl:raw:
-    background: url('[[pix:moodle|y/ln_rtl]]') 2px 10px no-repeat;
+    background: url('[[pix:moodle|y/ln_rtl]]') 0 0 no-repeat;
     */
-    width: 17px;
-    height: 22px;
+    width: 19px;
+    height: 32px;
 }
 // Last sibling, collapsable
 .file-picker .ygtvlm,
 // the style for the empty cells that are used for rendering the depth of the node
 .file-picker .ygtvdepthcell,
 .filemanager .ygtvdepthcell {
-    background: url('[[pix:moodle|y/vline]]') 0 10px no-repeat;
+    background: url('[[pix:moodle|y/vline]]') 0 0 no-repeat;
     /*rtl:raw:
-    background-position: 2px 10px;
+    background-position: 0 0;
     */
     width: 17px;
     height: 32px;
index 326c205..15b2c39 100644 (file)
@@ -73,13 +73,13 @@ if ($ADMIN->fulltree) {
     $page = new admin_settingpage('theme_boost_advanced', get_string('advancedsettings', 'theme_boost'));
 
     // Raw SCSS to include before the content.
-    $setting = new admin_setting_configtextarea('theme_boost/scsspre',
+    $setting = new admin_setting_scsscode('theme_boost/scsspre',
         get_string('rawscsspre', 'theme_boost'), get_string('rawscsspre_desc', 'theme_boost'), '', PARAM_RAW);
     $setting->set_updatedcallback('theme_reset_all_caches');
     $page->add($setting);
 
     // Raw SCSS to include after the content.
-    $setting = new admin_setting_configtextarea('theme_boost/scss', get_string('rawscss', 'theme_boost'),
+    $setting = new admin_setting_scsscode('theme_boost/scss', get_string('rawscss', 'theme_boost'),
         get_string('rawscss_desc', 'theme_boost'), '', PARAM_RAW);
     $setting->set_updatedcallback('theme_reset_all_caches');
     $page->add($setting);
index 4d369b8..e1db0f2 100644 (file)
     You should have received a copy of the GNU General Public License
     along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
 }}
-<ul class="nav nav-tabs">
+{{!
+    @template theme_boost/admin_setting_tabs
+
+
+    Example context (json):
+    {
+        "tabs": [
+         {
+            "name": "tab1",
+            "active": 0,
+            "displayname": "Inactive tab1",
+            "html": "<p>Tab 1 content</p>"
+         },
+         {
+            "name": "tab2",
+            "active": 1,
+            "displayname": "Active tab2",
+            "html": "<p>Tab 2 content</p>"
+         }
+        ]
+    }
+}}
+<ul class="nav nav-tabs" role="tablist">
     {{#tabs}}
         <li class="nav-item">
             <a href="#{{name}}" class="nav-link {{#active}}active{{/active}}" data-toggle="tab" role="tab">{{displayname}}</a>
index 5befb9c..5a0d38f 100644 (file)
     along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
 }}
 {{!
+    @template core/action_menu
+
     Action menu.
+
+    Example context (json):
+    {
+        "classes": "",
+        "primary": {
+            "items": [{"rawhtml": "<p>Item in primary menu</p>"}]
+        },
+        "secondary": {
+            "items": [{"rawhtml": "<p>Item in secondary menu</p>"}]
+        }
+    }
 }}
 <div class="action-menu {{classes}}" {{#attributes}}{{name}}="{{value}}"{{/attributes}}>
     {{#primary}}
index d00323a..bbbf864 100644 (file)
     along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
 }}
 {{!
+    @template core/action_menu_item
+
     Action menu item.
+
+    Example context (json):
+    {
+        "rawhtml": "<p>[rawhtml]</p>"
+    }
 }}
 {{#actionmenulink}}{{> core/action_menu_link }}{{/actionmenulink}}
 {{#actionmenufiller}}<span class="filler">&nbsp;</span>{{/actionmenufiller}}
index 3c41ad8..756e95c 100644 (file)
     along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
 }}
 {{!
+    @template core/action_menu_link
+
     Action menu link.
+
+    Example context (json):
+    {
+        "text": "Example link text",
+        "showtext": true,
+        "url": "http://example.com/link"
+    }
 }}
 {{^disabled}}
     <a href="{{url}}" class="{{classes}}" {{#attributes}}{{name}}={{#quote}}{{value}}{{/quote}}{{/attributes}} {{#showtext}}aria-labelledby="actionmenuaction-{{instance}}"{{/showtext}}>{{#icon}}{{>core/pix_icon}}{{/icon}}{{#showtext}}<span class="menu-action-text" id="actionmenuaction-{{instance}}">{{{text}}}</span>{{/showtext}}</a>
index 9efbed1..5396e29 100644 (file)
     along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
 }}
 {{!
+    @template core/chooser
+
     Chooser.
+
+    Example context (json):
+    {
+        "title": "Chooser title",
+        "method": "post",
+        "actionurl": "http://example.org/test",
+        "instructions": "Choose one:",
+        "paramname": "param",
+        "sections": [{
+            "id": "section-1",
+            "label": "Section one",
+            "items": [{
+                "label": "item one",
+                "description": "description one"
+            }]
+        }]
+    }
 }}
 <div class="hd choosertitle">
     {{title}}
index 421dfc3..7c11ab3 100644 (file)
     * options
     * sesskey
     * submit
+
+    Example context (json):
+    {
+        "base": "http://example.org/",
+        "name": "test",
+        "value": "test",
+        "label": "Download table data as",
+        "params": false,
+        "options": [{"label": "CSV", "name": "csv"}, {"label": "Excel", "name": "excel"}],
+        "submit": "Download",
+        "sesskey": ""
+    }
 }}
 <form method="get" action="{{base}}" class="dataformatselector m-a-1">
     <div class="form-inline text-xs-right">
index 5abbe5b..161f0bc 100644 (file)
@@ -1,3 +1,22 @@
+{{!
+    @template core/help_icon
+
+    Help icon.
+
+    Example context (json):
+    {
+        "title": "Help with something",
+        "url": "http://example.org/help",
+        "linktext": "",
+        "icon":{
+            "attributes": [
+                {"name": "class", "value": "iconhelp"},
+                {"name": "src", "value": "../../../pix/help.svg"},
+                {"name": "alt", "value": "Help icon"}
+            ]
+        }
+    }
+}}
 <a class="btn btn-link p-a-0" role="button"
     data-container="body" data-toggle="popover"
     data-placement="{{#ltr}}right{{/ltr}}{{^ltr}}left{{/ltr}}" data-content="{{text}} {{completedoclink}}"
index 7a9d4f6..56f8212 100644 (file)
     along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
 }}
 {{!
+    @template core/progress_bar
+
     Progress bar.
 
     Example context (json):
     {
-        id: 'progressbar_test',
-        width: '500'
+        "id": "progressbar_test",
+        "width": "500"
     }
 }}
-<div class="row" id="{{id}}" class="progressbar_container">
+<div id="{{id}}" class="row progressbar_container">
     <div class="col-md-6 push-md-3">
         <p id="{{id}}_status" class="text-xs-center"></p>
         <progress id="{{id}}_bar" class="progress progress-striped progress-animated" value="0" max="100"></progress>
index 9116468..6493280 100644 (file)
     along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
 }}
 {{!
+    @template core/select_time
+
     Select time.
+
+    Example context (json):
+    {
+        "id": "test-id",
+        "name": "test-name",
+        "label": "Test label",
+        "options": [
+            {"name": "Option 1", "value": "1"},
+            {"name": "Option 2", "value": "2"}
+        ]
+    }
 }}
 <label for="{{id}}" class="sr-only">{{label}}</label>
 <select name="{{name}}" id="{{id}}" {{#attributes}} {{name}}="{{value}}"{{/attributes}} class="form-control">
index e6696cd..f62655e 100644 (file)
@@ -1,3 +1,13 @@
+{{!
+    @template core/signup_form_layout
+
+    Example context (json):
+    {
+        "logourl": "https://moodle.org/logo/moodle-logo.svg",
+        "sitename": "Site name",
+        "formhtml": "<p>(Form html would go here)</p>"
+    }
+}}
 <div class="container-fluid">
     <div class="row">
         <div class="col-md-8 push-md-2 col-xl-6 push-xl-3">
index 2405f5f..d8c49a7 100644 (file)
@@ -1,3 +1,14 @@
+{{!
+    @template core/skip_links
+
+    Example context (json):
+    {
+        "links": [
+            {"url": "http://example.com/link1", "text": "Link 1"},
+            {"url": "http://example.com/link2", "text": "Link 2"}
+        ]
+    }
+}}
 <div>
 {{#links}}
     <a class="sr-only sr-only-focusable" href="#{{{url}}}">{{{text}}}</a>
index 918f272..d22db82 100644 (file)
@@ -7,7 +7,7 @@
         <input type="hidden" name="{{element.name}}" value="{{element.frozenvalue}}">
     {{/element.frozen}}
 {{/element.hardfrozen}}
-<input type="checkbox" name="{{element.name}}"
+<input type="checkbox" name="{{element.name}}" class="{{element.extraclasses}}"
     id="{{element.id}}"
     {{#element.selectedvalue}}
         value="{{element.selectedvalue}}"
index a6cb35c..8ec8fe9 100644 (file)
@@ -18,6 +18,7 @@
         {{/element.hardfrozen}}
         <input type="checkbox"
             name="{{element.name}}"
+            class="{{element.extraclasses}}"
             {{#element.selectedvalue}}
                 value="{{element.selectedvalue}}"
             {{/element.selectedvalue}}
index 8e098f0..150d2a0 100644 (file)
@@ -4,7 +4,7 @@
         <input type="hidden" name="{{element.name}}" value="{{element.frozenvalue}}">
     {{/element.frozen}}
 {{/element.hardfrozen}}
-<input type="checkbox" name="{{element.name}}"
+<input type="checkbox" name="{{element.name}}" class="{{element.extraclasses}}"
     id="{{element.id}}"
     {{#element.value}}
         value="{{element.value}}"
index 3a63cc4..ad55331 100644 (file)
@@ -15,6 +15,7 @@
         {{/element.hardfrozen}}
         <input type="checkbox"
             name="{{element.name}}"
+            class="{{element.extraclasses}}"
             {{#element.value}}
                 value="{{element.value}}"
             {{/element.value}}
index e22c726..f95ed92 100644 (file)
@@ -1,3 +1,9 @@
+{{!
+    @template theme_boost/nav-drawer
+
+
+    Example context (json): {}
+}}
 <div id="nav-drawer" data-region="drawer" class="hidden-print moodle-has-zindex {{^navdraweropen}}closed{{/navdraweropen}}" aria-hidden="{{#navdraweropen}}false{{/navdraweropen}}{{^navdraweropen}}true{{/navdraweropen}}" tabindex="-1">
     {{> theme_boost/flat_navigation }}
 </div>
index ea2ac8e..bc20577 100644 (file)
@@ -934,89 +934,7 @@ tr.flagged-tag a {
     text-align: left;
     border: 0 solid black;
 }
-/**
-* Smart Select Element
-*/
-.smartselect {
-    position: absolute;
-}
-.smartselect .smartselect_mask {
-    background-color: #fff;
-}
-.smartselect ul {
-    padding: 0;
-    margin: 0;
-}
-.smartselect ul li {
-    list-style: none;
-}
-.smartselect .smartselect_menu {
-    margin-right: 5px;
-}
-.safari .smartselect .smartselect_menu {
-    margin-left: 2px;
-}
-.smartselect .smartselect_menu,
-.smartselect .smartselect_submenu {
-    border: 1px solid #000;
-    background-color: #fff;
-    display: none;
-}
-.smartselect .smartselect_menu.visible,
-.smartselect .smartselect_submenu.visible {
-    display: block;
-}
-.smartselect .smartselect_menu_content ul li {
-    position: relative;
-    padding: 2px 5px;
-}
-.smartselect .smartselect_menu_content ul li a {
-    color: #333;
-    text-decoration: none;
-}
-.smartselect .smartselect_menu_content ul li a.selectable {
-    color: inherit;
-}
-.smartselect .smartselect_submenuitem {
-    background-image: url([[pix:moodle|t/collapsed]]);
-    background-repeat: no-repeat;
-    background-position: 100%;
-}
-/** Spanning mode */
-.smartselect.spanningmenu .smartselect_submenu {
-    position: absolute;
-    top: -1px;
-    left: 100%;
-}
-.smartselect.spanningmenu .smartselect_submenu a {
-    white-space: nowrap;
-    padding-right: 16px;
-}
-.smartselect.spanningmenu .smartselect_menu_content ul li a.selectable:hover {
-    text-decoration: underline;
-}
-/** Compact mode */
-.smartselect.compactmenu .smartselect_submenu {
-    position: relative;
-    margin: 2px -3px;
-    margin-left: 10px;
-    display: none;
-    border-width: 0;
-    z-index: 1010;
-}
-.smartselect.compactmenu .smartselect_submenu.visible {
-    display: block;
-}
-.smartselect.compactmenu .smartselect_menu {
-    z-index: 1000;
-    overflow: hidden;
-}
-.smartselect.compactmenu .smartselect_submenu .smartselect_submenu {
-    z-index: 1020;
-}
-.smartselect.compactmenu .smartselect_submenuitem:hover > .smartselect_menuitem_label {
-    font-weight: bold;
-}
+
 /**
 * Registration
 */
index 677e340..d09a3c3 100644 (file)
@@ -895,89 +895,6 @@ tr.flagged-tag a {
   border: 0 solid black;
 }
 /**
-* Smart Select Element
-*/
-.smartselect {
-  position: absolute;
-}
-.smartselect .smartselect_mask {
-  background-color: #fff;
-}
-.smartselect ul {
-  padding: 0;
-  margin: 0;
-}
-.smartselect ul li {
-  list-style: none;
-}
-.smartselect .smartselect_menu {
-  margin-right: 5px;
-}
-.safari .smartselect .smartselect_menu {
-  margin-left: 2px;
-}
-.smartselect .smartselect_menu,
-.smartselect .smartselect_submenu {
-  border: 1px solid #000;
-  background-color: #fff;
-  display: none;
-}
-.smartselect .smartselect_menu.visible,
-.smartselect .smartselect_submenu.visible {
-  display: block;
-}
-.smartselect .smartselect_menu_content ul li {
-  position: relative;
-  padding: 2px 5px;
-}
-.smartselect .smartselect_menu_content ul li a {
-  color: #333;
-  text-decoration: none;
-}
-.smartselect .smartselect_menu_content ul li a.selectable {
-  color: inherit;
-}
-.smartselect .smartselect_submenuitem {
-  background-image: url([[pix:moodle|t/collapsed]]);
-  background-repeat: no-repeat;
-  background-position: 100%;
-}
-/** Spanning mode */
-.smartselect.spanningmenu .smartselect_submenu {
-  position: absolute;
-  top: -1px;
-  left: 100%;
-}
-.smartselect.spanningmenu .smartselect_submenu a {
-  white-space: nowrap;
-  padding-right: 16px;
-}
-.smartselect.spanningmenu .smartselect_menu_content ul li a.selectable:hover {
-  text-decoration: underline;
-}
-/** Compact mode */
-.smartselect.compactmenu .smartselect_submenu {
-  position: relative;
-  margin: 2px -3px;
-  margin-left: 10px;
-  display: none;
-  border-width: 0;
-  z-index: 1010;
-}
-.smartselect.compactmenu .smartselect_submenu.visible {
-  display: block;
-}
-.smartselect.compactmenu .smartselect_menu {
-  z-index: 1000;
-  overflow: hidden;
-}
-.smartselect.compactmenu .smartselect_submenu .smartselect_submenu {
-  z-index: 1020;
-}
-.smartselect.compactmenu .smartselect_submenuitem:hover > .smartselect_menuitem_label {
-  font-weight: bold;
-}
-/**
 * Registration
 */
 #page-admin-registration-register .registration_textfield {
index ba6240c..5659302 100644 (file)
     You should have received a copy of the GNU General Public License
     along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
 }}
-<ul class="nav nav-tabs">
+{{!
+    @template theme_boost/admin_setting_tabs
+
+
+    Example context (json):
+    {
+        "tabs": [
+        {
+            "name": "tab1",
+            "active": 0,
+            "displayname": "Inactive tab1",
+            "html": "<p>Tab 1 content</p>"
+         },
+         {
+            "name": "tab2",
+            "active": 1,
+            "displayname": "Active tab2",
+            "html": "<p>Tab 2 content</p>"
+         }
+        ]
+    }
+}}
+<ul class="nav nav-tabs" role="tablist">
     {{#tabs}}
         <li class="{{#active}}active{{/active}}">
             <a href="#{{name}}" data-toggle="tab" role="tab">{{displayname}}</a>
index bb1ea9a..e3914df 100644 (file)
@@ -29,7 +29,7 @@
 
 defined('MOODLE_INTERNAL') || die();
 
-$version  = 2016122200.00;              // YYYYMMDD      = weekly release date of this DEV branch.
+$version  = 2016122800.00;              // YYYYMMDD      = weekly release date of this DEV branch.
                                         //         RR    = release increments - 00 in DEV branches.
                                         //           .XX = incremental changes.