Merge branch 'MDL-56958-master' of git://github.com/damyon/moodle
authorAndrew Nicols <andrew@nicols.co.uk>
Wed, 16 Nov 2016 03:07:20 +0000 (11:07 +0800)
committerAndrew Nicols <andrew@nicols.co.uk>
Wed, 16 Nov 2016 03:07:20 +0000 (11:07 +0800)
141 files changed:
admin/category.php
admin/roles/admins.php
admin/roles/allow.php
admin/roles/assign.php
admin/roles/check.php
admin/roles/classes/define_role_table_advanced.php
admin/roles/module.js
admin/roles/override.php
admin/settings/appearance.php
admin/tool/lp/amd/build/menubar.min.js
admin/tool/lp/amd/src/menubar.js
admin/tool/lp/classes/external/cohort_summary_exporter.php
admin/tool/lp/templates/competency_plan_navigation.mustache
admin/tool/lp/templates/course_competencies_page.mustache
admin/tool/lp/templates/manage_competencies_page.mustache
admin/tool/lp/templates/manage_competency_frameworks_page.mustache
admin/tool/lp/templates/manage_templates_page.mustache
admin/tool/lp/templates/plan_page.mustache
admin/tool/lp/templates/plans_page.mustache
admin/tool/lp/templates/user_competency_course_navigation.mustache
admin/tool/lp/templates/user_evidence_list_page.mustache
admin/tool/mobile/lang/en/tool_mobile.php
admin/tool/mobile/launch.php
admin/tool/usertours/lang/en/tool_usertours.php
auth/email/auth.php
auth/email/classes/external.php
auth/upgrade.txt
backup/util/ui/renderer.php
backup/util/ui/tests/behat/behat_backup.php
cache/stores/redis/lang/en/cachestore_redis.php
composer.json
composer.lock
course/tests/behat/category_management.feature
enrol/manual/yui/quickenrolment/assets/skins/sam/quickenrolment.css
enrol/manual/yui/quickenrolment/quickenrolment.js
enrol/self/lang/en/enrol_self.php
enrol/yui/rolemanager/rolemanager.js
filter/mediaplugin/lang/en/filter_mediaplugin.php
install/lang/da/admin.php
install/lang/da/error.php
install/lang/da/install.php
lang/en/admin.php
lang/en/deprecated.txt
lang/en/media.php
lang/en/message.php
lang/en/moodle.php
lang/en/role.php
lib/classes/component.php
lib/classes/plugininfo/theme.php
lib/classes/plugininfo/webservice.php
lib/classes/session/redis.php
lib/classes/upgrade/util.php
lib/coursecatlib.php
lib/deprecatedlib.php
lib/editor/atto/plugins/image/styles.css
lib/moodlelib.php
lib/navigationlib.php
lib/outputcomponents.php
lib/outputrenderers.php
lib/portfoliolib.php
lib/tests/coursecatlib_test.php
lib/tests/upgrade_util_test.php
lib/upgrade.txt
lib/weblib.php
lib/yui/build/moodle-core-notification-confirm/moodle-core-notification-confirm-debug.js
lib/yui/build/moodle-core-notification-confirm/moodle-core-notification-confirm-min.js
lib/yui/build/moodle-core-notification-confirm/moodle-core-notification-confirm.js
lib/yui/src/notification/js/confirm.js
login/confirm.php
media/player/html5audio/lang/en/media_html5audio.php
media/player/html5video/lang/en/media_html5video.php
media/player/videojs/lang/en/media_videojs.php
media/player/vimeo/lang/en/media_vimeo.php
media/player/youtube/lang/en/media_youtube.php
message/amd/build/message_area_messages.min.js
message/amd/build/preferences_notifications_list_controller.min.js
message/amd/src/message_area_messages.js
message/amd/src/preferences_notifications_list_controller.js
mod/assign/feedback/editpdf/fpdi/fpdi_bridge.php
mod/assign/feedback/editpdf/fpdi/readme_moodle.txt
mod/assign/lang/en/assign.php
mod/assign/locallib.php
mod/assign/upgrade.txt
mod/chat/gui_ajax/index.php
mod/chat/gui_ajax/module.js
mod/chat/gui_basic/index.php
mod/chat/report.php
mod/choice/renderer.php
mod/choice/report.php
mod/choice/styles.css [deleted file]
mod/data/field/url/field.class.php
mod/folder/renderer.php
mod/forum/discuss.php
mod/glossary/export.php
mod/glossary/lib.php
mod/glossary/styles.css
mod/glossary/view.php
mod/lesson/pagetypes/branchtable.php
mod/lesson/pagetypes/matching.php
mod/lesson/pagetypes/numerical.php
mod/lesson/pagetypes/shortanswer.php
mod/lesson/report.php
mod/quiz/amd/build/preflightcheck.min.js
mod/quiz/amd/src/preflightcheck.js
mod/quiz/lib.php
mod/quiz/tests/behat/preview.feature [new file with mode: 0644]
mod/quiz/view.php
mod/scorm/locallib.php
mod/scorm/styles.css
mod/scorm/view.php
mod/survey/lib.php
mod/survey/report.php
mod/survey/view.php
mod/workshop/classes/portfolio_caller.php
theme/boost/classes/output/core_renderer.php
theme/boost/layout/columns1.php
theme/boost/scss/moodle/admin.scss
theme/boost/scss/moodle/backup-restore.scss
theme/boost/scss/moodle/buttons.scss
theme/boost/scss/moodle/chat.scss
theme/boost/scss/moodle/core.scss
theme/boost/scss/moodle/course.scss
theme/boost/scss/moodle/expendable.scss
theme/boost/scss/moodle/forms.scss
theme/boost/scss/moodle/modules.scss
theme/boost/scss/moodle/undo.scss
theme/boost/scss/moodle/user.scss
theme/boost/templates/columns1.mustache
theme/boost/templates/core/navbar.mustache
theme/boost/templates/core/single_button.mustache
theme/boost/templates/core/url_select.mustache
theme/boost/templates/core_form/element-checkbox-inline.mustache
theme/boost/templates/flat_navigation.mustache
theme/bootstrapbase/less/moodle/admin.less
theme/bootstrapbase/less/moodle/buttons.less
theme/bootstrapbase/less/moodle/modules.less
theme/bootstrapbase/style/moodle.css
theme/upgrade.txt
user/selector/lib.php
user/selector/module.js
version.php

index 7847b2d..cb83e0d 100644 (file)
@@ -120,7 +120,7 @@ foreach ($settingspage->children as $childpage) {
 }
 if ($savebutton) {
     $outputhtml .= html_writer::start_tag('div', array('class' => 'form-buttons'));
-    $outputhtml .= html_writer::empty_tag('input', array('class' => 'form-submit', 'type' => 'submit', 'value' => get_string('savechanges','admin')));
+    $outputhtml .= html_writer::empty_tag('input', array('class' => 'btn btn-primary form-submit', 'type' => 'submit', 'value' => get_string('savechanges','admin')));
     $outputhtml .= html_writer::end_tag('div');
 }
 
index 57833b6..8245949 100644 (file)
@@ -133,9 +133,12 @@ echo $OUTPUT->header();
           </td>
       <td id="buttonscell">
         <p class="arrow_button">
-            <input name="add" id="add" type="submit" value="<?php echo $OUTPUT->larrow().'&nbsp;'.get_string('add'); ?>" title="<?php print_string('add'); ?>" /><br />
-            <input name="remove" id="remove" type="submit" value="<?php echo get_string('remove').'&nbsp;'.$OUTPUT->rarrow(); ?>" title="<?php print_string('remove'); ?>" />
-            <input name="main" id="main" type="submit" value="<?php echo get_string('mainadminset', 'core_role'); ?>" title="<?php print_string('mainadminset', 'core_role'); ?>" />
+            <input name="add" id="add" type="submit" value="<?php echo $OUTPUT->larrow().'&nbsp;'.get_string('add'); ?>"
+                   title="<?php print_string('add'); ?>" class="btn btn-secondary"/><br />
+            <input name="remove" id="remove" type="submit" value="<?php echo get_string('remove').'&nbsp;'.$OUTPUT->rarrow(); ?>"
+                   title="<?php print_string('remove'); ?>" class="btn btn-secondary"/><br />
+            <input name="main" id="main" type="submit" value="<?php echo get_string('mainadminset', 'core_role'); ?>"
+                   title="<?php print_string('mainadminset', 'core_role'); ?>" class="btn btn-secondary"/>
         </p>
       </td>
       <td id="potentialcell">
index 45f8b88..916e6c2 100644 (file)
@@ -80,7 +80,7 @@ echo $OUTPUT->box($controller->get_intro_text());
 echo '<form action="' . $baseurl . '" method="post">';
 echo '<input type="hidden" name="sesskey" value="' . sesskey() . '" />';
 echo html_writer::table($table);
-echo '<div class="submitbuttons">';
+echo '<div class="buttons">';
 echo '<input type="submit" class="btn btn-primary" name="submit" value="' . get_string('savechanges') . '"/>';
 echo '</div></form>';
 
index 3d723b8..f535866 100644 (file)
@@ -201,11 +201,13 @@ if ($roleid) {
       </td>
       <td id="buttonscell">
           <div id="addcontrols">
-              <input name="add" id="add" type="submit" value="<?php echo $OUTPUT->larrow().'&nbsp;'.get_string('add'); ?>" title="<?php print_string('add'); ?>" /><br />
+              <input name="add" id="add" type="submit" value="<?php echo $OUTPUT->larrow().'&nbsp;'.get_string('add'); ?>"
+                     title="<?php print_string('add'); ?>" class="btn btn-secondary"/><br />
           </div>
 
           <div id="removecontrols">
-              <input name="remove" id="remove" type="submit" value="<?php echo get_string('remove').'&nbsp;'.$OUTPUT->rarrow(); ?>" title="<?php print_string('remove'); ?>" />
+              <input name="remove" id="remove" type="submit" value="<?php echo get_string('remove').'&nbsp;'.$OUTPUT->rarrow(); ?>"
+                     title="<?php print_string('remove'); ?>" class="btn btn-secondary"/>
           </div>
       </td>
       <td id="potentialcell">
index 6649aaf..0f40308 100644 (file)
@@ -172,7 +172,8 @@ echo $OUTPUT->heading('<label for="reportuser">' . $selectheading . '</label>',
 $userselector->display();
 
 // Submit button and the end of the form.
-echo '<p id="chooseusersubmit"><input type="submit" value="' . get_string('showthisuserspermissions', 'core_role') . '" /></p>';
+echo '<p id="chooseusersubmit"><input type="submit" value="' . get_string('showthisuserspermissions', 'core_role') . '" ' .
+     'class="btn btn-primary"/></p>';
 echo '</form>';
 echo $OUTPUT->box_end();
 
index 303855b..bcf6a49 100644 (file)
@@ -471,13 +471,13 @@ class core_role_define_role_table_advanced extends core_role_capability_table_wi
     }
 
     protected function get_name_field($id) {
-        return '<input type="text" class="form-control" id="' . $id . '" name="' . $id . '" size="30"
-            value="' . s($this->role->name) . '" />';
+        return '<input type="text" id="' . $id . '" name="' . $id . '" maxlength="254" value="' . s($this->role->name) . '"' .
+                ' class="form-control"/>';
     }
 
     protected function get_shortname_field($id) {
-        return '<input type="text" class="form-control" id="' . $id . '" name="' . $id . '" size="30"
-            value="' . s($this->role->shortname) . '" />';
+        return '<input type="text" id="' . $id . '" name="' . $id . '" maxlength="254" value="' . s($this->role->shortname) . '"' .
+                ' class="form-control"/>';
     }
 
     protected function get_description_field($id) {
index bbfe602..243fd67 100644 (file)
@@ -54,7 +54,8 @@ M.core_role.init_cap_table_filter = function(Y, tableid, contextid) {
             this.input = Y.Node.create('<input class="form-control m-x-1" type="text"' +
                 ' id="'+this.table.get('id')+'capabilitysearch" value="'+Y.Escape.html(filtervalue)+'" />');
             // Create a label for the search input.
-            this.label = Y.Node.create('<label for="'+this.input.get('id')+'">'+M.util.get_string('filter', 'moodle')+' </label>');
+            this.label = Y.Node.create('<label for="' + this.input.get('id') + '">' +
+                M.util.get_string('filter', 'moodle') + ' </label>');
             // Create a clear button to clear the input.
             this.button = Y.Node.create('<input type="button" class="btn btn-primary"' +
                 ' value="'+M.util.get_string('clear', 'moodle')+'" />').set('disabled', filtervalue=='');
index 1583fab..878c820 100644 (file)
@@ -177,8 +177,10 @@ if (!empty($capabilities)) {
     }
 
     echo html_writer::start_tag('div', array('class'=>'submit_buttons'));
-    echo html_writer::empty_tag('input', array('type'=>'submit', 'name'=>'savechanges', 'value'=>get_string('savechanges')));
-    echo html_writer::empty_tag('input', array('type'=>'submit', 'name'=>'cancel', 'value'=>get_string('cancel')));
+    $attrs = array('type'=>'submit', 'name'=>'savechanges', 'value'=>get_string('savechanges'), 'class'=>'btn btn-primary');
+    echo html_writer::empty_tag('input', $attrs);
+    $attrs = array('type'=>'submit', 'name'=>'cancel', 'value'=>get_string('cancel'), 'class' => 'btn btn-secondary');
+    echo html_writer::empty_tag('input', $attrs);
     echo html_writer::end_tag('div');
     echo html_writer::end_tag('div');
     echo html_writer::end_tag('form');
index 95ab246..5a20af8 100644 (file)
@@ -181,7 +181,7 @@ preferences,moodle|/user/preferences.php|preferences',
     $temp->add(new admin_setting_configselect('navsortmycoursessort', new lang_string('navsortmycoursessort', 'admin'), new lang_string('navsortmycoursessort_help', 'admin'), 'sortorder', $sortoptions));
     $temp->add(new admin_setting_configtext('navcourselimit',new lang_string('navcourselimit','admin'),new lang_string('confignavcourselimit', 'admin'),20,PARAM_INT));
     $temp->add(new admin_setting_configcheckbox('usesitenameforsitepages', new lang_string('usesitenameforsitepages', 'admin'), new lang_string('configusesitenameforsitepages', 'admin'), 0));
-    $temp->add(new admin_setting_configcheckbox('linkadmincategories', new lang_string('linkadmincategories', 'admin'), new lang_string('linkadmincategories_help', 'admin'), 0));
+    $temp->add(new admin_setting_configcheckbox('linkadmincategories', new lang_string('linkadmincategories', 'admin'), new lang_string('linkadmincategories_help', 'admin'), 1));
     $temp->add(new admin_setting_configcheckbox('linkcoursesections', new lang_string('linkcoursesections', 'admin'), new lang_string('linkcoursesections_help', 'admin'), 0));
     $temp->add(new admin_setting_configcheckbox('navshowfrontpagemods', new lang_string('navshowfrontpagemods', 'admin'), new lang_string('navshowfrontpagemods_help', 'admin'), 1));
     $temp->add(new admin_setting_configcheckbox('navadduserpostslinks', new lang_string('navadduserpostslinks', 'admin'), new lang_string('navadduserpostslinks_help', 'admin'), 1));
index 6d50e26..627c7b7 100644 (file)
Binary files a/admin/tool/lp/amd/build/menubar.min.js and b/admin/tool/lp/amd/build/menubar.min.js differ
index 76595fe..4673e3d 100644 (file)
@@ -794,6 +794,7 @@ define(['jquery'], function($) {
         this.allItems.addClass('tool-lp-menu-item');
         this.rootMenus.addClass('tool-lp-root-menu');
         this.subMenus.addClass('tool-lp-sub-menu');
+        this.subMenuItems.addClass('dropdown-item');
     };
 
     return /** @alias module:tool_lp/menubar */ {
index a22ac9f..cd00a6f 100644 (file)
@@ -50,7 +50,8 @@ class cohort_summary_exporter extends \core_competency\external\exporter {
             ),
             'idnumber' => array(
                 'type' => PARAM_RAW,        // ID numbers are plain text.
-                'default' => ''
+                'default' => '',
+                'null' => NULL_ALLOWED
             ),
             'visible' => array(
                 'type' => PARAM_BOOL,
index 21bd7af..d658ea3 100644 (file)
@@ -36,7 +36,7 @@
 {{#hascompetencies}}
 <span>
 <label for="competency-nav-{{uniqid}}" class="accesshide">{{#str}}jumptocompetency, tool_lp{{/str}}</label>
-<select id="competency-nav-{{uniqid}}">
+<select class="custom-select" id="competency-nav-{{uniqid}}">
 {{#competencies}}
 <option value="{{id}}" {{#selected}}selected="selected"{{/selected}}>{{{shortname}}} {{idnumber}}</option>
 {{/competencies}}
index 39b061e..eaa0f43 100644 (file)
@@ -85,7 +85,7 @@
         <div data-region="coursecompetency-ruleoutcome">
             <label>
                 {{#str}}uponcoursecompletion, tool_lp{{/str}}
-                <select data-field="ruleoutcome" data-id="{{coursecompetency.id}}">
+                <select data-field="ruleoutcome" data-id="{{coursecompetency.id}}" class="custom-select">
                   {{#ruleoutcomeoptions}}
                      <option value="{{value}}" {{#selected}}selected{{/selected}}>{{text}}</option>
                   {{/ruleoutcomeoptions}}
index 8349d31..bf43b5d 100644 (file)
@@ -63,7 +63,7 @@
                             <ul title="{{#str}}edit{{/str}}" class="competencyactionsmenu">
                                 <li>
                                     <a href="#">{{#str}}edit{{/str}}</a><b class="caret"></b>
-                                    <ul class="dropdown-menu">
+                                    <ul class="dropdown dropdown-menu">
                                     <li class="dropdown-item">
                                         <a href="#" data-action="edit">
                                             {{#pix}}t/edit{{/pix}} {{#str}}edit{{/str}}
index f3d6a3e..2fcac12 100644 (file)
@@ -60,7 +60,7 @@
             <ul title="{{#str}}edit{{/str}}" class="competencyframeworkactions">
                 <li>
                     <a href="#">{{#str}}edit{{/str}}</a><b class="caret"></b>
-                    <ul class="dropdown-menu">
+                    <ul class="dropdown dropdown-menu">
                         <li class="dropdown-item">
                             <a href="{{pluginbaseurl}}/editcompetencyframework.php?id={{id}}&amp;pagecontextid={{pagecontextid}}">
                                 {{#pix}}t/edit{{/pix}} {{#str}}edit{{/str}}
index f0af972..4a267f6 100644 (file)
@@ -62,7 +62,7 @@
             <ul class="templateactions">
                 <li>
                     <a href="#">{{#str}}edit{{/str}}</a><b class="caret"></b>
-                    <ul class="dropdown-menu">
+                    <ul class="dropdown dropdown-menu">
                         <li class="dropdown-item">
                             <a href="{{pluginbaseurl}}/edittemplate.php?id={{id}}&amp;pagecontextid={{pagecontextid}}&amp;return=templates">
                                 {{#pix}}t/edit{{/pix}} {{#str}}edit{{/str}}
index d927eae..96f13f0 100644 (file)
                             <ul title="{{#str}}edit{{/str}}" class="user-competency-actions">
                                 <li>
                                     <a href="#">{{#str}}edit{{/str}}</a><b class="caret"></b>
-                                    <ul class="dropdown-menu">
+                                    <ul class="dropdown dropdown-menu">
                                         {{#usercompetency.isrequestreviewallowed}}
                                             <li class="dropdown-item">
                                                 <a href="#" data-action="request-review">{{#pix}}t/edit, core{{/pix}} {{#str}}requestreview, tool_lp{{/str}}</a>
index 84a4b66..8a3af99 100644 (file)
@@ -70,7 +70,7 @@
                 <ul title="{{#str}}edit{{/str}}" class="planactions">
                 <li>
                     <a href="#">{{#str}}edit{{/str}}</a><b class="caret"></b>
-                    <ul class="dropdown-menu">
+                    <ul class="dropdown dropdown-menu">
                     <li class="{{^canbeedited}} disabled {{/canbeedited}} dropdown-item">
                         <a href="{{#canbeedited}}
                                     {{pluginbaseurl}}/editplan.php?id={{id}}&amp;userid={{userid}}&amp;return=plans
index 533d01f..31e51ca 100644 (file)
@@ -41,7 +41,7 @@
 {{#hasusers}}
 <span>
 <label for="user-nav-{{uniqid}}" class="accesshide">{{#str}}jumptouser, tool_lp{{/str}}</label>
-<select id="user-nav-{{uniqid}}">
+<select id="user-nav-{{uniqid}}" class="custom-select">
 {{#users}}
 <option value="{{id}}" {{#selected}}selected="selected"{{/selected}}>{{fullname}}</option>
 {{/users}}
@@ -52,7 +52,7 @@
 {{#hascompetencies}}
 <span>
 <label for="competency-nav-{{uniqid}}" class="accesshide">{{#str}}jumptocompetency, tool_lp{{/str}}</label>
-<select id="competency-nav-{{uniqid}}">
+<select id="competency-nav-{{uniqid}}" class="custom-select">
 {{#competencies}}
 <option value="{{id}}" {{#selected}}selected="selected"{{/selected}}>{{{shortname}}} {{idnumber}}</option>
 {{/competencies}}
index d24f138..16ae1b7 100644 (file)
@@ -89,7 +89,7 @@
                 <ul title="{{#str}}edit{{/str}}" class="user-evidence-actions">
                 <li>
                     <a href="#">{{#str}}edit{{/str}}</a><b class="caret"></b>
-                    <ul class="dropdown-menu">
+                    <ul class="dropdown dropdown-menu">
                         <li class="dropdown-item">
                             <a href="{{pluginbaseurl}}/user_evidence_edit.php?id={{id}}&amp;userid={{userid}}&amp;return=list">
                                 {{#pix}}t/edit{{/pix}} {{#str}}editthisuserevidence, tool_lp{{/str}}
index c5862e7..53081b0 100644 (file)
@@ -22,8 +22,8 @@
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
 
-$string['autologinkeygenerationlockout'] = 'Auto-login key generation is locked out, too much requests in an hour.';
-$string['autologinnotallowedtoadmins'] = 'Auto-login is not allowed to site admins';
+$string['autologinkeygenerationlockout'] = 'Auto-login key generation is blocked because of too many requests within an hour.';
+$string['autologinnotallowedtoadmins'] = 'Auto-login is not allowed for site admins.';
 $string['clickheretolaunchtheapp'] = 'Click here if the app does not open automatically.';
 $string['configmobilecssurl'] = 'A CSS file to customise your mobile app interface.';
 $string['enablesmartappbanners'] = 'Enable Smart App Banners';
index 030daba..7600862 100644 (file)
@@ -31,6 +31,7 @@ require_once($CFG->libdir . '/externallib.php');
 $serviceshortname  = required_param('service',  PARAM_ALPHANUMEXT);
 $passport          = required_param('passport',  PARAM_RAW);    // Passport send from the app to validate the response URL.
 $urlscheme         = optional_param('urlscheme', 'moodlemobile', PARAM_NOTAGS); // The URL scheme the app supports.
+$confirmed         = optional_param('confirmed', false, PARAM_BOOL);  // If we are being redirected after user confirmation.
 
 // Check web services enabled.
 if (!$CFG->enablewebservices) {
@@ -39,7 +40,8 @@ if (!$CFG->enablewebservices) {
 
 // Check if the plugin is properly configured.
 $typeoflogin = get_config('tool_mobile', 'typeoflogin');
-if ($typeoflogin != tool_mobile\api::LOGIN_VIA_BROWSER and
+if (empty($SESSION->justloggedin) and
+        $typeoflogin != tool_mobile\api::LOGIN_VIA_BROWSER and
         $typeoflogin != tool_mobile\api::LOGIN_VIA_EMBEDDED_BROWSER) {
     throw new moodle_exception('pluginnotenabledorconfigured', 'tool_mobile');
 }
@@ -61,8 +63,8 @@ $token = external_generate_token_for_current_user($service);
 $privatetoken = $token->privatetoken;
 external_log_token_request($token);
 
-// Invalidate the private token if external_generate_token_for_current_user did not create a new token.
-if ($token->timecreated < $timenow) {
+// Don't return the private token if the user didn't just log in and a new token wasn't created.
+if (empty($SESSION->justloggedin) and $token->timecreated < $timenow) {
     $privatetoken = null;
 }
 
@@ -87,11 +89,24 @@ if (!empty($forcedurlscheme)) {
 $location = "$urlscheme://token=$apptoken";
 
 // For iOS 10 onwards, we have to simulate a user click.
-if (core_useragent::is_ios()) {
-    $PAGE->set_context(null);
+// If we come from the confirmation page, we should display a nicer page.
+$isios = core_useragent::is_ios();
+if ($confirmed or $isios) {
+    $PAGE->set_context(context_system::instance());
+    $PAGE->set_heading($COURSE->fullname);
     $PAGE->set_url('/local/mobile/launch.php', array('service' => $serviceshortname, 'passport' => $passport, 'urlscheme' => $urlscheme));
 
     echo $OUTPUT->header();
+    if ($confirmed) {
+        $confirmedstr = get_string('confirmed');
+        $PAGE->navbar->add($confirmedstr);
+        $PAGE->set_title($confirmedstr);
+        echo $OUTPUT->notification($confirmedstr, \core\output\notification::NOTIFY_SUCCESS);
+        echo $OUTPUT->box_start('generalbox centerpara boxwidthnormal boxaligncenter');
+        echo $OUTPUT->single_button(new moodle_url('/course/'), get_string('courses'));
+        echo $OUTPUT->box_end();
+    }
+
     $notice = get_string('clickheretolaunchtheapp', 'tool_mobile');
     echo html_writer::link($location, $notice, array('id' => 'launchapp'));
     echo html_writer::script(
index 31f74bf..32cd9b4 100644 (file)
@@ -28,8 +28,8 @@ $string['appliesto'] = 'Applies to';
 $string['below'] = 'Below';
 $string['block'] = 'Block';
 $string['block_named'] = 'Block named \'{$a}\'';
-$string['cachedef_stepdata'] = 'List of User Tour steps';
-$string['cachedef_tourdata'] = 'List of enabled User Tours information which are fetched on every page';
+$string['cachedef_stepdata'] = 'List of user tour steps';
+$string['cachedef_tourdata'] = 'List of enabled user tours information which is fetched on every page';
 $string['description'] = 'Description';
 $string['confirmstepremovalquestion'] = 'Are you sure that you wish to remove this step?';
 $string['confirmstepremovaltitle'] = 'Confirm step removal';
@@ -37,16 +37,9 @@ $string['confirmtourremovalquestion'] = 'Are you sure that you wish to remove th
 $string['confirmtourremovaltitle'] = 'Confirm tour removal';
 $string['content'] = 'Content';
 $string['content_heading'] = 'Content';
-$string['content_help'] = 'This is the content of the step.
-You can enter a content in the following formats:
-<dl>
-    <dt>Plain text</dt>
-    <dd>A plain text description</dd>
-    <dt>Moodle MultiLang</dt>
-    <dd>A string which makes use of the Moodle MultiLang format</dd>
-    <dt>Moodle Translated string</dt>
-    <dd>A value found in a standard Moodle language file in the format identifier,component</dd>
-</dl>';
+$string['content_help'] = 'Content describing the step may be added as plain text, enclosed in multilang tags (for use with the multi-language content filter) if required.
+
+Alternatively, a language string ID may be entered in the format identifier,component (with no brackets or space after the comma).';
 $string['cssselector'] = 'CSS selector';
 $string['defaultvalue'] = 'Default ({$a})';
 $string['delay'] = 'Delay before showing the step';
@@ -92,35 +85,24 @@ $string['pluginname'] = 'User tours';
 $string['resettouronpage'] = 'Reset user tour on this page';
 $string['right'] = 'Right';
 $string['select_block'] = 'Select a block';
-$string['targettype_help'] = 'Every step is associated with a part of the page which you must choose. To make this easier there are several types of target for different types of page content.
-<dl>
-    <dt>Block</dt>
-    <dd>Display the step next to the first matching block of the type on the page.</dd>
-    <dt>Selector</dt>
-    <dd>CSS Selectors are a powerful way which allow you to select different parts of the page based on metadata built into the page.</dd>
-    <dt>Display in middle of the page</dt>
-    <dd>Instead of associating the step with a specific part of the page you can have it displayed in the middle of the page.</dd>
-</dl>';
+$string['targettype_help'] = 'Each step is associated with a part of the page - the target. Target types are:
+
+* Block - for displaying a step next to a specified block
+* CSS selector - for accurately defining the target area using CSS
+* Display in middle of page - for a step which does not need to be associated with a specific part of the page';
 $string['selector_defaulttitle'] = 'Enter a descriptive title';
 $string['selectordisplayname'] = 'A CSS selector matching \'{$a}\'';
 $string['skip'] = 'Skip';
 $string['target'] = 'Target';
-$string['target_heading'] = 'Step Target';
+$string['target_heading'] = 'Step target';
 $string['target_block'] = 'Block';
 $string['target_selector'] = 'Selector';
 $string['target_unattached'] = 'Display in middle of page';
 $string['targettype'] = 'Target type';
 $string['title'] = 'Title';
-$string['title_help'] = 'This is the title shown at the top of the step.
-You can enter a title in the following formats:
-<dl>
-    <dt>Plain text</dt>
-    <dd>A plain text description</dd>
-    <dt>Moodle MultiLang</dt>
-    <dd>A string which makes use of the Moodle MultiLang format</dd>
-    <dt>Moodle Translated string</dt>
-    <dd>A value found in a standard Moodle language file in the format identifier,component</dd>
-</dl>';
+$string['title_help'] = 'The title of a step may be added as plain text, enclosed in multilang tags (for use with the multi-language content filter) if required.
+
+Alternatively, a language string ID may be entered in the format identifier,component (with no brackets or space after the comma).';
 $string['tourconfig'] = 'Tour configuration file to import';
 $string['tourlist_explanation'] = 'You can create as many tours as you like and enable them for different parts of Moodle. Only one tour can be created per page.';
 $string['tours'] = 'Tours';
@@ -136,11 +118,9 @@ Note: Backdrops are not compatible with some parts of the page such as the navig
 ';
 $string['reflex'] = 'Proceed on click';
 $string['reflex_help'] = 'Proceed to the next step when the target is clicked on.';
-$string['placement_help'] = 'You can place a step either above, below, to the left of, or to the right of the target.
+$string['placement_help'] = 'A step may be placed above, below, left or right of the target. Above or below is recommended, as these adjust better for mobile display.
 
-The best options are above, or below as these adjust better for mobile display.
-
-If the step does not fit into the page at at the placement you choose, it will be automatically be moved to give the best viewing experience. ';
+If the step does not fit on a particular page in the specified place, it will be automatically placed elsewhere.';
 $string['delay_help'] = 'You can optionally choose to add a delay before the step is displayed.
 
 This delay is in milliseconds.';
@@ -149,21 +129,7 @@ $string['sharedtourslink'] = 'Tour repository';
 $string['usertours'] = 'User tours';
 $string['usertours:managetours'] = 'Create, edit, and remove user tours';
 $string['target_selector_targetvalue'] = 'CSS selectors';
-$string['target_selector_targetvalue_help'] = 'You can use a "CSS Selector" to target almost any element on the page.
-
-CSS Selectors are very powerful and you can easily find parts of the page by building up the selector gradually.
-
-Mozilla provide some [very good
-documentation](https://developer.mozilla.org/en/docs/Web/Guide/CSS/Getting_started/Selectors)
-for selectors which may help you to build your selectors.
-
-You will also find your browser\'s developer tools to be extremely useful in creating these selectors:
-
-* [Google Chrome](https://developer.chrome.com/devtools#dom-and-styles)
-* [Mozilla Firefox](https://developer.mozilla.org/en-US/docs/Tools/DOM_Property_Viewer)
-* [Microsoft Edge](https://developer.microsoft.com/en-us/microsoft-edge/platform/documentation/f12-devtools-guide/)
-* [Apple Safari](https://developer.apple.com/library/iad/documentation/AppleApplications/Conceptual/Safari_Developer_Guide/ResourcesandtheDOM/ResourcesandtheDOM.html#//apple_ref/doc/uid/TP40007874-CH3-SW1)
-';
+$string['target_selector_targetvalue_help'] = 'A CSS selector can be used to target almost any element on the page. The appropriate selector can be easily found using the developer tools for your web browser.';
 $string['viewtour_info'] = 'This is the \'{$a->tourname}\' tour. It applies to the path \'{$a->path}\'.';
 $string['viewtour_edit'] = 'You can <a href="{$a->editlink}">edit the tour defaults</a> and <a href="{$a->resetlink}">force the tour to be displayed</a> to all users again.';
 $string['tour_resetforall'] = 'The state of the tour has been reset. It will be displayed to all users again.';
index ffa0e91..e9c1da1 100644 (file)
@@ -95,6 +95,24 @@ class auth_plugin_email extends auth_plugin_base {
      * @param boolean $notify print notice with link and terminate
      */
     function user_signup($user, $notify=true) {
+        // Standard signup, without custom confirmatinurl.
+        return $this->user_signup_with_confirmation($user, $notify);
+    }
+
+    /**
+     * Sign up a new user ready for confirmation.
+     *
+     * Password is passed in plaintext.
+     * A custom confirmationurl could be used.
+     *
+     * @param object $user new user object
+     * @param boolean $notify print notice with link and terminate
+     * @param string $confirmationurl user confirmation URL
+     * @return boolean true if everything well ok and $notify is set to true
+     * @throws moodle_exception
+     * @since Moodle 3.2
+     */
+    public function user_signup_with_confirmation($user, $notify=true, $confirmationurl = null) {
         global $CFG, $DB;
         require_once($CFG->dirroot.'/user/profile/lib.php');
         require_once($CFG->dirroot.'/user/lib.php');
@@ -115,8 +133,8 @@ class auth_plugin_email extends auth_plugin_base {
         // Trigger event.
         \core\event\user_created::create_from_userid($user->id)->trigger();
 
-        if (! send_confirmation_email($user)) {
-            print_error('auth_emailnoemail','auth_email');
+        if (! send_confirmation_email($user, $confirmationurl)) {
+            print_error('auth_emailnoemail', 'auth_email');
         }
 
         if ($notify) {
index 4ecff91..c7bfde7 100644 (file)
@@ -202,7 +202,8 @@ class auth_email_external extends external_api {
                             'value' => new external_value(PARAM_RAW, 'Custom field value, can be an encoded json if required')
                         )
                     ), 'User custom fields (also known as user profile fields)', VALUE_DEFAULT, array()
-                )
+                ),
+                'redirect' => new external_value(PARAM_URL, 'Redirect the user to this url after confirmation.', VALUE_DEFAULT, ''),
             )
         );
     }
@@ -220,13 +221,15 @@ class auth_email_external extends external_api {
      * @param  string $recaptchachallengehash recaptcha challenge hash
      * @param  string $recaptcharesponse      recaptcha response
      * @param  array  $customprofilefields    user custom fields (also known as user profile fields)
+     * @param  string $redirect               Url to redirect the user after confirmation
      * @return array settings and possible warnings
      * @since Moodle 3.2
      * @throws moodle_exception
      * @throws invalid_parameter_exception
      */
     public static function signup_user($username, $password, $firstname, $lastname, $email, $city = '', $country = '',
-                                        $recaptchachallengehash = '', $recaptcharesponse = '', $customprofilefields = array()) {
+                                        $recaptchachallengehash = '', $recaptcharesponse = '', $customprofilefields = array(),
+                                        $redirect = '') {
         global $CFG, $PAGE;
 
         $warnings = array();
@@ -243,6 +246,7 @@ class auth_email_external extends external_api {
                 'recaptchachallengehash' => $recaptchachallengehash,
                 'recaptcharesponse' => $recaptcharesponse,
                 'customprofilefields' => $customprofilefields,
+                'redirect' => $redirect,
             )
         );
 
@@ -324,7 +328,16 @@ class auth_email_external extends external_api {
             $user = signup_setup_new_user((object) $data);
 
             $authplugin = get_auth_plugin('email');
-            $authplugin->user_signup($user, false);
+
+            // Check if we should redirect the user once the user is confirmed.
+            $confirmationurl = null;
+            if (!empty($params['redirect'])) {
+                // Pass via moodle_url to fix thinks like admin links.
+                $redirect = new moodle_url($params['redirect']);
+
+                $confirmationurl = new moodle_url('/login/confirm.php', array('redirect' => $redirect->out()));
+            }
+            $authplugin->user_signup_with_confirmation($user, false, $confirmationurl);
 
             $result = array(
                 'success' => true,
index d339ea0..8a1dc21 100644 (file)
@@ -7,6 +7,8 @@ information provided here is intended especially for developers.
   This can be used to modify the user object before any authentication errors are raised.
 * The block_login now displays the loginpage_idp_list() links as well as main login page.
 * The authentication plugin auth_radius has been moved to https://github.com/moodlehq/moodle-auth_radius
+* New auth_email::user_signup_with_confirmation() method has a new optional parameter $confirmationurl to provide a different
+  confirmation URL.
 
 === 3.0 ===
 
index a3901e5..e5e93c9 100644 (file)
@@ -290,7 +290,8 @@ class core_backup_renderer extends plugin_renderer_base {
             } else {
                 $html .= $selectacategoryhtml;
             }
-            $html .= $this->backup_detail_pair('', html_writer::empty_tag('input', array('type' => 'submit', 'value' => get_string('continue'))));
+            $attrs = array('type' => 'submit', 'value' => get_string('continue'), 'class' => 'btn btn-primary');
+            $html .= $this->backup_detail_pair('', html_writer::empty_tag('input', $attrs));
             $html .= html_writer::end_tag('div');
             $html .= html_writer::end_tag('form');
         }
@@ -306,7 +307,8 @@ class core_backup_renderer extends plugin_renderer_base {
                 backup::TARGET_CURRENT_ADDING, array('checked' => 'checked'));
             $html .= $this->backup_detail_input(get_string('restoretocurrentcoursedeleting', 'backup'), 'radio', 'target',
                 backup::TARGET_CURRENT_DELETING);
-            $html .= $this->backup_detail_pair('', html_writer::empty_tag('input', array('type' => 'submit', 'value' => get_string('continue'))));
+            $attrs = array('type' => 'submit', 'value' => get_string('continue'), 'class' => 'btn btn-primary');
+            $html .= $this->backup_detail_pair('', html_writer::empty_tag('input', $attrs));
             $html .= html_writer::end_tag('div');
             $html .= html_writer::end_tag('form');
         }
@@ -340,7 +342,8 @@ class core_backup_renderer extends plugin_renderer_base {
             } else {
                 $html .= $selectacoursehtml;
             }
-            $html .= $this->backup_detail_pair('', html_writer::empty_tag('input', array('type' => 'submit', 'value' => get_string('continue'))));
+            $attrs = array('type' => 'submit', 'value' => get_string('continue'), 'class' => 'btn btn-primary');
+            $html .= $this->backup_detail_pair('', html_writer::empty_tag('input', $attrs));
             $html .= html_writer::end_tag('div');
             $html .= html_writer::end_tag('form');
         }
@@ -371,7 +374,8 @@ class core_backup_renderer extends plugin_renderer_base {
         $html .= html_writer::start_tag('div', array('class' => 'ics-existing-course backup-section'));
         $html .= $this->output->heading(get_string('importdatafrom'), 2, array('class' => 'header'));
         $html .= $this->backup_detail_pair(get_string('selectacourse', 'backup'), $this->render($courses));
-        $html .= $this->backup_detail_pair('', html_writer::empty_tag('input', array('type' => 'submit', 'value' => get_string('continue'))));
+        $attrs = array('type' => 'submit', 'value' => get_string('continue'), 'class' => 'btn btn-primary');
+        $html .= $this->backup_detail_pair('', html_writer::empty_tag('input', $attrs));
         $html .= html_writer::end_tag('div');
         $html .= html_writer::end_tag('form');
         $html .= html_writer::end_tag('div');
@@ -470,9 +474,11 @@ class core_backup_renderer extends plugin_renderer_base {
     public function substage_buttons($haserrors) {
         $output  = html_writer::start_tag('div', array('continuebutton'));
         if (!$haserrors) {
-            $output .= html_writer::empty_tag('input', array('type' => 'submit', 'value' => get_string('continue')));
+            $attrs = array('type' => 'submit', 'value' => get_string('continue'), 'class' => 'btn btn-primary');
+            $output .= html_writer::empty_tag('input', $attrs);
         }
-        $output .= html_writer::empty_tag('input', array('type' => 'submit', 'name' => 'cancel', 'value' => get_string('cancel')));
+        $attrs = array('type' => 'submit', 'name' => 'cancel', 'value' => get_string('cancel'), 'class' => 'btn btn-secondary');
+        $output .= html_writer::empty_tag('input', $attrs);
         $output .= html_writer::end_tag('div');
         return $output;
     }
@@ -601,7 +607,7 @@ class core_backup_renderer extends plugin_renderer_base {
     public function render_restore_course_search(restore_course_search $component) {
         $url = $component->get_url();
 
-        $output = html_writer::start_tag('div', array('class' => 'restore-course-search'));
+        $output = html_writer::start_tag('div', array('class' => 'restore-course-search form-inline m-b-1'));
         $output .= html_writer::start_tag('div', array('class' => 'rcs-results'));
 
         $table = new html_table();
@@ -641,8 +647,20 @@ class core_backup_renderer extends plugin_renderer_base {
         $output .= html_writer::end_tag('div');
 
         $output .= html_writer::start_tag('div', array('class' => 'rcs-search'));
-        $output .= html_writer::empty_tag('input', array('type' => 'text', 'name' => restore_course_search::$VAR_SEARCH, 'value' => $component->get_search()));
-        $output .= html_writer::empty_tag('input', array('type' => 'submit', 'name' => 'searchcourses', 'value' => get_string('search')));
+        $attrs = array(
+            'type' => 'text',
+            'name' => restore_course_search::$VAR_SEARCH,
+            'value' => $component->get_search(),
+            'class' => 'form-control'
+        );
+        $output .= html_writer::empty_tag('input', $attrs);
+        $attrs = array(
+            'type' => 'submit',
+            'name' => 'searchcourses',
+            'value' => get_string('search'),
+            'class' => 'btn btn-secondary'
+        );
+        $output .= html_writer::empty_tag('input', $attrs);
         $output .= html_writer::end_tag('div');
 
         $output .= html_writer::end_tag('div');
@@ -663,8 +681,20 @@ class core_backup_renderer extends plugin_renderer_base {
             $output .= $this->output->notification(get_string('nomatchingcourses', 'backup'));
 
             $output .= html_writer::start_tag('div', array('class' => 'ics-search'));
-            $output .= html_writer::empty_tag('input', array('type' => 'text', 'name' => restore_course_search::$VAR_SEARCH, 'value' => $component->get_search()));
-            $output .= html_writer::empty_tag('input', array('type' => 'submit', 'name' => 'searchcourses', 'value' => get_string('search')));
+            $attrs = array(
+                'type' => 'text',
+                'name' => restore_course_search::$VAR_SEARCH,
+                'value' => $component->get_search(),
+                'class' => 'form-control'
+            );
+            $output .= html_writer::empty_tag('input', $attrs);
+            $attrs = array(
+                'type' => 'submit',
+                'name' => 'searchcourses',
+                'value' => get_string('search'),
+                'class' => 'btn btn-secondary'
+            );
+            $output .= html_writer::empty_tag('input', $attrs);
             $output .= html_writer::end_tag('div');
 
             $output .= html_writer::end_tag('div');
@@ -709,8 +739,19 @@ class core_backup_renderer extends plugin_renderer_base {
         $output .= html_writer::end_tag('div');
 
         $output .= html_writer::start_tag('div', array('class' => 'ics-search'));
-        $output .= html_writer::empty_tag('input', array('type' => 'text', 'name' => restore_course_search::$VAR_SEARCH, 'value' => $component->get_search()));
-        $output .= html_writer::empty_tag('input', array('type' => 'submit', 'name' => 'searchcourses', 'value' => get_string('search')));
+        $attrs = array(
+            'type' => 'text',
+            'name' => restore_course_search::$VAR_SEARCH,
+            'value' => $component->get_search(),
+            'class' => 'form-control');
+        $output .= html_writer::empty_tag('input', $attrs);
+        $attrs = array(
+            'type' => 'submit',
+            'name' => 'searchcourses',
+            'value' => get_string('search'),
+            'class' => 'btn btn-secondary'
+        );
+        $output .= html_writer::empty_tag('input', $attrs);
         $output .= html_writer::end_tag('div');
 
         $output .= html_writer::end_tag('div');
@@ -726,7 +767,7 @@ class core_backup_renderer extends plugin_renderer_base {
     public function render_restore_category_search(restore_category_search $component) {
         $url = $component->get_url();
 
-        $output = html_writer::start_tag('div', array('class' => 'restore-course-search'));
+        $output = html_writer::start_tag('div', array('class' => 'restore-course-search form-inline m-b-1'));
         $output .= html_writer::start_tag('div', array('class' => 'rcs-results'));
 
         $table = new html_table();
@@ -767,8 +808,20 @@ class core_backup_renderer extends plugin_renderer_base {
         $output .= html_writer::end_tag('div');
 
         $output .= html_writer::start_tag('div', array('class' => 'rcs-search'));
-        $output .= html_writer::empty_tag('input', array('type' => 'text', 'name' => restore_category_search::$VAR_SEARCH, 'value' => $component->get_search()));
-        $output .= html_writer::empty_tag('input', array('type' => 'submit', 'name' => 'searchcourses', 'value' => get_string('search')));
+        $attrs = array(
+            'type' => 'text',
+            'name' => restore_category_search::$VAR_SEARCH,
+            'value' => $component->get_search(),
+            'class' => 'form-control'
+        );
+        $output .= html_writer::empty_tag('input', $attrs);
+        $attrs = array(
+            'type' => 'submit',
+            'name' => 'searchcourses',
+            'value' => get_string('search'),
+            'class' => 'btn btn-secondary'
+        );
+        $output .= html_writer::empty_tag('input', $attrs);
         $output .= html_writer::end_tag('div');
 
         $output .= html_writer::end_tag('div');
index ce0440e..ef8aada 100644 (file)
@@ -216,7 +216,7 @@ class behat_backup extends behat_base {
 
         // Selecting the specified course (we can not call behat_forms::select_radio here as is in another behat subcontext).
         $radionode = $this->find('xpath', "//div[contains(concat(' ', normalize-space(@class), ' '), ' bcs-existing-course ')]" .
-            "/descendant::div[@class='restore-course-search']" .
+            "/descendant::div[contains(concat(' ', normalize-space(@class), ' '), ' restore-course-search ')]" .
             "/descendant::tr[contains(., $existingcourse)]" .
             "/descendant::input[@type='radio']");
         $radionode->click();
index 2526bed..e51e19f 100644 (file)
@@ -30,7 +30,7 @@ $string['prefix_help'] = 'This prefix is used for all key names on the Redis ser
 * If you only have one Moodle instance using this server, you can leave this value default.
 * Due to key length restrictions, a maximum of 5 characters is permitted.';
 $string['prefixinvalid'] = 'Invalid prefix. You can only use a-z A-Z 0-9-_.';
-$string['test_server'] = 'Test Server';
+$string['test_server'] = 'Test server';
 $string['test_server_desc'] = 'Redis server to use for testing.';
 $string['server'] = 'Server';
 $string['server_help'] = 'This sets the hostname or IP address of the Redis server to use.';
\ No newline at end of file
index 3f7df7f..bb8421f 100644 (file)
@@ -7,6 +7,6 @@
     "require-dev": {
         "phpunit/phpunit": "5.5.*",
         "phpunit/dbUnit": "1.4.*",
-        "moodlehq/behat-extension": "3.32.3"
+        "moodlehq/behat-extension": "3.32.4"
     }
 }
index 1e231a8..ae338bf 100644 (file)
@@ -4,8 +4,8 @@
         "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
         "This file is @generated automatically"
     ],
-    "hash": "ec5f1e9d8b8134cf6a4490ba5dfe0082",
-    "content-hash": "583f9a915721de799118a396dc81f177",
+    "hash": "6042e8e9600da0881e3f63b2e79ad742",
+    "content-hash": "59c508468d7f10d1a16b4d24228e7eab",
     "packages": [],
     "packages-dev": [
         {
         },
         {
             "name": "moodlehq/behat-extension",
-            "version": "v3.32.3",
+            "version": "v3.32.4",
             "source": {
                 "type": "git",
                 "url": "https://github.com/moodlehq/moodle-behat-extension.git",
-                "reference": "8296916088e62f7cdf67b34abeebd419b301caff"
+                "reference": "d363b92f62770acdd8cd878810777f3a61eada4d"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/moodlehq/moodle-behat-extension/zipball/8296916088e62f7cdf67b34abeebd419b301caff",
-                "reference": "8296916088e62f7cdf67b34abeebd419b301caff",
+                "url": "https://api.github.com/repos/moodlehq/moodle-behat-extension/zipball/d363b92f62770acdd8cd878810777f3a61eada4d",
+                "reference": "d363b92f62770acdd8cd878810777f3a61eada4d",
                 "shasum": ""
             },
             "require": {
                 "Behat",
                 "moodle"
             ],
-            "time": "2016-10-03 03:33:07"
+            "time": "2016-11-10 23:36:48"
         },
         {
             "name": "myclabs/deep-copy",
index b90a8f8..5824254 100644 (file)
@@ -348,3 +348,20 @@ Feature: Test category management actions
       | Category ID number | CAT1 |
     When I press "Create category"
     Then I should see "ID number is already used for another category"
+
+  Scenario: Test that is possible to remove an idnumber from a course category
+    Given the following "categories" exist:
+      | name | category | idnumber |
+      | Cat 1 | 0 | CAT1 |
+      | Cat 2 | 0 ||
+    And I log in as "admin"
+    And I go to the courses management page
+    And I should see "CAT1" in the "#category-listing" "css_element"
+    When I click on "edit" action for "Cat 1" in management category listing
+    And I set the following fields to these values:
+      | Category name | Category 1 (edited) |
+      | Category ID number ||
+    And I press "Save changes"
+    # Redirect
+    Then I should see "Category 1 (edited)" in the "#category-listing" "css_element"
+    And I should not see "CAT1" in the "#course-listing" "css_element"
index 20076cc..34e12c5 100644 (file)
@@ -37,12 +37,7 @@ Structure of the user enroller panel
 
 .user-enroller-panel {
     width: 400px;
-    background-color: #666;
     position: absolute;
-    top: 10%;
-    left: 10%;
-    border: 1px solid #666;
-    border-width: 0 5px 5px 0;
 }
 
 .user-enroller-panel.hidden {
@@ -50,57 +45,22 @@ Structure of the user enroller panel
 }
 
 .user-enroller-panel .uep-wrap {
-    margin-top: -5px;
-    margin-left: -5px;
-    background-color: #fff;
-    border: 1px solid #999;
     height: inherit;
 }
 
-.user-enroller-panel .uep-header {
-    background-color: #eee;
-    padding: 1px;
-}
-
-.user-enroller-panel .uep-header h2 {
-    margin: 3px 1em 0.5em 1em;
-    font-size: 1em;
+.user-enroller-panel .uep-search-results .user .count {
+    display: none;
 }
 
-.user-enroller-panel .uep-header .close {
-    width: 25px;
-    height: 15px;
-    position: absolute;
-    top: 2px;
-    right: 1em;
-    cursor: pointer;
-    background: url("sprite.png") no-repeat scroll 0 0 transparent;
+.user-enroller-panel .uep-search-results .cohort .count {
+    display: none;
 }
 
 .user-enroller-panel .uep-content {
-    text-align: center;
     position: relative;
     width: 100%;
-    border-top: 1px solid #999;
-    border-bottom: 1px solid #999;
-}
-
-.user-enroller-panel .uep-content .uep-controls {
-    margin: 0;
-    padding: 3px;
-    background-color: #ddd;
-    text-align: left;
-    border-bottom: 1px solid #bbb;
-}
-
-.user-enroller-panel .uep-content .uep-controls label {
-    display: inline;
-    padding-right: 5px;
-}
-
-.user-enroller-panel .uep-content .uep-controls .uep-enrolment-option input {
-    vertical-align: middle;
-    margin-left: 1em;
+    box-sizing: border-box;
+    max-height: 1000px;
 }
 
 .user-enroller-panel .uep-ajax-content {
@@ -108,39 +68,6 @@ Structure of the user enroller panel
     overflow: auto;
 }
 
-.user-enroller-panel .uep-search-results .totalusers {
-    background-color: #eee;
-    padding: 5px;
-    border-bottom: 1px solid #bbb;
-    font-size: 7pt;
-    font-weight: bold;
-}
-
-.user-enroller-panel .uep-search-results .user {
-    width: 100%;
-    text-align: left;
-    font-size: 9pt;
-    border-bottom: 1px solid #ddd;
-    border-top: 1px solid #eee;
-}
-
-.user-enroller-panel .uep-search-results .user.odd {
-    border-bottom: 1px solid #ddd;
-    border-top: 1px solid #eee;
-    background-color: #f9f9f9;
-}
-
-.user-enroller-panel .uep-search-results .user .count {
-    width: 20px;
-    float: left;
-    font-size: 7pt;
-    line-height: 41px;
-    border-right: 1px solid #ddd;
-    background-color: #eee;
-    text-align: right;
-    padding: 2px;
-}
-
 .user-enroller-panel .uep-search-results .user .picture {
     width: 45px;
     float: left;
@@ -148,14 +75,13 @@ Structure of the user enroller panel
 }
 
 .user-enroller-panel .uep-search-results .user .details {
-    width: 250px;
+    width: 180px;
     float: left;
     margin: 3px;
 }
 
 .user-enroller-panel .uep-search-results .user .options {
     padding-right: 7px;
-    font-size: 8pt;
     margin: 3px;
 }
 
@@ -165,56 +91,21 @@ Structure of the user enroller panel
     cursor: pointer;
 }
 
-.user-enroller-panel .uep-search-results .user.enrolled .count {
-    width: 40px;
-    color: #eee;
-}
-
-.user-enroller-panel .uep-search-results .uep-more-results {
-    background-color: #eee;
-    padding: 5px;
-    border-top: 1px solid #bbb;
-}
-
-.user-enroller-panel .uep-search-results .totalcohorts {
-    background-color: #eee;
-    padding: 5px;
-    border-bottom: 1px solid #bbb;
-    font-size: 7pt;
-    font-weight: bold;
-}
-
 .user-enroller-panel .uep-search-results .cohort {
     width: 100%;
     text-align: left;
-    font-size: 9pt;
-    border-bottom: 1px solid #ddd;
-    border-top: 1px solid #eee;
 }
 
 .user-enroller-panel .uep-search-results .cohort .count {
-    width: 20px;
-    float: left;
-    font-size: 7pt;
-    line-height: 27px;
-    border-right: 1px solid #ddd;
-    background-color: #eee;
-    text-align: right;
-    padding: 2px;
+    display: none;
 }
 
 .user-enroller-panel .uep-search-results .cohort .details {
-    width: 250px;
+    width: 180px;
     float: left;
     margin: 5px;
 }
 
-.user-enroller-panel .uep-search-results .cohort .options {
-    padding-right: 7px;
-    font-size: 8pt;
-    margin: 3px;
-}
-
 .user-enroller-panel .uep-search-results .cohort .options .enrol {
     margin: 3px;
     float: right;
@@ -223,16 +114,15 @@ Structure of the user enroller panel
 
 .user-enroller-panel .uep-search-results .cohort.enrolled .count {
     width: 40px;
-    color: #eee;
 }
 
 .user-enroller-panel .uep-loading-lightbox {
     position: absolute;
     width: 100%;
     height: 100%;
+    background-color: #ddd;
     top: 0;
     left: 0;
-    background-color: #fff;
     min-width: 50px;
     min-height: 50px;
 }
@@ -245,12 +135,11 @@ Structure of the user enroller panel
     margin: auto;
     vertical-align: middle;
     margin-top: 125px;
+    display: block;
 }
 
 .user-enroller-panel .uep-footer {
-    padding: 3px;
-    background-color: #ddd;
-    text-align: center;
+    text-align: right;
 }
 
 .user-enroller-panel .uep-search {
@@ -263,7 +152,6 @@ Structure of the user enroller panel
 
 .user-enroller-panel .uep-search input {
     width: 50%;
-    margin: 0 0 10px 5px;
 }
 
 .user-enroller-panel .uep-search input.uep-search-btn {
@@ -285,8 +173,8 @@ Structure of the user enroller panel
 }
 
 .user-enroller-panel .collapsiblearea {
-    border: 1px solid #bbb;
-    background-color: #f6f6f6;
+    border: 1px solid #ddd;
+    padding: 0.5rem;
 }
 
 .user-enroller-panel .collapsiblearea.hidden {
index 012cafb..8f235cc 100644 (file)
@@ -32,7 +32,7 @@ YUI.add('moodle-enrol_manual-quickenrolment', function(Y) {
     /** CSS classes for nodes in structure **/
     var CSS = {
         PANEL : 'user-enroller-panel',
-        WRAP : 'uep-wrap',
+        WRAP : 'uep-wrap modal-dialog',
         HEADER : 'uep-header',
         CONTENT : 'uep-content',
         AJAXCONTENT : 'uep-ajax-content',
@@ -90,33 +90,33 @@ YUI.add('moodle-enrol_manual-quickenrolment', function(Y) {
             if (this.get(UEP.DISABLEGRADEHISTORY) != true) {
                 recovergrades = create('<div class="'+CSS.ENROLMENTOPTION+' '+CSS.RECOVERGRADES+'"></div>')
                     .append(create('<label class="'+CSS.RECOVERGRADESTITLE+'" for="'+CSS.RECOVERGRADES+'">'+M.util.get_string('recovergrades', 'enrol')+'</label>'))
-                    .append(create('<input type="checkbox" id="'+CSS.RECOVERGRADES+'" name="'+CSS.RECOVERGRADES+'"'+ this.get(UEP.RECOVERGRADESDEFAULT) +' />'))
+                    .append(create('<input type="checkbox" class="m-x-1" id="'+CSS.RECOVERGRADES+'" name="'+CSS.RECOVERGRADES+'"'+ this.get(UEP.RECOVERGRADESDEFAULT) +' />'))
             }
 
             this.set(UEP.BASE, create('<div class="'+CSS.PANEL+' '+CSS.HIDDEN+'"></div>')
-                .append(create('<div class="'+CSS.WRAP+'"></div>')
-                    .append(create('<div class="'+CSS.HEADER+' header"></div>')
-                        .append(create('<div class="'+CSS.CLOSE+'"></div>'))
-                        .append(create('<h2>'+M.util.get_string('enrolusers', 'enrol')+'</h2>')))
-                    .append(create('<div class="'+CSS.CONTENT+'"></div>')
-                        .append(create('<div class="'+CSS.SEARCHCONTROLS+'"></div>')
+                .append(create('<div class="'+CSS.WRAP+' modal show modal-dialog modal-content"></div>')
+                    .append(create('<div class="'+CSS.HEADER+' header modal-header"></div>')
+                        .append(create('<div class="'+CSS.CLOSE+'">&times;</div>'))
+                        .append(create('<h2 class="modal-title">'+M.util.get_string('enrolusers', 'enrol')+'</h2>')))
+                    .append(create('<div class="'+CSS.CONTENT+' modal-body"></div>')
+                        .append(create('<div class="'+CSS.SEARCHCONTROLS+' form-inline"></div>')
                             .append(create('<div class="'+CSS.ENROLMENTOPTION+' '+CSS.ROLE+'"><label for="id_enrol_manual_assignable_roles">'+M.util.get_string('assignroles', 'role')+'</label></div>')
-                                    .append(create('<select id="id_enrol_manual_assignable_roles"><option value="">'+M.util.get_string('none', 'enrol')+'</option></select>'))
+                                    .append(create('<select id="id_enrol_manual_assignable_roles" class="custom-select"><option value="">'+M.util.get_string('none', 'enrol')+'</option></select>'))
                             )
-                            .append(create('<div class="'+CSS.ENTITYSELECTOR+'"></div>'))
+                            .append(create('<div class="'+CSS.ENTITYSELECTOR+' m-y-1"></div>'))
                             .append(create('<div class="'+CSS.SEARCHOPTIONS+'"></div>')
                                 .append(create('<div class="'+CSS.COLLAPSIBLEHEADING+'"><img alt="" />'+M.util.get_string('enrolmentoptions', 'enrol')+'</div>'))
                                 .append(create('<div class="'+CSS.COLLAPSIBLEAREA+' '+CSS.HIDDEN+'"></div>')
                                     .append(recovergrades)
                                     .append(create('<div class="'+CSS.ENROLMENTOPTION+' '+CSS.STARTDATE+'">'+M.util.get_string('startingfrom', 'moodle')+'</div>')
-                                        .append(create('<select></select>')))
+                                        .append(create('<select class="custom-select"></select>')))
                                     .append(create('<div class="'+CSS.ENROLMENTOPTION+' '+CSS.DURATION+'">'+M.util.get_string('enrolperiod', 'enrol')+'</div>')
-                                        .append(create('<select><option value="0" selected="selected">'+M.util.get_string('unlimitedduration', 'enrol')+'</option></select>')))
+                                        .append(create('<select class="custom-select"><option value="0" selected="selected">'+M.util.get_string('unlimitedduration', 'enrol')+'</option></select>')))
                                 )
                             )
                             .append(create('<div class="'+CSS.SEARCH+'"><label for="enrolusersearch" class="accesshide">'+M.util.get_string('usersearch', 'enrol')+'</label></div>')
-                                .append(create('<input type="text" id="enrolusersearch" value="" />'))
-                                .append(create('<input type="button" id="searchbtn" class="'+CSS.SEARCHBTN+'" value="'+M.util.get_string('usersearch', 'enrol')+'" />'))
+                                .append(create('<input class="form-control" type="text" id="enrolusersearch" value="" />'))
+                                .append(create('<input type="button" id="searchbtn" class="'+CSS.SEARCHBTN+' btn btn-secondary m-l-1" value="'+M.util.get_string('usersearch', 'enrol')+'" />'))
                             )
                         )
                         .append(create('<div class="'+CSS.AJAXCONTENT+'"></div>'))
@@ -124,9 +124,9 @@ YUI.add('moodle-enrol_manual-quickenrolment', function(Y) {
                             .append(create('<img alt="loading" class="'+CSS.LOADINGICON+'" />')
                                 .setAttribute('src', M.util.image_url('i/loading', 'moodle')))
                             .setStyle('opacity', 0.5)))
-                    .append(create('<div class="'+CSS.FOOTER+'"></div>')
+                    .append(create('<div class="'+CSS.FOOTER+' modal-footer"></div>')
                         .append(create('<div class="'+CSS.CLOSEBTN+'"></div>')
-                            .append(create('<input type="button" value="'+M.util.get_string('finishenrollingusers', 'enrol')+'" />'))
+                            .append(create('<input type="button" class="btn btn-primary" value="'+M.util.get_string('finishenrollingusers', 'enrol')+'" />'))
                         )
                     )
                 )
@@ -151,9 +151,9 @@ YUI.add('moodle-enrol_manual-quickenrolment', function(Y) {
 
             if (this.get(UEP.COHORTSAVAILABLE)) {
                 this.get(UEP.BASE).one('.'+CSS.ENTITYSELECTOR)
-                    .append(create('<input type="radio" id="id_enrol_manual_entity_users" name="enrol_manual_entity" value="users" checked="checked"/>'))
+                    .append(create('<input type="radio" id="id_enrol_manual_entity_users" name="enrol_manual_entity" value="users" class="m-r-1" checked="checked"/>'))
                     .append(create('<label for="id_enrol_manual_entity_users">'+ M.util.get_string('browseusers', 'enrol_manual')+'</label>'))
-                    .append(create('<input type="radio" id="id_enrol_manual_entity_cohorts" name="enrol_manual_entity" value="cohorts"/>'))
+                    .append(create('<input type="radio" id="id_enrol_manual_entity_cohorts" name="enrol_manual_entity" class="m-x-1" value="cohorts"/>'))
                     .append(create('<label for="id_enrol_manual_entity_cohorts">'+M.util.get_string('browsecohorts', 'enrol_manual')+'</label>'));
                 this.get(UEP.BASE).one('#id_enrol_manual_entity_cohorts').on('change', this.search, this);
                 this.get(UEP.BASE).one('#id_enrol_manual_entity_users').on('change', this.search, this);
@@ -397,7 +397,7 @@ YUI.add('moodle-enrol_manual-quickenrolment', function(Y) {
             }
             var users;
             if (!args.append) {
-                users = create('<div class="'+CSS.USERS+'"></div>');
+                users = create('<div class="'+CSS.USERS+' list-group"></div>');
             } else {
                 users = this.get(UEP.BASE).one('.'+CSS.SEARCHRESULTS+' .'+CSS.USERS);
             }
@@ -405,7 +405,7 @@ YUI.add('moodle-enrol_manual-quickenrolment', function(Y) {
             for (var i in result.response.users) {
                 count++;
                 var user = result.response.users[i];
-                users.append(create('<div class="'+CSS.USER+' clearfix" rel="'+user.id+'"></div>')
+                users.append(create('<div class="'+CSS.USER+' clearfix list-group-item list-group-item-action" rel="'+user.id+'"></div>')
                     .addClass((count%2)?CSS.ODD:CSS.EVEN)
                     .append(create('<div class="'+CSS.COUNT+'">'+count+'</div>'))
                     .append(create('<div class="'+CSS.PICTURE+'"></div>')
@@ -414,7 +414,7 @@ YUI.add('moodle-enrol_manual-quickenrolment', function(Y) {
                         .append(create('<div class="'+CSS.FULLNAME+'">'+user.fullname+'</div>'))
                         .append(create('<div class="'+CSS.EXTRAFIELDS+'">'+user.extrafields+'</div>')))
                     .append(create('<div class="'+CSS.OPTIONS+'"></div>')
-                        .append(create('<input type="button" class="'+CSS.ENROL+'" value="'+M.util.get_string('enrol', 'enrol')+'" />')))
+                        .append(create('<input type="button" class="'+CSS.ENROL+' btn btn-secondary" value="'+M.util.get_string('enrol', 'enrol')+'" />')))
                 );
             }
             this.set(UEP.USERCOUNT, count);
@@ -464,7 +464,7 @@ YUI.add('moodle-enrol_manual-quickenrolment', function(Y) {
                     .append(create('<div class="'+CSS.DETAILS+'"></div>')
                         .append(create('<div class="'+CSS.COHORTNAME+'">'+cohort.name+'</div>')))
                     .append(create('<div class="'+CSS.OPTIONS+'"></div>')
-                        .append(create('<input type="button" class="' + CSS.ENROL + '" value="' + M.util.get_string('enrolxusers', 'enrol', cohort.cnt) + '" />')))
+                        .append(create('<input type="button" class="' + CSS.ENROL + ' btn btn-secondary" value="' + M.util.get_string('enrolxusers', 'enrol', cohort.cnt) + '" />')))
                 );
             }
             this.set(UEP.COHORTCOUNT, count);
index 583955a..5f4e5e1 100644 (file)
@@ -98,14 +98,7 @@ $string['self:manage'] = 'Manage enrolled users';
 $string['self:unenrol'] = 'Unenrol users from course';
 $string['self:unenrolself'] = 'Unenrol self from the course';
 $string['sendcoursewelcomemessage'] = 'Send course welcome message';
-$string['sendcoursewelcomemessage_help'] = 'When a user self enrols in the course, they may be sent an welcome message email.<br />
-If sent from the course contact (by default the teacher), and more than one user has this role, the email is sent from the first user to be assigned the role.
-
-The welcome email can be sent as:
-
-* First user with key holder capability assigned.
-* First user with assigned as course contact.
-* No reply address contact.';
+$string['sendcoursewelcomemessage_help'] = 'When a user self enrols in the course, they may be sent a welcome message email. If sent from the course contact (by default the teacher), and more than one user has this role, the email is sent from the first user to be assigned the role.';
 $string['showhint'] = 'Show hint';
 $string['showhint_desc'] = 'Show first letter of the guest access key.';
 $string['status'] = 'Allow existing enrolments';
index a75d61a..750ecdb 100644 (file)
@@ -358,12 +358,18 @@ YUI.add('moodle-enrol-rolemanager', function(Y) {
         submitevent : null,
         initializer : function() {
             var i, m = this.get(MANIPULATOR);
-            var element = Y.Node.create('<div class="enrolpanel roleassign"><div class="container"><div class="header"><h2>'+M.util.get_string('assignroles', 'role')+'</h2><div class="close"></div></div><div class="content"></div></div></div>');
+            var element = Y.Node.create('<div class="popover popover-bottom"><div class="arrow"></div>' +
+                                        '<div class="header popover-title">' +
+                                        '<div role="button" class="close" aria-label="Close">' +
+                                        '<span aria-hidden="true">&times;</span></div>' +
+                                        '<h3>'+M.util.get_string('assignroles', 'role')+'</h3>' +
+                                        '</div><div class="content popover-content form-inline form-group"></div></div>');
             var content = element.one('.content');
             var roles = m.get(ASSIGNABLEROLES);
             for (i in roles) {
                 var buttonid = 'add_assignable_role_' + roles[i].id;
-                var buttonhtml = '<input type="button" value="' + roles[i].name + '" id="' + buttonid + '" />';
+                var buttonhtml = '<input type="button" class="btn btn-secondary m-r-1" value="' +
+                                 roles[i].name + '" id="' + buttonid + '" />';
                 var button = Y.Node.create(buttonhtml);
                 button.on('click', this.submit, this, roles[i].id);
                 content.append(button);
@@ -392,7 +398,7 @@ YUI.add('moodle-enrol-rolemanager', function(Y) {
             } else {
                 this.get('elementNode').setStyle('left', x).setStyle('top', y);
             }
-            this.get('elementNode').addClass('visible');
+            this.get('elementNode').setStyle('display', 'block');
             this.escCloseEvent = Y.on('key', this.hide, document.body, 'down:27', this);
             this.displayed = true;
         },
@@ -409,7 +415,7 @@ YUI.add('moodle-enrol-rolemanager', function(Y) {
             }
             this.roles = [];
             this.user = null;
-            this.get('elementNode').removeClass('visible');
+            this.get('elementNode').setStyle('display', 'none');
             if (this.submitevent) {
                 this.submitevent.detach();
                 this.submitevent = null;
index bb83a2b..f1273b9 100644 (file)
  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
 
+defined('MOODLE_INTERNAL') || die();
+
 $string['fallbackaudio'] = 'Audio link';
 $string['fallbackvideo'] = 'Video link';
 $string['filtername'] = 'Multimedia plugins';
-$string['linktomedia'] = 'Multimedia plugins filter engages the existing media players to convert links to media files into embedded objects. '.
-    ' It can also enhance &lt;video&gt; and &lt;audio&gt; tags with selected video and audio players.<br><a href="{$a}">Manage media players</a>';
+$string['linktomedia'] = 'The multimedia plugins filter finds a link in text that points to a multimedia resource and replaces the link with an appropriate multimedia player code which can play the resource. It also replaces &lt;video&gt; and &lt;audio&gt; tags with selected video and audio players.
+
+<a href="{$a}">Manage media players</a>';
index cd95381..5f5b2d4 100644 (file)
@@ -35,9 +35,13 @@ $string['cliansweryes'] = 'j';
 $string['cliincorrectvalueerror'] = 'Fejl, "{$a->value}" er en forkert værdi for "{$a->option}"';
 $string['cliincorrectvalueretry'] = 'Forkert værdi, prøv igen!';
 $string['clitypevalue'] = 'tast værdi';
-$string['clitypevaluedefault'] = 'tast værdi eller brug standardværdien ved at trykke Enter';
-$string['cliunknowoption'] = 'Ukendte options: {$a} Brug venligst --help option.';
+$string['clitypevaluedefault'] = 'tast værdi eller brug standardværdien ({$a}) ved at trykke Enter';
+$string['cliunknowoption'] = 'Ukendte options:
+
+{$a}
+
+Brug venligst --help option.';
 $string['cliyesnoprompt'] = 'j (betyder ja) og n (betyder nej)';
 $string['environmentrequireinstall'] = 'skal være installeret og aktiveret';
 $string['environmentrequireversion'] = 'minimumskravet er version {$a->needed} og du kører {$a->current}';
-$string['upgradekeyset'] = 'Opgraderingsnøgle (efterlad blank for ikke at angive denne)';
+$string['upgradekeyset'] = 'Opgraderingsnøgle (efterlad tom for ikke at angive denne)';
index 4b0ac1c..a8d4ed1 100644 (file)
@@ -41,10 +41,10 @@ $string['cannotsavezipfile'] = 'Kan ikke gemme zip-fil';
 $string['cannotunzipfile'] = 'Kan ikke pakke filen ud';
 $string['componentisuptodate'] = 'Komponenten er ajour';
 $string['dmlexceptiononinstall'] = '<p>En databasefejl er opstået [{$a->errorcode}].<br />{$a->debuginfo}</p>';
-$string['downloadedfilecheckfailed'] = 'Downloadet fil-tjek fejlede';
+$string['downloadedfilecheckfailed'] = 'Downloadet fil-tjek mislykkedes';
 $string['invalidmd5'] = 'Tjekvariablen var forkert - prøv igen';
 $string['missingrequiredfield'] = 'Der mangler nogle obligatoriske felter';
-$string['remotedownloaderror'] = '<p>Download af komponent til din server fejlede. Verificer venligst proxy-indstillilngerne; udvidelsen PHP cURL  anbefales kraftigt. </p> <p>Du må downloade filen <a href="{$a->url}">{$a->url}</a> manuelt, kopiere den til "{$a->dest}" på din server og udpakke den der.</p>';
+$string['remotedownloaderror'] = '<p>Download af komponenten til din server mislykkedes. Verificer proxy-indstillilngerne, udvidelsen PHP cURL anbefales kraftigt. </p> <p>Du skal downloade filen <a href="{$a->url}">{$a->url}</a> manuelt, kopiere den til "{$a->dest}" på din server og udpakke den der.</p>';
 $string['wrongdestpath'] = 'Forkert destinationssti';
 $string['wrongsourcebase'] = 'Forkert kilde-URL';
 $string['wrongzipfilename'] = 'Forkert zip-filnavn';
index 6c433be..f988765 100644 (file)
@@ -35,7 +35,7 @@ $string['availablelangs'] = 'Tilgængelige sprogpakker';
 $string['chooselanguagehead'] = 'Vælg et sprog';
 $string['chooselanguagesub'] = 'Vælg et sprog til brug under installationen. Dette sprog vil også blive brugt som standardsprog på webstedet, men det kan altid ændres til et andet sprog.';
 $string['clialreadyconfigured'] = 'Konfigurationsfilen config.php eksisterer allerede. Benyt venigst admin/cli/install_database.php til at installere Moodle for dette site.';
-$string['clialreadyinstalled'] = 'Filen config.php eksisterer allerede, brug venligst admin/cli/upgrade.php hvis du ønsker at opgradere dette websted.';
+$string['clialreadyinstalled'] = 'Filen config.php eksisterer allerede, brug venligst admin/cli/install_database.php hvis du ønsker at opgradere dette websted.';
 $string['cliinstallheader'] = 'Moodle {$a} kommandolinje-installationsprogram';
 $string['databasehost'] = 'Databasevært';
 $string['databasename'] = 'Databasenavn';
index accbbec..354f075 100644 (file)
@@ -396,7 +396,7 @@ $string['ctyperequired'] = 'The ctype PHP extension is now required by Moodle, i
 $string['curlsecurityallowedport'] = 'cURL allowed ports list';
 $string['curlsecurityallowedportsyntax'] = 'Put every entry on one line. Valid entries are integer numbers only.';
 $string['curlsecurityblockedhosts'] = 'cURL blocked hosts list';
-$string['curlsecurityblockedhostssyntax'] = 'Put each entry on a new line. Valid entries are either full IPv4 or IPv6 addresses (such as <b>192.168.10.1, 0:0:0:0:0:0:0:1, ::1, fe80::</b>) which match a single host; or CIDR notation (such as <b>231.54.211.0/20 or fe80::/64</b>); or a range of IP addresses (such as <b>231.3.56.10-20 or fe80::1111-bbbb</b>) where the range applies to the last group of the address; or domain names (such as <b>localhost or example.com</b>); or wildcard domain names (such as <b>*.example.com or *.sub.example.com</b>). Blank lines are not allowed.';
+$string['curlsecurityblockedhostssyntax'] = 'Put each entry on a new line. Valid entries are either full IPv4 or IPv6 addresses (such as 192.168.10.1, 0:0:0:0:0:0:0:1, ::1, fe80::) which match a single host; or CIDR notation (such as 231.54.211.0/20 or fe80::/64); or a range of IP addresses (such as 231.3.56.10-20 or fe80::1111-bbbb) where the range applies to the last group of the address; or domain names (such as localhost or example.com); or wildcard domain names (such as *.example.com or *.sub.example.com). Blank lines are not allowed.';
 $string['curlsecurityurlblocked'] = 'The URL is blocked.';
 $string['curlcache'] = 'cURL cache TTL';
 $string['curlrequired'] = 'The cURL PHP extension is now required by Moodle, in order to communicate with Moodle repositories.';
@@ -635,7 +635,7 @@ $string['legacyfilesaddallowed'] = 'Allow adding to legacy course files';
 $string['legacyfilesaddallowed_help'] = 'If a course has legacy course files, allow new files and folders to be added to it.';
 $string['legacyfilesinnewcourses'] = 'Legacy course files in new courses';
 $string['legacyfilesinnewcourses_help'] = 'By default, legacy course files areas are available in upgraded courses only. Please note that some features such as activity backup and restore are not compatible with this setting.';
-$string['libcurlwarning'] = 'Libcurl with CURLOPT_PROTOCOL support has not been detected. It is recommended to have an up to date libcurl installation for security reasons.';
+$string['libcurlwarning'] = 'It has been detected that libcurl doesn\'t have CURLOPT_PROTOCOL support. An up-to-date libcurl installation is recommended for security reasons.';
 $string['licensesettings'] = 'Licence settings';
 $string['linkadmincategories'] = 'Link admin categories';
 $string['linkadmincategories_help'] = 'If enabled admin setting categories will be displayed as links in the navigation and will lead to the admin category pages.';
index 68ca4b3..12593ed 100644 (file)
@@ -40,3 +40,22 @@ legacyheading,core_media
 legacyheading_desc,core_media
 mobile,core_admin
 for,core_calendar
+context,core_message
+discussion,core_message
+emptysearchstring,core_message
+formorethan,core_message
+keywords,core_message
+messagehistory,core_message
+newsearch,core_message
+nosearchresults,core_message
+onlymycourses,core_message
+pagerefreshes,core_message
+page-message-x,core_message
+recent,core_message
+savemysettings,core_message
+search,core_message
+settingssaved,core_message
+strftimedaydatetime,core_message
+timenosee,core_message
+timesent,core_message
+userssearchresults,core_message
index be6847f..134c921 100644 (file)
 
 $string['audioextensions'] = 'Audio: {$a}';
 $string['defaultwidth'] = 'Default width';
-$string['defaultwidthdesc'] = 'Default width of video or other embedded object if no width is specified and player is not able to pick the real video width';
+$string['defaultwidthdesc'] = 'Media player width if a width is not specified and the actual media file width cannot be determined by the player.';
 $string['defaultheight'] = 'Default height';
-$string['defaultheightdesc'] = 'Default height of video or other embedded object if no height is specified and player is not able to pick the real video height';
+$string['defaultheightdesc'] = 'Media player height if a height is not specified and the actual media file height cannot be determined by the player.';
 $string['extensions'] = 'Extensions: {$a}';
 $string['managemediaplayers'] = 'Manage media players';
 $string['mediaformats'] = 'Available players';
-$string['mediaformats_desc'] = 'When players are enabled in these settings, files can be embedded using the media filter (if enabled) or using a File or URL resources with the Embed option. When not enabled, these formats are not embedded and users can manually download or follow links to these resources.
+$string['mediaformats_desc'] = 'When players are enabled, media files can be embedded using the multimedia plugins filter (if enabled) or using a file or URL resource. When not enabled, these formats are not embedded and users can download or follow links to these resources.
 
 Where two players support the same format, enabling both increases compatibility across different devices such as mobile phones. It is possible to increase compatibility further by providing multiple files in different formats for a single audio or video clip.';
 $string['supports'] = 'Supports';
index a4eeef8..404b70a 100644 (file)
  */
 
 $string['addcontact'] = 'Add contact';
-$string['addsomecontacts'] = 'To send a message to someone, or to add a shortcut for them on this page, use the <a href="{$a}">search tab</a> above.';
-$string['addsomecontactsincoming'] = 'These messages are from people who are not in your contact list. To add them to your contacts, click the "Add contact" icon next to their name.';
 $string['addtoyourcontacts'] = 'Add to your contacts';
 $string['ago'] = '{$a} ago';
-$string['ajax_gui'] = 'Ajax chat room';
-$string['allmine'] = 'All messages to me or from me';
-$string['allstudents'] = 'All messages between students in course';
 $string['allusers'] = 'All messages from all users';
-$string['alwayssend'] = 'Always send me';
 $string['backupmessageshelp'] = 'If enabled, then instant messages will be included in SITE automated backups';
-$string['beepnewmessage'] = 'Beep when popup notification is displayed';
 $string['blockcontact'] = 'Block contact';
-$string['blockedmessages'] = '{$a} message(s) to/from blocked users';
-$string['blockedusers'] = 'Blocked users ({$a})';
 $string['blocknoncontacts'] = 'Prevent non-contacts from messaging me';
-$string['collapsenotification'] = 'Collapse notification';
 $string['contactblocked'] = 'Contact blocked';
-$string['contactlistempty'] = 'Contact list empty';
 $string['contacts'] = 'Contacts';
-$string['context'] = 'context';
 $string['defaultmessageoutputs'] = 'Default message outputs';
 $string['defaults'] = 'Defaults';
 $string['deleteallconfirm'] = "Are you sure you would like to delete this entire conversation?";
-$string['deletemessage'] = 'Delete message';
-$string['deletemessageconfirmation'] = 'Are you sure you want to delete this message? It will only be deleted from your messaging history and will still be viewable by the user who sent or received the message.';
 $string['deleteselectedmessages'] = 'Delete selected messages';
-$string['deletemessagesdays'] = 'Number of days before old messages are automatically deleted';
 $string['disableall'] = 'Disable notifications';
 $string['disabled'] = 'Messaging is disabled on this site';
 $string['disallowed'] = 'Disallowed';
-$string['discussion'] = 'Discussion';
-$string['emailmessages'] = 'Email messages when I am offline';
 $string['emailtagline'] = 'This is a copy of a message sent to you at "{$a->sitename}". Go to {$a->url} to reply.';
-$string['emptysearchstring'] = 'You must search for something';
 $string['enabled'] = 'Enabled';
 $string['errorcallingprocessor'] = 'Error calling defined output';
 $string['errortranslatingdefault'] = 'Error translating default setting provided by plugin, using system defaults instead.';
@@ -67,93 +49,53 @@ $string['eventmessagecontactunblocked'] = 'Message contact unblocked';
 $string['eventmessagedeleted'] = 'Message deleted';
 $string['eventmessageviewed'] = 'Message viewed';
 $string['eventmessagesent'] = 'Message sent';
-$string['expandnotification'] = 'Expand notification';
 $string['forced'] = 'Locked';
-$string['formorethan'] = 'For more than';
 $string['guestnoeditmessage'] = 'Guest user can not edit messaging options';
 $string['guestnoeditmessageother'] = 'Guest user can not edit other user messaging options';
-$string['gotomessages'] = 'Go to messages';
 $string['hidemessagewindow'] = 'Hide message window';
 $string['hidenotificationwindow'] = 'Hide notification window';
-$string['includeblockedusers'] = 'Include blocked users';
-$string['incomingcontacts'] = 'Incoming contacts ({$a})';
-$string['keywords'] = 'Keywords';
-$string['keywordssearchresults'] = 'Messages found: {$a}';
-$string['keywordssearchresultstoomany'] = 'More than {$a} messages found. Refine your search.';
-$string['loading'] = 'Loading ...';
 $string['loggedin'] = 'Online';
 $string['loggedin_help'] = 'Configure how you would like to receive notifications when you are logged into Moodle';
 $string['loggedindescription'] = 'When you are logged into Moodle';
 $string['loggedoff'] = 'Offline';
 $string['loggedoff_help'] = 'Configure how you would like to receive notifications when you are not logged into Moodle';
 $string['loggedoffdescription'] = 'When you are not logged into Moodle';
-$string['managecontacts'] = 'Manage my contacts';
 $string['managemessageoutputs'] = 'Manage message outputs';
 $string['messageoutputs'] = 'Message outputs';
 $string['messagepreferences'] = 'Message preferences';
-$string['mostrecent'] = 'Recent messages';
-$string['mostrecentconversations'] = 'Recent conversations';
-$string['mostrecentnotifications'] = 'Recent notifications';
-$string['mailsent'] = 'Your message was sent via email.';
-$string['maxmessages'] = 'Maximum number of messages to show in the discussion history';
 $string['message'] = 'Message';
-$string['messagehistory'] = 'Message history';
-$string['messagehistoryfull'] = 'All messages';
-$string['messagenavigation'] = 'Message navigation:';
 $string['messagepreferences'] = 'Message preferences';
-$string['messageprocessors'] = 'Message processors';
 $string['messages'] = 'Messages';
-$string['messaging'] = 'Messaging';
-$string['messagingblockednoncontact'] = '{$a} will not be able to reply as you have blocked non-contacts';
 $string['messagingdisabled'] = 'Messaging is disabled on this site, emails will be sent instead';
 $string['newonlymsg'] = 'Show only new';
-$string['newsearch'] = 'New search';
 $string['noframesjs'] = 'Use more accessible interface';
 $string['nocontacts'] = 'No contacts';
 $string['nomessages'] = 'No messages';
 $string['nomessagesfound'] = 'No messages were found';
 $string['noreply'] = 'Do not reply to this message';
-$string['nosearchresults'] = 'There were no results from your search';
 $string['noncontacts'] = 'Non-contacts';
 $string['nonotifications'] = 'You have no notifications';
-$string['nonewnotifications'] = 'You have no new notifications';
 $string['notificationwindow'] = 'Notification window';
 $string['notificationpreferences'] = 'Notification preferences';
 $string['notificationimage'] = 'Notification image';
 $string['notifications'] = 'Notifications';
 $string['off'] = 'Off';
 $string['offline'] = 'Offline';
-$string['offlinecontacts'] = 'Offline contacts ({$a})';
 $string['on'] = 'On';
 $string['online'] = 'Online';
-$string['onlinecontacts'] = 'Online contacts ({$a})';
-$string['onlyfromme'] = 'Only messages from me';
-$string['onlymycourses'] = 'Only in my courses';
-$string['onlytome'] = 'Only messages to me';
 $string['outputdisabled'] = 'Output disabled';
 $string['outputdoesnotexist'] = 'Message output does not exists';
 $string['outputenabled'] = 'Output enabled';
 $string['outputnotavailable'] = 'Not available';
 $string['outputnotconfigured'] = 'Not configured';
-$string['pagerefreshes'] = 'This page refreshes automatically every {$a} seconds';
 $string['permitted'] = 'Permitted';
-$string['page-message-x'] = 'Any message pages';
-$string['private_config'] = 'Popup message window';
 $string['processorsettings'] = 'Processor settings';
-$string['processortag'] = 'Destination';
-$string['providers_config'] = 'Configure notification methods for incoming messages';
-$string['providerstag'] = 'Source';
-$string['recent'] = 'Recent';
-$string['readmessages'] = '{$a} read messages';
 $string['removecontact'] = 'Remove contact';
 $string['removefromyourcontacts'] = 'Remove from your contacts';
 $string['requiresconfiguration'] = 'Requires configuration';
-$string['savemysettings'] = 'Save my settings';
-$string['search'] = 'Search';
 $string['searchforuser'] = 'Search for a user';
 $string['searchforuserorcourse'] = 'Search for a user or course';
 $string['searchmessages'] = 'Search messages';
-$string['searchcombined'] = 'Search people and messages';
 $string['selectmessagestodelete'] = 'Select messages to delete';
 $string['selectnotificationtoview'] = 'Select from the list of notifications on the side to view more details';
 $string['send'] = 'Send';
@@ -163,30 +105,18 @@ $string['sendmessage'] = 'Send message';
 $string['sendmessageto'] = 'Send message to {$a}';
 $string['sendmessagetopopup'] = 'Send message to {$a} - new window';
 $string['settings'] = 'Settings';
-$string['settingssaved'] = 'Your settings have been saved';
-$string['showmessagewindow'] = 'Popup window on new message';
 $string['showmessagewindownonew'] = 'Show message window with no new messages';
 $string['showmessagewindowwithcount'] = 'Show message window with {$a} new messages';
-$string['showallnotifications'] = 'Show all notifications';
-$string['shownewnotifications'] = 'Show new notifications';
 $string['shownotificationwindownonew'] = 'Show notification window with no new notifications';
 $string['shownotificationwindowwithcount'] = 'Show notification window with {$a} new notifications';
-$string['strftimedaydatetime'] = '%A, %d %B %Y, %I:%M %p';
-$string['thisconversation'] = 'this conversation';
-$string['timenosee'] = 'Minutes since I was last seen online';
-$string['timesent'] = 'Time sent';
 $string['togglenotificationmenu'] = 'Toggle notification menu';
 $string['togglemessagemenu'] = 'Toggle message menu';
 $string['touserdoesntexist'] = 'You can not send a message to a user id ({$a}) that doesn\'t exist';
-$string['unabletomessageuser'] = 'You are not permitted to send a message to that user';
 $string['unblockcontact'] = 'Unblock contact';
-$string['unreadmessages'] = 'Unread messages ({$a})';
 $string['unreadnotification'] = 'Unread notification: {$a}';
-$string['unreadnewmessages'] = 'New messages ({$a})';
 $string['unreadnewmessage'] = 'New message from {$a}';
 $string['userisblockingyou'] = 'This user has blocked you from sending messages to them';
 $string['userisblockingyounoncontact'] = '{$a} only accepts messages from their contacts.';
-$string['userssearchresults'] = 'Users found: {$a}';
 $string['viewinganotherusersmessagearea'] = 'You are viewing another user\'s message area.';
 $string['viewmessages'] = 'View messages';
 $string['viewmessageswith'] = 'View messages with {$a}';
@@ -194,3 +124,24 @@ $string['viewnotificationresource'] = 'Go to: {$a}';
 $string['viewunreadmessageswith'] = 'View unread messages with {$a}';
 $string['writeamessage'] = 'Write a message...';
 $string['you'] = 'You:';
+
+// Deprecated since Moodle 3.2.
+$string['context'] = 'context';
+$string['discussion'] = 'Discussion';
+$string['emptysearchstring'] = 'You must search for something';
+$string['formorethan'] = 'For more than';
+$string['keywords'] = 'Keywords';
+$string['messagehistory'] = 'Message history';
+$string['newsearch'] = 'New search';
+$string['nosearchresults'] = 'There were no results from your search';
+$string['onlymycourses'] = 'Only in my courses';
+$string['pagerefreshes'] = 'This page refreshes automatically every {$a} seconds';
+$string['page-message-x'] = 'Any message pages';
+$string['recent'] = 'Recent';
+$string['savemysettings'] = 'Save my settings';
+$string['search'] = 'Search';
+$string['settingssaved'] = 'Your settings have been saved';
+$string['strftimedaydatetime'] = '%A, %d %B %Y, %I:%M %p';
+$string['timenosee'] = 'Minutes since I was last seen online';
+$string['timesent'] = 'Time sent';
+$string['userssearchresults'] = 'Users found: {$a}';
index 9cbbe57..a29760b 100644 (file)
@@ -1191,6 +1191,7 @@ $string['morehelp'] = 'More help';
 $string['moreinfo'] = 'More info';
 $string['moreinformation'] = 'More information about this error';
 $string['moreprofileinfoneeded'] = 'Please tell us more about yourself';
+$string['morenavigationlinks'] = 'More...';
 $string['mostrecently'] = 'most recently';
 $string['move'] = 'Move';
 $string['movecoursemodule'] = 'Move resource';
index 2a779b2..2af8989 100644 (file)
@@ -395,7 +395,7 @@ $string['site:doclinks'] = 'Show links to offsite docs';
 $string['site:forcelanguage'] = 'Override course language';
 $string['site:import'] = 'Import other courses into a course';
 $string['site:maintenanceaccess'] = 'Access site while in maintenance mode';
-$string['site:manageallmessaging'] = 'Can perform all messaging actions on site';
+$string['site:manageallmessaging'] = 'Add, remove, block and unblock contacts for any user';
 $string['site:manageblocks'] = 'Manage blocks on a page';
 $string['site:mnetloginfromremote'] = 'Login from a remote application via MNet';
 $string['site:mnetlogintoremote'] = 'Roam to a remote application via MNet';
index de5c308..735f386 100644 (file)
@@ -1029,7 +1029,7 @@ $cache = '.var_export($cache, true).';
      * Note: this does not verify the validity of plugin or type names.
      *
      * @param string $component
-     * @return array as (string)$type => (string)$plugin
+     * @return array two-items list of [(string)type, (string|null)name]
      */
     public static function normalize_component($component) {
         if ($component === 'moodle' or $component === 'core' or $component === '') {
index 2c6a5c0..332ff1e 100644 (file)
@@ -34,7 +34,7 @@ class theme extends base {
     public function is_uninstall_allowed() {
         global $CFG;
 
-        if ($this->name === 'base' or $this->name === 'bootstrapbase') {
+        if ($this->name === 'bootstrapbase') {
             // All of these are protected for now.
             return false;
         }
index dce102d..886e831 100644 (file)
@@ -33,7 +33,7 @@ defined('MOODLE_INTERNAL') || die();
 class webservice extends base {
     /**
      * Finds all enabled plugins, the result may include missing plugins.
-     * @return array|null of enabled plugins $pluginname=>$pluginname, null means unknown
+     * @return array of enabled plugins $pluginname => $pluginname
      */
     public static function get_enabled_plugins() {
         global $CFG;
index d8b0610..eb94689 100644 (file)
@@ -54,7 +54,7 @@ class redis extends handler {
      * @var int $lockexpire how long to wait in seconds before expiring the lock automatically
      * so that other requests may continue execution, ignored if PECL redis is below version 2.2.0.
      */
-    protected $lockexpire = 7200;
+    protected $lockexpire;
 
     /** @var Redis Connection */
     protected $connection = null;
@@ -62,6 +62,9 @@ class redis extends handler {
     /** @var array $locks List of currently held locks by this page. */
     protected $locks = array();
 
+    /** @var int $timeout How long sessions live before expiring. */
+    protected $timeout;
+
     /**
      * Create new instance of handler.
      */
@@ -88,6 +91,13 @@ class redis extends handler {
             $this->acquiretimeout = (int)$CFG->session_redis_acquire_lock_timeout;
         }
 
+        // The following configures the session lifetime in redis to allow some
+        // wriggle room in the user noticing they've been booted off and
+        // letting them log back in before they lose their session entirely.
+        $updatefreq = empty($CFG->session_update_timemodified_frequency) ? 20 : $CFG->session_update_timemodified_frequency;
+        $this->timeout = $CFG->sessiontimeout + $updatefreq + MINSECS;
+
+        $this->lockexpire = $CFG->sessiontimeout;
         if (isset($CFG->session_redis_lock_expire)) {
             $this->lockexpire = (int)$CFG->session_redis_lock_expire;
         }
@@ -210,7 +220,7 @@ class redis extends handler {
                 $this->unlock_session($id);
                 return '';
             }
-            $this->connection->expire($id, $this->lockexpire);
+            $this->connection->expire($id, $this->timeout);
         } catch (RedisException $e) {
             error_log('Failed talking to redis: '.$e->getMessage());
             throw $e;
@@ -237,7 +247,7 @@ class redis extends handler {
         // There can be race conditions on new sessions racing each other but we can
         // address that in the future.
         try {
-            $this->connection->setex($id, $this->lockexpire, $data);
+            $this->connection->setex($id, $this->timeout, $data);
         } catch (RedisException $e) {
             error_log('Failed talking to redis: '.$e->getMessage());
             return false;
index c300901..2c14e98 100644 (file)
@@ -86,7 +86,9 @@ final class util {
      * @return bool
      */
     public static function can_use_tls12(array $curlinfo, $uname) {
-        if ($curlinfo['version_number'] < 467456 || !defined('CURL_SSLVERSION_TLSv1_2')) {
+        // Do not compare the cURL version, e.g. $curlinfo['version_number'], with v7.34.0 (467456):
+        // some Linux distros backport security issues and keep lower version numbers.
+        if (!defined('CURL_SSLVERSION_TLSv1_2')) {
             return false;
         }
 
index 1cd5269..d8ce7b8 100644 (file)
@@ -361,15 +361,13 @@ class coursecat implements renderable, cacheable_object, IteratorAggregate {
         $newcategory->name = $data->name;
 
         // Validate and set idnumber.
-        if (!empty($data->idnumber)) {
+        if (isset($data->idnumber)) {
             if (core_text::strlen($data->idnumber) > 100) {
                 throw new moodle_exception('idnumbertoolong');
             }
-            if ($DB->record_exists('course_categories', array('idnumber' => $data->idnumber))) {
+            if (strval($data->idnumber) !== '' && $DB->record_exists('course_categories', array('idnumber' => $data->idnumber))) {
                 throw new moodle_exception('categoryidnumbertaken');
             }
-        }
-        if (isset($data->idnumber)) {
             $newcategory->idnumber = $data->idnumber;
         }
 
@@ -484,11 +482,11 @@ class coursecat implements renderable, cacheable_object, IteratorAggregate {
             $newcategory->name = $data->name;
         }
 
-        if (isset($data->idnumber) && $data->idnumber != $this->idnumber) {
+        if (isset($data->idnumber) && $data->idnumber !== $this->idnumber) {
             if (core_text::strlen($data->idnumber) > 100) {
                 throw new moodle_exception('idnumbertoolong');
             }
-            if ($DB->record_exists('course_categories', array('idnumber' => $data->idnumber))) {
+            if (strval($data->idnumber) !== '' && $DB->record_exists('course_categories', array('idnumber' => $data->idnumber))) {
                 throw new moodle_exception('categoryidnumbertaken');
             }
             $newcategory->idnumber = $data->idnumber;
index 215dff7..0c24640 100644 (file)
@@ -214,7 +214,7 @@ function get_plugin_directory($plugintype, $name) {
  * @deprecated since 2.6, use core_component::normalize_component()
  *
  * @param string $component
- * @return array as (string)$type => (string)$plugin
+ * @return array two-items list of [(string)type, (string|null)name]
  */
 function normalize_component($component) {
 
index 80cdcfe..84c0533 100644 (file)
@@ -53,6 +53,7 @@
     max-width: calc(100% - 1em);
 }
 
+/*rtl:begin:ignore*/
 .atto_image_button_left {
     float: left;
     margin: 0 0.5em 0 0;
@@ -64,3 +65,4 @@
     margin: 0 0 0 0.5em;
     max-width: calc(100% - 1em);
 }
+/*rtl:end:ignore*/
\ No newline at end of file
index f1451d5..1a7f9e0 100644 (file)
@@ -4438,6 +4438,7 @@ function complete_user_login($user) {
             if ($changeurl = $userauth->change_password_url()) {
                 redirect($changeurl);
             } else {
+                require_once($CFG->dirroot . '/login/lib.php');
                 $SESSION->wantsurl = core_login_get_return_url();
                 redirect($CFG->httpswwwroot.'/login/change_password.php');
             }
@@ -6134,9 +6135,10 @@ function reset_password_and_mail($user) {
  * Send email to specified user with confirmation text and activation link.
  *
  * @param stdClass $user A {@link $USER} object
+ * @param string $confirmationurl user confirmation URL
  * @return bool Returns true if mail was sent OK and false if there was an error.
  */
-function send_confirmation_email($user) {
+function send_confirmation_email($user, $confirmationurl = null) {
     global $CFG;
 
     $site = get_site();
@@ -6151,7 +6153,12 @@ function send_confirmation_email($user) {
 
     $username = urlencode($user->username);
     $username = str_replace('.', '%2E', $username); // Prevent problems with trailing dots.
-    $data->link  = $CFG->wwwroot .'/login/confirm.php?data='. $user->secret .'/'. $username;
+    if (empty($confirmationurl)) {
+        $confirmationurl = '/login/confirm.php';
+    }
+    $confirmationurl = new moodle_url($confirmationurl, array('data' => $user->secret .'/'. $username));
+    $data->link = $confirmationurl->out(false);
+
     $message     = get_string('emailconfirmation', '', $data);
     $messagehtml = text_to_html(get_string('emailconfirmation', '', $data), false, false, true);
 
index 9d45700..588bb99 100644 (file)
@@ -3632,6 +3632,24 @@ class flat_navigation_node extends navigation_node {
         return $this->type == navigation_node::TYPE_SECTION;
     }
 
+    /**
+     * In flat navigation - sections are active if we are looking at activities in the section.
+     * @return boolean
+     */
+    public function isactive() {
+        global $PAGE;
+
+        if ($this->is_section()) {
+            $active = $PAGE->navigation->find_active_node();
+            while ($active = $active->parent) {
+                if ($active->key == $this->key && $active->type == $this->type) {
+                    return true;
+                }
+            }
+        }
+        return $this->isactive;
+    }
+
     /**
      * Getter for "showdivider"
      * @return boolean
index 157bb15..09eb0bd 100644 (file)
@@ -648,52 +648,57 @@ class single_button implements renderable {
     /**
      * @var moodle_url Target url
      */
-    var $url;
+    public $url;
 
     /**
      * @var string Button label
      */
-    var $label;
+    public $label;
 
     /**
      * @var string Form submit method post or get
      */
-    var $method = 'post';
+    public $method = 'post';
 
     /**
      * @var string Wrapping div class
      */
-    var $class = 'singlebutton';
+    public $class = 'singlebutton';
+
+    /**
+     * @var bool True if button is primary button. Used for styling.
+     */
+    public $primary = false;
 
     /**
      * @var bool True if button disabled, false if normal
      */
-    var $disabled = false;
+    public $disabled = false;
 
     /**
      * @var string Button tooltip
      */
-    var $tooltip = null;
+    public $tooltip = null;
 
     /**
      * @var string Form id
      */
-    var $formid;
+    public $formid;
 
     /**
      * @var array List of attached actions
      */
-    var $actions = array();
+    public $actions = array();
 
     /**
      * @var array $params URL Params
      */
-    var $params;
+    public $params;
 
     /**
      * @var string Action id
      */
-    var $actionid;
+    public $actionid;
 
     /**
      * Constructor
@@ -701,10 +706,11 @@ class single_button implements renderable {
      * @param string $label button text
      * @param string $method get or post submit method
      */
-    public function __construct(moodle_url $url, $label, $method='post') {
+    public function __construct(moodle_url $url, $label, $method='post', $primary=false) {
         $this->url    = clone($url);
         $this->label  = $label;
         $this->method = $method;
+        $this->primary = $primary;
     }
 
     /**
@@ -743,6 +749,7 @@ class single_button implements renderable {
         $data->classes = $this->class;
         $data->disabled = $this->disabled;
         $data->tooltip = $this->tooltip;
+        $data->primary = $this->primary;
 
         // Form parameters.
         $params = $this->url->params();
index 0f95e96..4e18268 100644 (file)
@@ -1707,10 +1707,11 @@ class core_renderer extends renderer_base {
     public function confirm($message, $continue, $cancel) {
         if ($continue instanceof single_button) {
             // ok
+            $continue->primary = true;
         } else if (is_string($continue)) {
-            $continue = new single_button(new moodle_url($continue), get_string('continue'), 'post');
+            $continue = new single_button(new moodle_url($continue), get_string('continue'), 'post', true);
         } else if ($continue instanceof moodle_url) {
-            $continue = new single_button($continue, get_string('continue'), 'post');
+            $continue = new single_button($continue, get_string('continue'), 'post', true);
         } else {
             throw new coding_exception('The continue param to $OUTPUT->confirm() must be either a URL (string/moodle_url) or a single_button instance.');
         }
@@ -1725,10 +1726,19 @@ class core_renderer extends renderer_base {
             throw new coding_exception('The cancel param to $OUTPUT->confirm() must be either a URL (string/moodle_url) or a single_button instance.');
         }
 
-        $output = $this->box_start('generalbox', 'notice');
+        $output = $this->box_start('generalbox modal modal-dialog modal-in-page show', 'notice');
+        $output .= $this->box_start('modal-content', 'modal-content');
+        $output .= $this->box_start('modal-header', 'modal-header');
+        $output .= html_writer::tag('h4', get_string('confirm'));
+        $output .= $this->box_end();
+        $output .= $this->box_start('modal-body', 'modal-body');
         $output .= html_writer::tag('p', $message);
+        $output .= $this->box_end();
+        $output .= $this->box_start('modal-footer', 'modal-footer');
         $output .= html_writer::tag('div', $this->render($continue) . $this->render($cancel), array('class' => 'buttons'));
         $output .= $this->box_end();
+        $output .= $this->box_end();
+        $output .= $this->box_end();
         return $output;
     }
 
@@ -2594,7 +2604,7 @@ $icon_progress
 </div>
 <div id="filepicker-wrapper-{$client_id}" class="mdl-left" style="display:none">
     <div>
-        <input type="button" class="fp-btn-choose" id="filepicker-button-{$client_id}" value="{$straddfile}"{$buttonname}/>
+        <input type="button" class="btn btn-secondary fp-btn-choose" id="filepicker-button-{$client_id}" value="{$straddfile}"{$buttonname}/>
         <span> $maxsize </span>
     </div>
 EOD;
index edadee7..c1f00b7 100644 (file)
@@ -346,7 +346,7 @@ class portfolio_add_button {
         switch ($format) {
             case PORTFOLIO_ADD_FULL_FORM:
                 $formoutput .= $selectoutput;
-                $formoutput .= "\n" . '<input type="submit" value="' . $addstr .'" />';
+                $formoutput .= "\n" . '<input type="submit" class="btn btn-secondary" value="' . $addstr .'" />';
                 $formoutput .= "\n" . '</form>';
             break;
             case PORTFOLIO_ADD_ICON_FORM:
@@ -453,7 +453,7 @@ function portfolio_instance_select($instances, $callerformats, $callbackclass, $
 
     $count = 0;
     $selectoutput = "\n" . '<label class="accesshide" for="instanceid">' . get_string('plugin', 'portfolio') . '</label>';
-    $selectoutput .= "\n" . '<select id="instanceid" name="' . $selectname . '">' . "\n";
+    $selectoutput .= "\n" . '<select id="instanceid" name="' . $selectname . '" class="custom-select">' . "\n";
     $existingexports = portfolio_existing_exports_by_plugin($USER->id);
     foreach ($instances as $instance) {
         $formats = portfolio_supported_formats_intersect($callerformats, $instance->supported_formats());
index b219753..10b3cdb 100644 (file)
@@ -128,6 +128,21 @@ class core_coursecatlib_testcase extends advanced_testcase {
         } catch (moodle_exception $e) {
             $this->assertInstanceOf('moodle_exception', $e);
         }
+        // Test that duplicates with an idnumber of 0 cannot be created.
+        coursecat::create(array('name' => 'Cat3', 'idnumber' => '0'));
+        try {
+            coursecat::create(array('name' => 'Cat4', 'idnumber' => '0'));
+            $this->fail('Duplicate idnumber "0" exception expected in coursecat::create');
+        } catch (moodle_exception $e) {
+            $this->assertInstanceOf('moodle_exception', $e);
+        }
+        // Test an update cannot make a duplicate idnumber of 0.
+        try {
+            $cat2->update(array('idnumber' => '0'));
+            $this->fail('Duplicate idnumber "0" exception expected in coursecat::update');
+        } catch (Exception $e) {
+            $this->assertInstanceOf('moodle_exception', $e);
+        }
     }
 
     public function test_visibility() {
index bba4ceb..50aa919 100644 (file)
@@ -24,7 +24,7 @@
 
 defined('MOODLE_INTERNAL') || die();
 
-// Hack to let tests run on travis..
+// Hack to let tests run on Travis CI.
 defined('CURL_SSLVERSION_TLSv1_2') || define('CURL_SSLVERSION_TLSv1_2', 6);
 
 /**
@@ -36,16 +36,6 @@ defined('CURL_SSLVERSION_TLSv1_2') || define('CURL_SSLVERSION_TLSv1_2', 6);
  */
 class upgrade_util_testcase extends advanced_testcase {
 
-    /**
-     * A cURL version that supports TLS 1.2.
-     */
-    const VALID_CURL_VERSION = 467456;
-
-    /**
-     * A cURL version that does not support TLS 1.2.
-     */
-    const INVALID_CURL_VERSION = 467455;
-
     /**
      * The value of PHP_ZTS when thread safety is enabled.
      */
@@ -132,17 +122,11 @@ class upgrade_util_testcase extends advanced_testcase {
 
         // Set the curl values we are testing to the passed data.
         $curlinfo['ssl_version'] = $sslversion;
-        $curlinfo['version_number'] = self::VALID_CURL_VERSION;
 
         // Set uname to system value if none passed in test case.
         $uname = !empty($uname) ? $uname : php_uname('r');
 
         $this->assertSame($expected, \core\upgrade\util::can_use_tls12($curlinfo, $uname));
-
-        // Now set the curl version to outdated one.
-        $curlinfo['version_number'] = self::INVALID_CURL_VERSION;
-        // Tls12 should never be possible now curl version is bad.
-        $this->assertFalse(\core\upgrade\util::can_use_tls12($curlinfo, $uname));
     }
 
     /**
index b644a5a..56447fc 100644 (file)
@@ -29,8 +29,7 @@ information provided here is intended especially for developers.
 * The following functions have been removed and should not be used any more:
     - file_modify_html_header() - See MDL-29738 for more information.
 * core_grades_external::get_grades has been deprecated. Please do not call this function any more.
-  External function gradereport_user_external::get_grades_table can be used for retrieving the course grades table.
-  Please note that the information returned from that WS is intended for displaying (not for data consumption).
+  External function gradereport_user_external::get_grade_items can be used for retrieving the course grades information.
 * New option 'escape' added to format_string. When true (default), escapes HTML entities from the string
 * The following functions have been deprecated and are not used any more:
   - get_records_csv() Please use csv_import_reader::load_csv_content() instead.
@@ -121,7 +120,6 @@ information provided here is intended especially for developers.
 * The minifier library used by core_minify has been switched to https://github.com/matthiasmullie/minify - there are minor differences
   in minifier output.
 * context_header additional buttons can now have a class attribute provided in the link attributes.
-* Webservice function mod_assign_get_submissions returns a new field 'gradingstatus' from each submission.
 * The return signature for the antivirus::scan_file() function has changed.
   The calling function will now handle removal of infected files from Moodle based on the new integer return value.
 * The first parameter $eventdata of both message_send() and \core\message\manager::send_message() should
@@ -130,6 +128,7 @@ information provided here is intended especially for developers.
   message_sent::create_from_ids() will show a debugging notice if the \core\message\message being sent is missing
   the courseid property, defaulting to SITEID automatically. In Moodle 3.6 (MDL-55449) courseid will be fully mandatory
   for all messages sent.
+* The send_confirmation_email() function has a new optional parameter $confirmationurl to provide a different confirmation URL.
 
 === 3.1 ===
 
index 75a227c..97da57a 100644 (file)
@@ -1319,7 +1319,9 @@ function format_text($text, $format = FORMAT_MOODLE, $options = null, $courseidd
 
     if ($options['blanktarget']) {
         $domdoc = new DOMDocument();
+        libxml_use_internal_errors(true);
         $domdoc->loadHTML('<?xml version="1.0" encoding="UTF-8" ?>' . $text);
+        libxml_clear_errors();
         foreach ($domdoc->getElementsByTagName('a') as $link) {
             if ($link->hasAttribute('target') && strpos($link->getAttribute('target'), '_blank') === false) {
                 continue;
index d8ec012..3ba1e84 100644 (file)
Binary files a/lib/yui/build/moodle-core-notification-confirm/moodle-core-notification-confirm-debug.js and b/lib/yui/build/moodle-core-notification-confirm/moodle-core-notification-confirm-debug.js differ
index 07e6c82..3c20d46 100644 (file)
Binary files a/lib/yui/build/moodle-core-notification-confirm/moodle-core-notification-confirm-min.js and b/lib/yui/build/moodle-core-notification-confirm/moodle-core-notification-confirm-min.js differ
index d8ec012..3ba1e84 100644 (file)
Binary files a/lib/yui/build/moodle-core-notification-confirm/moodle-core-notification-confirm.js and b/lib/yui/build/moodle-core-notification-confirm/moodle-core-notification-confirm.js differ
index 2bf74f3..0a13ee5 100644 (file)
@@ -63,14 +63,14 @@ Y.extend(CONFIRM, M.core.notification.info, {
         this.publish('complete');
         this.publish('complete-yes');
         this.publish('complete-no');
-        this._yesButton = Y.Node.create('<input type="button" class="btn btn-primary" id="id_yuiconfirmyes-' +
+        this._yesButton = Y.Node.create('<input type="button" class="btn btn-primary m-r-1" id="id_yuiconfirmyes-' +
                                         this.get('COUNT') + '" value="' + this.get(CONFIRMYES) + '" />');
-        this._noButton = Y.Node.create('<input type="button" class="btn btn-secondary m-l-1" id="id_yuiconfirmno-' +
+        this._noButton = Y.Node.create('<input type="button" class="btn btn-secondary m-r-1" id="id_yuiconfirmno-' +
                                         this.get('COUNT') + '" value="' + this.get(CONFIRMNO) + '" />');
         this._question = Y.Node.create('<div class="confirmation-message">' + this.get(QUESTION) + '</div>');
         var content = Y.Node.create('<div class="confirmation-dialogue"></div>')
                         .append(this._question)
-                        .append(Y.Node.create('<div class="confirmation-buttons form-inline text-xs-right"></div>')
+                        .append(Y.Node.create('<div class="confirmation-buttons form-inline"></div>')
                             .append(this._yesButton)
                             .append(this._noButton));
         this.get(BASE).addClass('moodle-dialogue-confirm');
index cd44e0b..04314e4 100644 (file)
@@ -31,6 +31,7 @@ $data = optional_param('data', '', PARAM_RAW);  // Formatted as:  secret/usernam
 
 $p = optional_param('p', '', PARAM_ALPHANUM);   // Old parameter:  secret
 $s = optional_param('s', '', PARAM_RAW);        // Old parameter:  username
+$redirect = optional_param('redirect', '', PARAM_LOCALURL);    // Where to redirect the browser once the user has been confirmed.
 
 $PAGE->set_url('/login/confirm.php');
 $PAGE->set_context(context_system::instance());
@@ -78,10 +79,14 @@ if (!empty($data) || (!empty($p) && !empty($s))) {
 
             \core\session\manager::apply_concurrent_login_limit($user->id, session_id());
 
-            if ( ! empty($SESSION->wantsurl) ) {   // Send them where they were going.
-                $goto = $SESSION->wantsurl;
+            // Check where to go, $redirect has a higher preference.
+            if (empty($redirect) and !empty($SESSION->wantsurl) ) {
+                $redirect = $SESSION->wantsurl;
                 unset($SESSION->wantsurl);
-                redirect($goto);
+            }
+
+            if (!empty($redirect)) {
+                redirect($redirect);
             }
         }
 
index 3971b06..2c74e60 100644 (file)
@@ -22,5 +22,5 @@
  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
 
-$string['pluginname'] = 'HTML 5 audio';
-$string['pluginname_help'] = 'Audio files played by browser native audio player. (Format support depends on browser.)';
+$string['pluginname'] = 'HTML5 audio';
+$string['pluginname_help'] = 'Audio files played by the browser\'s native audio player. (Format support depends on the browser.)';
index 17eab3a..43a5aae 100644 (file)
@@ -22,5 +22,5 @@
  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
 
-$string['pluginname'] = 'HTML 5 video';
-$string['pluginname_help'] = 'Video files played by browser native video player. (Format support depends on browser.)';
+$string['pluginname'] = 'HTML5 video';
+$string['pluginname_help'] = 'Video files played by the browser\'s native audio player. (Format support depends on the browser.)';
index 8cf676f..ce750ec 100644 (file)
  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
 
-$string['audiocssclass'] = 'CSS class for audios';
-$string['audioextensions'] = 'Audio files extensions';
-$string['configaudiocssclass'] = 'CSS class that will be added to &lt;audio&gt; element';
-$string['configaudioextensions'] = 'Comma-separated list of supported video file extensions, VideoJS will try to use the browser native video player when available, ' .
-    'and fall back to flash player for other formats if flash is supported by the browser and flash playback is enabled here.';
-$string['configlimitsize'] = 'If width and height are not specified for the video, display with default width/height. If unchecked the videos without specified dimensions will stretch to maximum possible width';
-$string['configvideocssclass'] = 'CSS class that will be added to &lt;video&gt; element. For example class "vjs-big-play-centered" will place the play button in the middle. You can also set the custom skin, refer to <a href="http://docs.videojs.com/">VideoJS documentation</a>';
-$string['configvideoextensions'] = 'Comma-separated list of supported video file extensions, VideoJS will try to use the browser native video player when available, ' .
-    'and fall back to flash player for other formats if flash is supported by the browser and flash playback is enabled here.';
-$string['configyoutube'] = 'Use Video.JS to play YouTube videos. Youtube playlists are not currently supported by Video.JS';
-$string['configuseflash'] = 'Use Flash player if video format is not natively supported by the browser. If enabled, VideoJS will be engaged for any '.
-    'file extension from the above list without browser check. Please note that Flash is not available in mobile browsers and discouraged in many desktop ones.';
+defined('MOODLE_INTERNAL') || die();
+
+$string['audiocssclass'] = 'CSS class for audio';
+$string['audioextensions'] = 'Audio file extensions';
+$string['configaudiocssclass'] = 'A CSS class that will be added to the &lt;audio&gt; element.';
+$string['configaudioextensions'] = 'A comma-separated list of supported audio file extensions. VideoJS will try to use the browser\'s native video player when available, and fall back to a Flash player for other formats if Flash is supported by the browser and Flash fallback is enabled below.';
+$string['configlimitsize'] = 'If enabled, and width and height are not specified, the video will display with default width and height. Otherwise it will stretch to the maximum possible width.';
+$string['configvideocssclass'] = 'A CSS class that will be added to the &lt;video&gt; element. For example, the CSS class "vjs-big-play-centered" will place the play button in the middle. For details, including how to set a custom skin, see docs.videojs.com.';
+$string['configvideoextensions'] = 'A comma-separated list of supported video file extensions. VideoJS will try to use the browser\'s native video player when available, and fall back to a Flash player for other formats if Flash is supported by the browser and Flash fallback is enabled below.';
+$string['configyoutube'] = 'Use VideoJS to play YouTube videos. Note that YouTube playlists are not yet supported by VideoJS.';
+$string['configuseflash'] = 'Use Flash player if video format is not natively supported by the browser. If enabled, VideoJS will be engaged for any file extension from the above list without browser check. Please note that Flash is not available in mobile browsers and discouraged in many desktop ones.';
 $string['limitsize'] = 'Limit size';
 $string['pluginname'] = 'VideoJS player';
-$string['pluginname_help'] = 'Javascript wrapper for video files played by browser native video player with fallback to Flash player. (Format support depends on browser.)';
-$string['videoextensions'] = 'Video files extensions';
+$string['pluginname_help'] = 'A JavaScript wrapper for video files played by the browser\'s native video player with a Flash player fallback. (Format support depends on the browser.)';
+$string['videoextensions'] = 'Video file extensions';
 $string['useflash'] = 'Use Flash fallback';
-$string['videocssclass'] = 'CSS class for videos';
+$string['videocssclass'] = 'CSS class for video';
 $string['youtube'] = 'YouTube videos';
index ebf41b9..05386cd 100644 (file)
@@ -23,4 +23,4 @@
  */
 
 $string['pluginname'] = 'Vimeo';
-$string['pluginname_help'] = 'Vimeo video sharing site.';
+$string['pluginname_help'] = 'The video-sharing website vimeo.com.';
index 36f59ee..00f69ce 100644 (file)
@@ -23,6 +23,6 @@
  */
 
 $string['pluginname'] = 'YouTube';
-$string['pluginname_help'] = 'YouTube video sharing site, video and playlist links supported.';
+$string['pluginname_help'] = 'The video-sharing website youtube.com. Video and playlist links are supported.';
 $string['supportsvideo'] = 'YouTube videos';
 $string['supportsplaylist'] = 'YouTube playlists';
index 6228531..79d87c8 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 1e24d3f..d4347b0 100644 (file)
Binary files a/message/amd/build/preferences_notifications_list_controller.min.js and b/message/amd/build/preferences_notifications_list_controller.min.js differ
index 0f5e7b5..51394ca 100644 (file)
@@ -434,36 +434,42 @@ define(['jquery', 'core/ajax', 'core/templates', 'core/notification', 'core/cust
         Messages.prototype._deleteAllMessages = function() {
             // Create the confirmation modal if we haven't already.
             if (!this._confirmationModal) {
-                ModalFactory.create({
-                    type: ModalFactory.types.CONFIRM,
-                    body: Str.get_string('deleteallconfirm', 'message'),
-                }, this.messageArea.find(SELECTORS.DELETEALLMESSAGES))
-                .done(function(modal) {
-                    this._confirmationModal = modal;
-
-                    // Only delete the conversation if the user agreed in the confirmation modal.
-                    modal.getRoot().on(ModalEvents.yes, function() {
-                        var otherUserId = this._getUserId();
-                        var request = {
-                            methodname: 'core_message_delete_conversation',
-                            args: {
-                                userid: this.messageArea.getCurrentUserId(),
-                                otheruserid: otherUserId
-                            }
-                        };
-
-                        // Delete the conversation.
-                        Ajax.call([request])[0].then(function() {
-                            // Clear the message area.
-                            this.messageArea.find(SELECTORS.MESSAGESAREA).empty();
-                            // Let the app know a conversation was deleted.
-                            this.messageArea.trigger(Events.CONVERSATIONDELETED, otherUserId);
-                            this._hideDeleteAction();
-                        }.bind(this), Notification.exeption);
-                    }.bind(this));
-
-                    // Display the confirmation.
-                    modal.show();
+                Str.get_strings([
+                    {key: 'confirm'},
+                    {key: 'deleteallconfirm', component: 'message'}
+                ]).done(function(s) {
+                    ModalFactory.create({
+                        title: s[0],
+                        type: ModalFactory.types.CONFIRM,
+                        body: s[1]
+                    }, this.messageArea.find(SELECTORS.DELETEALLMESSAGES))
+                        .done(function(modal) {
+                            this._confirmationModal = modal;
+
+                            // Only delete the conversation if the user agreed in the confirmation modal.
+                            modal.getRoot().on(ModalEvents.yes, function() {
+                                var otherUserId = this._getUserId();
+                                var request = {
+                                    methodname: 'core_message_delete_conversation',
+                                    args: {
+                                        userid: this.messageArea.getCurrentUserId(),
+                                        otheruserid: otherUserId
+                                    }
+                                };
+
+                                // Delete the conversation.
+                                Ajax.call([request])[0].then(function() {
+                                    // Clear the message area.
+                                    this.messageArea.find(SELECTORS.MESSAGESAREA).empty();
+                                    // Let the app know a conversation was deleted.
+                                    this.messageArea.trigger(Events.CONVERSATIONDELETED, otherUserId);
+                                    this._hideDeleteAction();
+                                }.bind(this), Notification.exception);
+                            }.bind(this));
+
+                            // Display the confirmation.
+                            modal.show();
+                        }.bind(this));
                 }.bind(this));
             } else {
                 // Otherwise just show the existing modal.
index f55c262..14ab2e6 100644 (file)
@@ -143,10 +143,11 @@ define(['jquery', 'core/ajax', 'core/notification', 'core/custom_interaction_eve
             }
         }.bind(this));
 
-        this.root.on(CustomEvents.events.activate, SELECTORS.PROCESSOR_SETTING, function(e) {
+        this.root.on(CustomEvents.events.activate, SELECTORS.PROCESSOR_SETTING, function(e, data) {
             var element = $(e.target).closest(SELECTORS.PROCESSOR_SETTING);
             var processorSettings = new NotificationProcessorSettings(element);
             processorSettings.show();
+            data.originalEvent.preventDefault();
         });
 
         CustomEvents.define(disabledNotificationsElement, [
index b2e43c4..2062185 100644 (file)
  * This way it is possible to use FPDI for both FPDF and TCPDF with one FPDI version.
  */
 
-if (!class_exists('TCPDF', false)) {
-    /**
-     * Class fpdi_bridge
-     */
-    class fpdi_bridge extends FPDF
-    {
-        // empty body
-    }
-
-} else {
-
     /**
      * Class fpdi_bridge
      *
@@ -205,4 +194,3 @@ if (!class_exists('TCPDF', false)) {
             return current(unpack('H*', $str));
         }
     }
-}
\ No newline at end of file
index 7cac3bb..edc01d8 100644 (file)
@@ -19,6 +19,13 @@ Installation
 3) Update mod/assign/feedback/editpdf/fpdi/fpdi_bridge.php (or the replacement file) to extend 'pdf' instead of 'TCPDF'.
 4) Make a note below of any changes made.
 
+2016/11/15
+----------
+
+1) Class not exists check and the empty fpdi_bridge class has been removed from fpdi_bridge.php to fix a behat error.
+
+Updated by Simey Lameze (MDL-55848)
+
 2015/12/04
 ----------
 Updated to FPDI: 1.6.1
index 8dd5d0a..dabe025 100644 (file)
@@ -49,7 +49,7 @@ $string['assign:grade'] = 'Grade assignment';
 $string['assign:grantextension'] = 'Grant extension';
 $string['assign:manageallocations'] = 'Manage markers allocated to submissions';
 $string['assign:managegrades'] = 'Review and release grades';
-$string['assign:manageoverrides'] = 'Manage assign overrides';
+$string['assign:manageoverrides'] = 'Manage assignment overrides';
 $string['assign:receivegradernotifications'] = 'Receive grader submission notifications';
 $string['assign:releasegrades'] = 'Release grades';
 $string['assign:revealidentities'] = 'Reveal student identities';
@@ -174,9 +174,9 @@ $string['eventgradingformviewed'] = 'Grading form viewed';
 $string['eventgradingtableviewed'] = 'Grading table viewed';
 $string['eventidentitiesrevealed'] = 'The identities have been revealed.';
 $string['eventmarkerupdated'] = 'The allocated marker has been updated.';
-$string['eventoverridecreated'] = 'Assign override created';
-$string['eventoverridedeleted'] = 'Assign override deleted';
-$string['eventoverrideupdated'] = 'Assign override updated';
+$string['eventoverridecreated'] = 'Assignment override created';
+$string['eventoverridedeleted'] = 'Assignment override deleted';
+$string['eventoverrideupdated'] = 'Assignment override updated';
 $string['eventrevealidentitiesconfirmationpageviewed'] = 'Reveal identities confirmation page viewed.';
 $string['eventstatementaccepted'] = 'The user has accepted the statement of the submission.';
 $string['eventsubmissionconfirmationformviewed'] = 'Submission confirmation form viewed.';
@@ -327,7 +327,7 @@ $string['nomoresubmissionsaccepted'] = 'Only allowed for participants who have b
 $string['none'] = 'None';
 $string['noonlinesubmissions'] = 'This assignment does not require you to submit anything online';
 $string['noopen'] = 'No open date';
-$string['nooverridedata'] = 'You must override at least one of the assign settings.';
+$string['nooverridedata'] = 'You must override at least one of the assignment settings.';
 $string['nosavebutnext'] = 'Next';
 $string['nosubmission'] = 'Nothing has been submitted for this assignment';
 $string['nosubmissionsacceptedafter'] = 'No submissions accepted after ';
@@ -382,7 +382,7 @@ $string['requireallteammemberssubmit_help'] = 'If enabled, all members of the st
 $string['recordid'] = 'Identifier';
 $string['revealidentities'] = 'Reveal student identities';
 $string['revealidentitiesconfirm'] = 'Are you sure you want to reveal student identities for this assignment? This operation cannot be undone. Once the student identities have been revealed, the marks will be released to the gradebook.';
-$string['reverttodefaults'] = 'Revert to assign defaults';
+$string['reverttodefaults'] = 'Revert to assignment defaults';
 $string['reverttodraftforstudent'] = 'Revert submission to draft for student: (id={$a->id}, fullname={$a->fullname}).';
 $string['reverttodraft'] = 'Revert the submission to draft status.';
 $string['reverttodraftshort'] = 'Revert the submission to draft';
@@ -515,7 +515,7 @@ $string['userextensiondate'] = 'Extension granted until: {$a}';
 $string['useridlistnotcached'] = 'The grade changes were NOT saved, as it was not possible to determine which submission they were for.';
 $string['useroverrides'] = 'User overrides';
 $string['useroverridesdeleted'] = 'User overrides deleted';
-$string['usersnone'] = 'No students have access to this assign';
+$string['usersnone'] = 'No students have access to this assignment.';
 $string['userswhoneedtosubmit'] = 'Users who need to submit: {$a}';
 $string['usergrade'] = 'User grade';
 $string['validmarkingworkflowstates'] = 'Valid marking workflow states';
index 4dae253..b45d032 100644 (file)
@@ -7046,14 +7046,13 @@ class assign {
         if (!empty($CFG->enableoutcomes)) {
             foreach ($gradinginfo->outcomes as $index => $outcome) {
                 $options = make_grades_menu(-$outcome->scaleid);
+                $options[0] = get_string('nooutcome', 'grades');
                 if ($outcome->grades[$userid]->locked) {
-                    $options[0] = get_string('nooutcome', 'grades');
                     $mform->addElement('static',
                                        'outcome_' . $index . '[' . $userid . ']',
                                        $outcome->name . ':',
                                        $options[$outcome->grades[$userid]->grade]);
                 } else {
-                    $options[''] = get_string('nooutcome', 'grades');
                     $attributes = array('id' => 'menuoutcome_' . $index );
                     $mform->addElement('select',
                                        'outcome_' . $index . '[' . $userid . ']',
index 2b60118..381692f 100644 (file)
@@ -15,6 +15,7 @@ This files describes API changes in the assign code.
   which will then be included in backup and restore; see assign_plugin::get_config_file_areas().
 * Submission and feedback plugins must now return the specific list of configs available for external functions,
   this can be done implementing the new assign plugin method get_config_for_external()
+* Webservice function mod_assign_get_submissions returns a new field 'gradingstatus' from each submission.
 
 === 3.1 ===
 * The feedback plugins now need to implement the is_feedback_modified() method. The default is to return true
index 529f125..8f73785 100644 (file)
@@ -51,11 +51,6 @@ if ($groupmode = groups_get_activity_groupmode($cm)) {   // Groups are being use
     $groupid = 0;
     $groupname = '';
 }
-$showcoursetheme = in_array('bootstrapbase', $PAGE->theme->parents);
-if (!$showcoursetheme && $theme === 'course_theme') { // Set compact as default for non bootstrapbase based themes.
-    $theme = 'compact';
-}
-
 // If requested theme doesn't exist, use default 'bubble' theme.
 if ($theme != 'course_theme' and !file_exists(__DIR__ . '/theme/'.$theme.'/chat.css')) {
     $theme = 'compact';
@@ -78,7 +73,6 @@ $modulecfg = array(
     'home' => $CFG->httpswwwroot.'/mod/chat/view.php?id='.$cm->id,
     'chaturl' => $CFG->httpswwwroot.'/mod/chat/gui_ajax/index.php?id='.$id,
     'theme' => $theme,
-    'showcoursetheme' => $showcoursetheme ? 1 : 0,
     'userid' => $USER->id,
     'sid' => $chatsid,
     'timer' => 3000,
@@ -97,18 +91,19 @@ if ( $theme != 'course_theme') {
 
 echo $OUTPUT->header();
 echo $OUTPUT->box(html_writer::tag('h2',  get_string('participants'), array('class' => 'accesshide')) .
-        '<ul id="users-list"></ul>', '', 'chat-userlist');
+        '<ul id="users-list" class="list-group"></ul>', '', 'chat-userlist');
 echo $OUTPUT->box('', '', 'chat-options');
 echo $OUTPUT->box(html_writer::tag('h2',  get_string('messages', 'chat'), array('class' => 'accesshide')) .
         '<ul id="messages-list"></ul>', '', 'chat-messages');
 $table = new html_table();
 $table->data = array(
-    array('<label class="accesshide" for="input-message">'.get_string('entermessage', 'chat').' </label>'.
-          '<input type="text" disabled="true" id="input-message" value="Loading..." /> '.
-          '<input type="button" id="button-send" value="'.get_string('send', 'chat').'" /> <a id="choosetheme" href="###">'.
+    array('<div class="form-inline"><label class="accesshide" for="input-message">'.get_string('entermessage', 'chat').' </label>'.
+          '<span class="form-group"><input type="text" disabled="true" class="form-control" ' .
+          'id="input-message" value="Loading..." size="48" /></span>'.
+          '<span class="form-group"><input type="button" id="button-send" class="btn btn-secondary m-x-1" ' .
+          'value="'.get_string('send', 'chat').'" /></span> <span class="form-group"><a id="choosetheme" href="###">'.
           get_string('themes').
-          ' &raquo; </a>')
-);
+          ' &raquo; </a></span></div>'));
 echo $OUTPUT->box(html_writer::tag('h2',  get_string('composemessage', 'chat'), array('class' => 'accesshide')) .
         html_writer::table($table), '', 'chat-input-area');
 echo $OUTPUT->box('', '', 'chat-notify');
index 28453e7..7f4b07e 100644 (file)
@@ -118,11 +118,9 @@ M.mod_chat_ajax.init = function(Y, cfg) {
             this.thememenu = new Y.YUI2.widget.Menu('basicmenu', {xy:[0,0]});
             this.thememenu.addItems([
                 {text: M.util.get_string('bubble', 'mod_chat'), url: this.cfg.chaturl + '&theme=bubble'},
-                {text: M.util.get_string('compact', 'mod_chat'), url: this.cfg.chaturl + '&theme=compact'}
+                {text: M.util.get_string('compact', 'mod_chat'), url: this.cfg.chaturl + '&theme=compact'},
+                {text: M.util.get_string('coursetheme', 'mod_chat'), url: this.cfg.chaturl + '&theme=course_theme'}
             ]);
-            if (this.cfg.showcoursetheme == 1) {
-                this.thememenu.addItem({text: M.util.get_string('coursetheme', 'mod_chat'), url: this.cfg.chaturl + '&theme=course_theme'});
-            }
             this.thememenu.render(document.body);
             Y.one('#choosetheme').on('click', function(e) {
                 this.moveTo((e.pageX - 20), (e.pageY - 20));
index 588d7c8..ea717a1 100644 (file)
@@ -150,16 +150,16 @@ echo '<div id="send">';
 echo '<form id="editing" method="post" action="index.php">';
 
 echo '<h2><label for="message">'.get_string('sendmessage', 'message').'</label></h2>';
-echo '<div>';
-echo '<input type="text" id="message" name="message" value="'.s($refreshedmessage, true).'" size="60" />';
-echo '</div><div>';
+echo '<div class="m-b-1">';
+echo '<input type="text" id="message" class="form-control" name="message" value="'.s($refreshedmessage, true).'" size="60" />';
+echo '</div><div class="m-b-1">';
 echo '<input type="hidden" name="id" value="'.$id.'" />';
 echo '<input type="hidden" name="groupid" value="'.$groupid.'" />';
 echo '<input type="hidden" name="last" value="'.time().'" />';
 echo '<input type="hidden" name="sesskey" value="'.sesskey().'" />';
-echo '<input type="submit" value="'.get_string('submit').'" />&nbsp;';
-echo '<input type="submit" name="refresh" value="'.get_string('refresh').'" />';
-echo '<input type="checkbox" name="newonly" id="newonly" '.($newonly ? 'checked="checked" ' : '').'/>';
+echo '<input type="submit" class="btn btn-primary" value="'.get_string('submit').'" />&nbsp;';
+echo '<input type="submit" class="btn btn-secondary" name="refresh" value="'.get_string('refresh').'" />';
+echo '<input type="checkbox" class="m-x-1" name="newonly" id="newonly" '.($newonly ? 'checked="checked" ' : '').'/>';
 echo '<label for="newonly">'.get_string('newonlymsg', 'message').'</label>';
 echo '</div>';
 echo '</form>';
index 6ed7173..e6f001f 100644 (file)
@@ -222,6 +222,8 @@ $completesessions  = 0;
 
 $messagesleft = count($messages);
 
+echo '<div class="list-group">';
+
 foreach ($messages as $message) {  // We are walking BACKWARDS through the messages.
 
     $messagesleft --;              // Countdown.
@@ -246,7 +248,8 @@ foreach ($messages as $message) {  // We are walking BACKWARDS through the messa
         $iscomplete = ($sessionend - $sessionstart > 60 and count($sessionusers) > 1);
         if ($showall or $iscomplete) {
 
-            echo '<p align="center">'.userdate($sessionstart).' --> '. userdate($sessionend).'</p>';
+            echo '<div class="list-group-item">';
+            echo '<p>'.userdate($sessionstart).' --> '. userdate($sessionend).'</p>';
 
             echo $OUTPUT->box_start();
 
@@ -283,6 +286,7 @@ foreach ($messages as $message) {  // We are walking BACKWARDS through the messa
             }
             echo '</p>';
             echo $OUTPUT->box_end();
+            echo '</div>';
         }
         if ($iscomplete) {
             $completesessions++;
@@ -294,6 +298,7 @@ foreach ($messages as $message) {  // We are walking BACKWARDS through the messa
     }
     $lasttime = $message->timestamp;
 }
+echo '</div>';
 
 if (!empty($CFG->enableportfolios) && $canexportsess) {
     require_once($CFG->libdir . '/portfoliolib.php');
index 48f31ba..55aa28c 100644 (file)
@@ -44,7 +44,7 @@ class mod_choice_renderer extends plugin_renderer_base {
         $disabled = empty($options['previewonly']) ? array() : array('disabled' => 'disabled');
 
         $html = html_writer::start_tag('form', $attributes);
-        $html .= html_writer::start_tag('ul', array('class'=>'choices' ));
+        $html .= html_writer::start_tag('ul', array('class' => 'choices list-unstyled unstyled'));
 
         $availableoption = count($options['options']);
         $choicecount = 0;
@@ -59,6 +59,7 @@ class mod_choice_renderer extends plugin_renderer_base {
                 $option->attributes->type = 'radio';
             }
             $option->attributes->id = 'choice_'.$choicecount;
+            $option->attributes->class = 'm-x-1';
 
             $labeltext = $option->text;
             if (!empty($option->attributes->disabled)) {
@@ -82,8 +83,11 @@ class mod_choice_renderer extends plugin_renderer_base {
                 if ($availableoption < 1) {
                     $html .= html_writer::tag('label', get_string('choicefull', 'choice'));
                 } else {
-                    $html .= html_writer::empty_tag('input',
-                            array('type' => 'submit', 'value' => get_string('savemychoice', 'choice'), 'class' => 'button'));
+                    $html .= html_writer::empty_tag('input', array(
+                        'type' => 'submit',
+                        'value' => get_string('savemychoice', 'choice'),
+                        'class' => 'btn btn-primary'
+                    ));
                 }
 
                 if (!empty($options['allowupdate']) && ($options['allowupdate'])) {
@@ -147,7 +151,7 @@ class mod_choice_renderer extends plugin_renderer_base {
         $table = new html_table();
         $table->cellpadding = 0;
         $table->cellspacing = 0;
-        $table->attributes['class'] = 'results names ';
+        $table->attributes['class'] = 'results names table table-bordered';
         $table->tablealign = 'center';
         $table->summary = get_string('responsesto', 'choice', format_string($choices->name));
         $table->data = array();
@@ -223,6 +227,7 @@ class mod_choice_renderer extends plugin_renderer_base {
                         }
 
                         $userfullname = fullname($user, $choices->fullnamecapability);
+                        $mediabody = '';
                         if ($choices->viewresponsecapability && $choices->deleterepsonsecapability) {
                             $checkboxid = 'attempt-user'.$user->id.'-option'.$optionid;
                             $attemptaction = html_writer::label($userfullname . ' ' . $optionsnames[$optionid],
@@ -234,16 +239,17 @@ class mod_choice_renderer extends plugin_renderer_base {
                                 $attemptaction .= html_writer::checkbox('userid[]', $user->id, '', null,
                                     array('id' => $checkboxid));
                             }
-                            $data .= html_writer::tag('div', $attemptaction, array('class'=>'attemptaction'));
+                            $mediabody .= html_writer::tag('div', $attemptaction, array('class'=>'media-left media-middle p-t-1'));
                         }
-                        $userimage = $this->output->user_picture($user, array('courseid'=>$choices->courseid));
-                        $data .= html_writer::tag('div', $userimage, array('class'=>'image'));
-
-                        $userlink = new moodle_url('/user/view.php', array('id'=>$user->id,'course'=>$choices->courseid));
-                        $name = html_writer::tag('a', $userfullname, array('href'=>$userlink, 'class'=>'username'));
-                        $data .= html_writer::tag('div', $name, array('class'=>'fullname'));
-                        $data .= html_writer::tag('div','', array('class'=>'clearfloat'));
-                        $optionusers .= html_writer::tag('div', $data, array('class'=>'user'));
+                        $userimage = $this->output->user_picture($user, array('courseid' => $choices->courseid));
+                        $mediabody .= html_writer::tag('div', $userimage, array('class' => 'media-left media-middle'));
+
+                        $userlink = new moodle_url('/user/view.php', array('id'=>$user->id,'course' => $choices->courseid));
+                        $name = html_writer::tag('a', $userfullname, array('href' => $userlink));
+                        $mediabody .= html_writer::tag('div', $name, array('class' => 'media-body media-middle'));
+                        $data .= html_writer::tag('div', $mediabody, array('class' => 'media m-b-1'));
+
+                        $optionusers .= $data;
                     }
                     $cell->text = $optionusers;
                 }
index 3fcde42..ef14497 100644 (file)
         $options["id"] = "$cm->id";
         $options["download"] = "ods";
         $button =  $OUTPUT->single_button(new moodle_url("report.php", $options), get_string("downloadods"));
-        $downloadoptions[] = html_writer::tag('li', $button, array('class'=>'reportoption'));
+        $downloadoptions[] = html_writer::tag('li', $button, array('class' => 'reportoption list-inline-item'));
 
         $options["download"] = "xls";
         $button = $OUTPUT->single_button(new moodle_url("report.php", $options), get_string("downloadexcel"));
-        $downloadoptions[] = html_writer::tag('li', $button, array('class'=>'reportoption'));
+        $downloadoptions[] = html_writer::tag('li', $button, array('class' => 'reportoption list-inline-item'));
 
         $options["download"] = "txt";
         $button = $OUTPUT->single_button(new moodle_url("report.php", $options), get_string("downloadtext"));
-        $downloadoptions[] = html_writer::tag('li', $button, array('class'=>'reportoption'));
+        $downloadoptions[] = html_writer::tag('li', $button, array('class' => 'reportoption list-inline-item'));
 
-        $downloadlist = html_writer::tag('ul', implode('', $downloadoptions));
-        $downloadlist .= html_writer::tag('div', '', array('class'=>'clearfloat'));
-        echo html_writer::tag('div',$downloadlist, array('class'=>'downloadreport'));
+        $downloadlist = html_writer::tag('ul', implode('', $downloadoptions), array('class' => 'list-inline inline'));
+        $downloadlist .= html_writer::tag('div', '', array('class' => 'clearfloat'));
+        echo html_writer::tag('div',$downloadlist, array('class' => 'downloadreport m-t-1'));
     }
     echo $OUTPUT->footer();
 
diff --git a/mod/choice/styles.css b/mod/choice/styles.css
deleted file mode 100644 (file)
index bdc53df..0000000
+++ /dev/null
@@ -1,152 +0,0 @@
-.path-mod-choice .results {
-    border-collapse: separate;
-}
-
-.path-mod-choice .results .data {
-    vertical-align: top;
-    white-space: nowrap;
-}
-
-.path-mod-choice .button {
-    text-align: center;
-}
-
-.path-mod-choice .attemptcell {
-    width: 5px;
-    white-space: nowrap;
-}
-
-.path-mod-choice .anonymous,
-.path-mod-choice .names {
-    margin-left: auto;
-    margin-right: auto;
-    width: 100%;
-}
-
-.path-mod-choice .downloadreport {
-    border-width: 0;
-    margin-left: 10%;
-}
-
-.path-mod-choice .choiceresponse {
-    width: 100%;
-}
-
-.path-mod-choice .choiceresponse .picture {
-    width: 10px;
-    white-space: nowrap;
-}
-
-.path-mod-choice .choiceresponse .fullname {
-    width: 100%;
-    white-space: nowrap;
-}
-
-.path-mod-choice .responseheader {
-    width: 100%;
-    text-align: center;
-    margin-top: 10px;
-}
-
-.path-mod-choice .choices .option label {
-    vertical-align: top;
-}
-
-.path-mod-choice .choices .option input {
-    vertical-align: middle;
-}
-
-.path-mod-choice .horizontal,
-.path-mod-choice .vertical {
-    margin-left: 10%;
-    margin-right: 10%;
-}
-
-.path-mod-choice .horizontal .choices .option {
-    padding-right: 20px;
-    display: inline-block;
-    white-space: normal;
-}
-
-.path-mod-choice .horizontal .choices .button {
-    margin-top: 10px;
-}
-
-.path-mod-choice ul.choices li {
-    list-style: none;
-}
-
-.path-mod-choice .results {
-    text-align: center;
-}
-
-.path-mod-choice .results.anonymous .graph.horizontal {
-    vertical-align: middle;
-    text-align: left;
-    width: 70%;
-}
-
-.path-mod-choice .results.anonymous .graph.vertical,
-.path-mod-choice .cell {
-    vertical-align: bottom;
-    text-align: center;
-}
-
-.path-mod-choice .results.names .header {
-    width: 10%;
-    white-space: normal;
-}
-
-.path-mod-choice .results.names .cell {
-    vertical-align: top;
-    text-align: left;
-}
-
-.path-mod-choice .results.names .user,
-.path-mod-choice #yourselection {
-    padding: 5px;
-}
-
-.path-mod-choice .results.names .user .attemptaction,
-.path-mod-choice .results.names .user .image,
-.path-mod-choice .results.names .user .fullname {
-    float: left;
-}
-
-.path-mod-choice .results.names .user .fullname {
-    padding-left: 5px;
-}
-
-.path-mod-choice .results .data.header {
-    width: 10%;
-}
-
-.path-mod-choice .responseaction {
-    text-align: center;
-}
-
-.path-mod-choice .results .option {
-    white-space: normal;
-}
-
-.path-mod-choice .response {
-    overflow: auto;
-}
-
-.path-mod-choice .results .option,
-.path-mod-choice .results .numberofuser,
-.path-mod-choice .results .percentage {
-    font-weight: bold;
-    font-size: 108%;
-}
-
-#page-mod-choice-report .downloadreport ul li {
-    list-style: none;
-    padding: 0 20px;
-    float: left;
-}
-
-.path-mod-choice .clearfloat {
-    float: none;
-    clear: both;
-}
index 38b7194..3b3641c 100644 (file)
@@ -63,7 +63,7 @@ class data_field_url extends data_field_base {
             }
         }
 
-        $autolinkable = !empty($this->field->param1) and empty($this->field->param2);
+        $autolinkable = !empty($this->field->param1) && empty($this->field->param2);
 
         $str = '<div title="' . s($this->field->description) . '" class="form-inline">';
 
index 1331c02..7b695e1 100644 (file)
@@ -75,9 +75,7 @@ class mod_folder_renderer extends plugin_renderer_base {
                     get_string('downloadfolder', 'folder')
                 );
 
-                $output .= $this->output->container(
-                    $downloadbutton,
-                    'mdl-align folder-download-button');
+                $output .= $downloadbutton;
             }
 
             if (has_capability('mod/folder:managefiles', $context)) {
@@ -86,9 +84,7 @@ class mod_folder_renderer extends plugin_renderer_base {
                     get_string('edit')
                 );
 
-                $output .= $this->output->container(
-                    $editbutton,
-                    'mdl-align folder-edit-button');
+                $output .= $editbutton;
             }
         }
         return $output;
index 2bb4a41..bccea71 100644 (file)
@@ -294,7 +294,7 @@ $neighbourlinks = $renderer->neighbouring_discussion_navigation($neighbours['pre
 echo $neighbourlinks;
 
 /// Print the controls across the top
-echo '<div class="discussioncontrols clearfix"><div class="controlscontainer">';
+echo '<div class="discussioncontrols clearfix"><div class="controlscontainer m-b-1">';
 
 if (!empty($CFG->enableportfolios) && has_capability('mod/forum:exportdiscussion', $modcontext)) {
     require_once($CFG->libdir.'/portfoliolib.php');
index af790b6..56bdb8f 100644 (file)
@@ -58,12 +58,7 @@ $exporturl = moodle_url::make_pluginfile_url($context->id, 'mod_glossary', 'expo
 
 ?>
     <form action="<?php echo $exporturl->out(); ?>" method="post">
-    <table border="0" cellpadding="6" cellspacing="6" width="100%">
-    <tr><td align="center">
-        <input type="submit" value="<?php p($strexportfile)?>" />
-    </td></tr></table>
-    <div>
-    </div>
+        <input class="btn btn-primary" type="submit" value="<?php p($strexportfile)?>" />
     </form>
 <?php
     // don't need cap check here, we share with the general export.
index 6f43dc4..6a632d0 100644 (file)
@@ -1181,7 +1181,7 @@ function  glossary_print_entry_aliases($course, $cm, $glossary, $entry,$mode='',
         foreach ($aliases as $alias) {
             if (trim($alias->alias)) {
                 if ($return == '') {
-                    $return = '<select id="keyword" style="font-size:8pt">';
+                    $return = '<select id="keyword" class="custom-select">';
                 }
                 $return .= "<option>$alias->alias</option>";
             }
@@ -1233,7 +1233,7 @@ function glossary_print_entry_icons($course, $cm, $glossary, $entry, $mode='',$h
 
     if (has_capability('mod/glossary:approve', $context) && !$glossary->defaultapproval && $entry->approved) {
         $output = true;
-        $return .= '<a class="action-icon" title="' . get_string('disapprove', 'glossary').
+        $return .= '<a class="icon" title="' . get_string('disapprove', 'glossary').
                    '" href="approve.php?newstate=0&amp;eid='.$entry->id.'&amp;mode='.$mode.
                    '&amp;hook='.urlencode($hook).'&amp;sesskey='.sesskey().
                    '"><img src="'.$OUTPUT->pix_url('t/block').'" class="smallicon" alt="'.
@@ -1248,7 +1248,10 @@ function glossary_print_entry_icons($course, $cm, $glossary, $entry, $mode='',$h
             $mainglossary = $DB->get_record('glossary', array('mainglossary'=>1,'course'=>$course->id));
             if ( $mainglossary ) {  // if there is a main glossary defined, allow to export the current entry
                 $output = true;
-                $return .= '<a class="action-icon" title="'.get_string('exporttomainglossary','glossary') . '" href="exportentry.php?id='.$entry->id.'&amp;prevmode='.$mode.'&amp;hook='.urlencode($hook).'"><img src="'.$OUTPUT->pix_url('export', 'glossary').'" class="smallicon" alt="'.get_string('exporttomainglossary','glossary').$altsuffix.'" /></a>';
+                $return .= '<a class="icon" title="'.get_string('exporttomainglossary','glossary') . '" ' .
+                    'href="exportentry.php?id='.$entry->id.'&amp;prevmode='.$mode.'&amp;hook='.urlencode($hook).'">' .
+                    '<img src="'.$OUTPUT->pix_url('export', 'glossary').'" class="smallicon" ' .
+                    'alt="'.get_string('exporttomainglossary','glossary').$altsuffix.'" /></a>';
             }
         }
 
@@ -1264,11 +1267,16 @@ function glossary_print_entry_icons($course, $cm, $glossary, $entry, $mode='',$h
         $ineditperiod = ((time() - $entry->timecreated <  $CFG->maxeditingtime) || $glossary->editalways);
         if ( !$importedentry and (has_capability('mod/glossary:manageentries', $context) or ($entry->userid == $USER->id and ($ineditperiod and has_capability('mod/glossary:write', $context))))) {
             $output = true;
-            $return .= "<a class='action-icon' title=\"" . get_string("delete") . "\" href=\"deleteentry.php?id=$cm->id&amp;mode=delete&amp;entry=$entry->id&amp;prevmode=$mode&amp;hook=".urlencode($hook)."\"><img src=\"";
+            $url = "deleteentry.php?id=$cm->id&amp;mode=delete&amp;entry=$entry->id&amp;prevmode=$mode&amp;hook=".urlencode($hook);
+            $return .= "<a class='icon' title=\"" . get_string("delete") . "\" " .
+                       "href=\"$url\"><img src=\"";
             $return .= $icon;
             $return .= "\" class=\"smallicon\" alt=\"" . get_string("delete") .$altsuffix."\" /></a>";
 
-            $return .= "<a class='action-icon' title=\"" . get_string("edit") . "\" href=\"edit.php?cmid=$cm->id&amp;id=$entry->id&amp;mode=$mode&amp;hook=".urlencode($hook)."\"><img src=\"" . $OUTPUT->pix_url('t/edit') . "\" class=\"smallicon\" alt=\"" . get_string("edit") .$altsuffix. "\" /></a>";
+            $url = "edit.php?cmid=$cm->id&amp;id=$entry->id&amp;mode=$mode&amp;hook=".urlencode($hook);
+            $return .= "<a class='icon' title=\"" . get_string("edit") . "\" href=\"$url\">" .
+                       "<img src=\"" . $OUTPUT->pix_url('t/edit') . "\" class=\"smallicon\" " .
+                       "alt=\"" . get_string("edit") .$altsuffix. "\" /></a>";
         } elseif ( $importedentry ) {
             $return .= "<font size=\"-1\">" . get_string("exportedentry","glossary") . "</font>";
         }
@@ -1311,6 +1319,7 @@ function glossary_print_entry_icons($course, $cm, $glossary, $entry, $mode='',$h
         $return .= '<div>'.$comment->output(true).'</div>';
         $output = true;
     }
+    $return .= '<hr>';
 
     //If we haven't calculated any REAL thing, delete result ($return)
     if (!$output) {
index f4d92eb..12530e1 100644 (file)
     width: 35px;
 }
 
-.path-mod-glossary .glossarypost .entrylowersection .aliases {
-    text-align: center;
-}
-
 .path-mod-glossary .glossarypost .entrylowersection .icons {
     text-align: right;
     padding-right: 5px;
index 7c023e1..a53b43f 100644 (file)
@@ -360,46 +360,41 @@ if ($glossary->intro && $showcommonelements) {
 
 /// Search box
 if ($showcommonelements ) {
-    echo '<form method="post" action="view.php">';
+    echo '<form method="post" class="form form-inline m-b-1" action="view.php">';
 
-    echo '<table class="boxaligncenter" width="70%" border="0">';
-    echo '<tr><td align="center" class="glossarysearchbox">';
 
-    echo '<input type="submit" value="'.$strsearch.'" name="searchbutton" /> ';
     if ($mode == 'search') {
-        echo '<input type="text" name="hook" size="20" value="'.s($hook).'" alt="'.$strsearch.'" /> ';
+        echo '<input type="text" name="hook" size="20" value="'.s($hook).'" alt="'.$strsearch.'" class="form-control"/> ';
     } else {
-        echo '<input type="text" name="hook" size="20" value="" alt="'.$strsearch.'" /> ';
+        echo '<input type="text" name="hook" size="20" value="" alt="'.$strsearch.'" class="form-control"/> ';
     }
+    echo '<input type="submit" value="'.$strsearch.'" name="searchbutton" class="btn btn-secondary m-r-1"/> ';
     if ($fullsearch || $mode != 'search') {
         $fullsearchchecked = 'checked="checked"';
     } else {
         $fullsearchchecked = '';
     }
-    echo '<input type="checkbox" name="fullsearch" id="fullsearch" value="1" '.$fullsearchchecked.' />';
+    echo '<span class="checkbox"><label for="fullsearch">';
+    echo ' <input type="checkbox" name="fullsearch" id="fullsearch" value="1" '.$fullsearchchecked.'/> ';
     echo '<input type="hidden" name="mode" value="search" />';
     echo '<input type="hidden" name="id" value="'.$cm->id.'" />';
-    echo '<label for="fullsearch">'.$strsearchindefinition.'</label>';
-    echo '</td></tr></table>';
+    echo $strsearchindefinition.'</label></span>';
 
     echo '</form>';
-
-    echo '<br />';
 }
 
 /// Show the add entry button if allowed
 if (has_capability('mod/glossary:write', $context) && $showcommonelements ) {
     echo '<div class="singlebutton glossaryaddentry">';
-    echo "<form id=\"newentryform\" method=\"get\" action=\"$CFG->wwwroot/mod/glossary/edit.php\">";
+    echo "<form class=\"form form-inline m-b-1\" id=\"newentryform\" method=\"get\" action=\"$CFG->wwwroot/mod/glossary/edit.php\">";
     echo '<div>';
     echo "<input type=\"hidden\" name=\"cmid\" value=\"$cm->id\" />";
-    echo '<input type="submit" value="'.get_string('addentry', 'glossary').'" />';
+    echo '<input type="submit" value="'.get_string('addentry', 'glossary').'" class="btn btn-secondary" />';
     echo '</div>';
     echo '</form>';
     echo "</div>\n";
 }
 
-echo '<br />';
 
 require("tabs.php");
 
index cb7568a..ee1a3c1 100644 (file)
@@ -251,7 +251,9 @@ class lesson_page_type_branchtable extends lesson_page {
         $formattextdefoptions->context = $answerpage->context;
 
         foreach ($answers as $answer) {
-            $data = "<input type=\"button\" name=\"$answer->id\" value=\"".s(strip_tags(format_text($answer->answer, FORMAT_MOODLE,$formattextdefoptions)))."\" disabled=\"disabled\"> ";
+            $data = "<input type=\"button\" class=\"btn btn-secondary\" name=\"$answer->id\" " .
+                    "value=\"".s(strip_tags(format_text($answer->answer, FORMAT_MOODLE, $formattextdefoptions)))."\" " .
+                    "disabled=\"disabled\"> ";
             $data .= get_string('jumpsto', 'lesson', $this->get_jump_name($answer->jumpto));
             $answerdata->answers[] = array($data, "");
             $answerpage->answerdata = $answerdata;
index f88d04b..cb8e522 100644 (file)
@@ -443,14 +443,16 @@ class lesson_page_type_matching extends lesson_page {
                 if ($useranswer != null) {
                     $userresponse = explode(",", $useranswer->useranswer);
                     $data .= '<label class="accesshide" for="stu_answer_response_' . $n . '">' . get_string('matchesanswer', 'lesson') . '</label>';
-                    $data .= "<select id=\"stu_answer_response_" . $n . "\" disabled=\"disabled\"><option selected=\"selected\">";
+                    $data .= "<select class=\"custom-select\" id=\"stu_answer_response_" . $n . "\" " .
+                             "disabled=\"disabled\"><option selected=\"selected\">";
                     if (array_key_exists($i, $userresponse)) {
                         $data .= $userresponse[$i];
                     }
                     $data .= "</option></select>";
                 } else {
                     $data .= '<label class="accesshide" for="answer_response_' . $n . '">' . get_string('matchesanswer', 'lesson') . '</label>';
-                    $data .= "<select id=\"answer_response_" . $n . "\" disabled=\"disabled\"><option selected=\"selected\">".strip_tags(format_string($answer->response))."</option></select>";
+                    $data .= "<select class=\"custom-select\" id=\"answer_response_" . $n . "\" " .
+                             "disabled=\"disabled\"><option selected=\"selected\">".strip_tags(format_string($answer->response))."</option></select>";
                 }
 
                 if ($n == 2) {
index 4840ca9..f47280d 100644 (file)
@@ -204,7 +204,8 @@ class lesson_page_type_numerical extends lesson_page {
                     $total = $stats["total"];
                     unset($stats["total"]);
                     foreach ($stats as $valentered => $ntimes) {
-                        $data = '<input type="text" size="50" disabled="disabled" readonly="readonly" value="'.s($valentered).'" />';
+                        $data = '<input class="form-control" type="text" size="50" ' .
+                                'disabled="disabled" readonly="readonly" value="'.s($valentered).'" />';
                         $percent = $ntimes / $total * 100;
                         $percent = round($percent, 2);
                         $percent .= "% ".get_string("enteredthis", "lesson");
@@ -216,7 +217,8 @@ class lesson_page_type_numerical extends lesson_page {
                 $i++;
             } else if ($useranswer != null && ($answer->id == $useranswer->answerid || ($answer == end($answers) && empty($answerdata)))) {
                  // get in here when what the user entered is not one of the answers
-                $data = '<input type="text" size="50" disabled="disabled" readonly="readonly" value="'.s($useranswer->useranswer).'">';
+                $data = '<input class="form-control" type="text" size="50" ' .
+                        'disabled="disabled" readonly="readonly" value="'.s($useranswer->useranswer).'">';
                 if (isset($pagestats[$this->properties->id][$useranswer->useranswer])) {
                     $percent = $pagestats[$this->properties->id][$useranswer->useranswer] / $pagestats[$this->properties->id]["total"] * 100;
                     $percent = round($percent, 2);
index 0efed4f..cda6740 100644 (file)
@@ -267,7 +267,8 @@ class lesson_page_type_shortanswer extends lesson_page {
                     $total = $stats["total"];
                     unset($stats["total"]);
                     foreach ($stats as $valentered => $ntimes) {
-                        $data = '<input type="text" size="50" disabled="disabled" readonly="readonly" value="'.s($valentered).'" />';
+                        $data = '<input type="text" size="50" disabled="disabled" class="form-control" ' .
+                                'readonly="readonly" value="'.s($valentered).'" />';
                         $percent = $ntimes / $total * 100;
                         $percent = round($percent, 2);
                         $percent .= "% ".get_string("enteredthis", "lesson");
@@ -279,7 +280,8 @@ class lesson_page_type_shortanswer extends lesson_page {
                 $i++;
             } else if ($useranswer != null && ($answer->id == $useranswer->answerid || $answer == end($answers))) {
                  // get in here when what the user entered is not one of the answers
-                $data = '<input type="text" size="50" disabled="disabled" readonly="readonly" value="'.s($useranswer->useranswer).'">';
+                $data = '<input type="text" size="50" disabled="disabled" class="form-control" ' .
+                        'readonly="readonly" value="'.s($useranswer->useranswer).'">';
                 if (isset($pagestats[$this->properties->id][$useranswer->useranswer])) {
                     $percent = $pagestats[$this->properties->id][$useranswer->useranswer] / $pagestats[$this->properties->id]["total"] * 100;
                     $percent = round($percent, 2);
index a1c4777..ba2f4f9 100644 (file)
@@ -451,7 +451,9 @@ if ($action === 'delete') {
         $checklinks  = '<a href="javascript: checkall();">'.get_string('selectall').'</a> / ';
         $checklinks .= '<a href="javascript: checknone();">'.get_string('deselectall').'</a>';
         $checklinks .= html_writer::label('action', 'menuaction', false, array('class' => 'accesshide'));
-        $checklinks .= html_writer::select(array('delete' => get_string('deleteselected')), 'action', 0, array(''=>'choosedots'), array('id'=>'actionid', 'class' => 'autosubmit'));
+        $options = array('delete' => get_string('deleteselected'));
+        $attributes = array('id' => 'actionid', 'class' => 'custom-select m-l-1 autosubmit');
+        $checklinks .= html_writer::select($options, 'action', 0, array('' => 'choosedots'), $attributes);
         $PAGE->requires->yui_module('moodle-core-formautosubmit',
             'M.core.init_formautosubmit',
             array(array('selectid' => 'actionid', 'nothing' => false))
@@ -664,7 +666,7 @@ if ($action === 'delete') {
 
         $table->head = array();
         $table->align = array('right', 'left');
-        $table->attributes['class'] = 'compacttable generaltable';
+        $table->attributes['class'] = 'compacttable generaltable form-inline';
 
         $params = array("lessonid"=>$lesson->id, "userid"=>$userid);
         if (!$grades = $DB->get_records_select("lesson_grades", "lessonid = :lessonid and userid = :userid", $params, "completed", "*", $try, 1)) {
@@ -706,7 +708,7 @@ if ($action === 'delete') {
 
     $table->align = array('left', 'left');
     $table->size = array('70%', null);
-    $table->attributes['class'] = 'compacttable generaltable';
+    $table->attributes['class'] = 'compacttable generaltable form-inline';
 
     foreach ($answerpages as $page) {
         unset($table->data);
index 48befcf..e05bbf6 100644 (file)
Binary files a/mod/quiz/amd/build/preflightcheck.min.js and b/mod/quiz/amd/build/preflightcheck.min.js differ
index f55e130..2558520 100644 (file)
@@ -45,7 +45,7 @@ define(['jquery', 'core/yui'], function($, Y) {
         init: function(startButton, confirmationTitle, confirmationForm, popupoptions) {
             var finalStartButton = startButton;
 
-            Y.use('moodle-core-notification', 'moodle-core-formchangechecker', 'io-form', function() {
+            Y.use('moodle-core-notification', function() {
                 if (Y.one(confirmationForm)) {
                     t.confirmDialogue = new M.core.dialogue({
                         headerContent: confirmationTitle,
@@ -99,13 +99,15 @@ define(['jquery', 'core/yui'], function($, Y) {
          */
         launchQuizPopup: function(e, popupoptions) {
             e.halt();
-            M.core_formchangechecker.reset_form_dirty_state();
-            var form = e.target.ancestor('form');
-            window.openpopup(e, {
-                url: form.get('action') + '?' + Y.IO.stringify(form).replace(/\bcancel=/, 'x='),
-                windowname: 'quizpopup',
-                options: popupoptions,
-                fullscreen: true,
+            Y.use('moodle-core-formchangechecker', 'io-form', function() {
+                M.core_formchangechecker.reset_form_dirty_state();
+                var form = e.target.ancestor('form');
+                window.openpopup(e, {
+                    url: form.get('action') + '?' + Y.IO.stringify(form).replace(/\bcancel=/, 'x='),
+                    windowname: 'quizpopup',
+                    options: popupoptions,
+                    fullscreen: true,
+                });
             });
         }
     };
index 06a1a2f..16b22d1 100644 (file)
@@ -458,7 +458,13 @@ function quiz_user_outline($course, $user, $mod, $quiz) {
     }
 
     $result = new stdClass();
-    $result->info = get_string('grade') . ': ' . $grade->str_long_grade;
+    // If the user can't see hidden grades, don't return that information.
+    $gitem = grade_item::fetch(array('id' => $grades->items[0]->id));
+    if (!$gitem->hidden || has_capability('moodle/grade:viewhidden', context_course::instance($course->id))) {
+        $result->info = get_string('grade') . ': ' . $grade->str_long_grade;
+    } else {
+        $result->info = get_string('grade') . ': ' . get_string('hidden', 'grades');
+    }
 
     // Datesubmitted == time created. dategraded == time modified or time overridden
     // if grade was last modified by the user themselves use date graded. Otherwise use
@@ -491,9 +497,18 @@ function quiz_user_complete($course, $user, $mod, $quiz) {
     $grades = grade_get_grades($course->id, 'mod', 'quiz', $quiz->id, $user->id);
     if (!empty($grades->items[0]->grades)) {
         $grade = reset($grades->items[0]->grades);
-        echo $OUTPUT->container(get_string('grade').': '.$grade->str_long_grade);
-        if ($grade->str_feedback) {
-            echo $OUTPUT->container(get_string('feedback').': '.$grade->str_feedback);
+        // If the user can't see hidden grades, don't return that information.
+        $gitem = grade_item::fetch(array('id' => $grades->items[0]->id));
+        if (!$gitem->hidden || has_capability('moodle/grade:viewhidden', context_course::instance($course->id))) {
+            echo $OUTPUT->container(get_string('grade').': '.$grade->str_long_grade);
+            if ($grade->str_feedback) {
+                echo $OUTPUT->container(get_string('feedback').': '.$grade->str_feedback);
+            }
+        } else {
+            echo $OUTPUT->container(get_string('grade') . ': ' . get_string('hidden', 'grades'));
+            if ($grade->str_feedback) {
+                echo $OUTPUT->container(get_string('feedback').': '.get_string('hidden', 'grades'));
+            }
         }
     }
 
@@ -504,8 +519,19 @@ function quiz_user_complete($course, $user, $mod, $quiz) {
             if ($attempt->state != quiz_attempt::FINISHED) {
                 echo quiz_attempt_state_name($attempt->state);
             } else {
-                echo quiz_format_grade($quiz, $attempt->sumgrades) . '/' .
-                        quiz_format_grade($quiz, $quiz->sumgrades);
+                if (!isset($gitem)) {
+                    if (!empty($grades->items[0]->grades)) {
+                        $gitem = grade_item::fetch(array('id' => $grades->items[0]->id));
+                    } else {
+                        $gitem = new stdClass();
+                        $gitem->hidden = true;
+                    }
+                }
+                if (!$gitem->hidden || has_capability('moodle/grade:viewhidden', context_course::instance($course->id))) {
+                    echo quiz_format_grade($quiz, $attempt->sumgrades) . '/' . quiz_format_grade($quiz, $quiz->sumgrades);
+                } else {
+                    echo get_string('hidden', 'grades');
+                }
             }
             echo ' - '.userdate($attempt->timemodified).'<br />';
         }
diff --git a/mod/quiz/tests/behat/preview.feature b/mod/quiz/tests/behat/preview.feature
new file mode 100644 (file)
index 0000000..2cec65d
--- /dev/null
@@ -0,0 +1,45 @@
+@mod @mod_quiz
+Feature: Preview a quiz as a teacher
+  In order to verify my quizzes are ready for my students
+  As a teacher
+  I need to be able to preview them
+
+  Background:
+    Given the following "users" exist:
+      | username | firstname | lastname | email               |
+      | teacher  | Teacher   | One      | teacher@example.com |
+    And the following "courses" exist:
+      | fullname | shortname | category |
+      | Course 1 | C1        | 0        |
+    And the following "course enrolments" exist:
+      | user     | course | role    |
+      | teacher  | C1     | teacher |
+    And the following "question categories" exist:
+      | contextlevel | reference | name           |
+      | Course       | C1        | Test questions |
+    And the following "activities" exist:
+      | activity   | name   | intro              | course | idnumber |
+      | quiz       | Quiz 1 | Quiz 1 description | C1     | quiz1    |
+    And the following "questions" exist:
+      | questioncategory | qtype       | name  | questiontext    |
+      | Test questions   | truefalse   | TF1   | First question  |
+      | Test questions   | truefalse   | TF2   | Second question |
+    And quiz "Quiz 1" contains the following questions:
+      | question | page | maxmark |
+      | TF1      | 1    |         |
+      | TF2      | 1    | 3.0     |
+    And I log in as "teacher"
+    And I follow "Course 1"
+
+  @javascript
+  Scenario: Preview a quiz
+    When I follow "Quiz 1"
+    And I press "Preview quiz now"
+    And I click on "True" "radio" in the "First question" "question"
+    And I click on "False" "radio" in the "Second question" "question"
+    And I press "Finish attempt ..."
+    And I press "Submit all and finish"
+    And I click on "Submit all and finish" "button" in the "Confirmation" "dialogue"
+    Then I should see "25.00 out of 100.00"
+    And I follow "Finish review"
+    And "Review" "link" in the "Preview" "table_row" should be visible
index 02b1a90..4f8c086 100644 (file)
@@ -78,7 +78,7 @@ $PAGE->set_url('/mod/quiz/view.php', array('id' => $cm->id));
 // Create view object which collects all the information the renderer will need.
 $viewobj = new mod_quiz_view_object();
 $viewobj->accessmanager = $accessmanager;
-$viewobj->canreviewmine = $canreviewmine;
+$viewobj->canreviewmine = $canreviewmine || $canpreview;
 
 // Get this user's attempts.
 $attempts = quiz_get_user_attempts($quiz->id, $USER->id, 'finished', true);
index 7b71623..28d1303 100644 (file)
@@ -897,7 +897,7 @@ function scorm_print_launch ($user, $scorm, $action, $cm) {
     $organization = optional_param('organization', '', PARAM_INT);
 
     if ($scorm->displaycoursestructure == 1) {
-        echo $OUTPUT->box_start('generalbox boxaligncenter toc', 'toc');
+        echo $OUTPUT->box_start('generalbox boxaligncenter toc container', 'toc');
         echo html_writer::div(get_string('contents', 'scorm'), 'structurehead');
     }
     if (empty($organization)) {
@@ -946,7 +946,8 @@ function scorm_print_launch ($user, $scorm, $action, $cm) {
             echo html_writer::start_div('scorm-center');
             echo html_writer::start_tag('form', array('id' => 'scormviewform',
                                                         'method' => 'post',
-                                                        'action' => $CFG->wwwroot.'/mod/scorm/player.php'));
+                                                        'action' => $CFG->wwwroot.'/mod/scorm/player.php',
+                                                        'class' => 'container'));
         if ($scorm->hidebrowse == 0) {
             print_string('mode', 'scorm');
             echo ': '.html_writer::empty_tag('input', array('type' => 'radio', 'id' => 'b', 'name' => 'mode',
index c900bda..fc6644b 100644 (file)
@@ -6,10 +6,6 @@
     text-align: left;
 }
 
-.path-mod-scorm .scorm-center {
-    text-align: center;
-}
-
 .path-mod-scorm .scorm-right {
     text-align: right;
 }
     overflow: auto;
 }
 
-.path-mod-scorm.forcejavascript .scorm-center {
-    display: none;
-}
-
 .path-mod-scorm.forcejavascript .toc {
     display: none;
 }
     display: none;
 }
 
-.path-mod-scorm.jsenabled .scorm-center {
-    display: block;
-}
-
 .path-mod-scorm.jsenabled .toc {
     display: block;
 }
index 7fe2216..21d63b8 100644 (file)
@@ -156,20 +156,20 @@ if (empty($launch) && ($scorm->displayattemptstatus == SCORM_DISPLAY_ATTEMPTSTAT
          $scorm->displayattemptstatus == SCORM_DISPLAY_ATTEMPTSTATUS_ENTRY)) {
     $attemptstatus = scorm_get_attempt_status($USER, $scorm, $cm);
 }
-echo $OUTPUT->box(format_module_intro('scorm', $scorm, $cm->id).$attemptstatus, 'generalbox boxaligncenter boxwidthwide', 'intro');
+echo $OUTPUT->box(format_module_intro('scorm', $scorm, $cm->id).$attemptstatus, 'container', 'intro');
 
 // Check if SCORM available.
 list($available, $warnings) = scorm_get_availability_status($scorm);
 if (!$available) {
     $reason = current(array_keys($warnings));
-    echo $OUTPUT->box(get_string($reason, "scorm", $warnings[$reason]), "generalbox boxaligncenter");
+    echo $OUTPUT->box(get_string($reason, "scorm", $warnings[$reason]), "container");
 }
 
 if ($available && empty($launch)) {
     scorm_print_launch($USER, $scorm, 'view.php?id='.$cm->id, $cm);
 }
 if (!empty($forcejs)) {
-    echo $OUTPUT->box(get_string("forcejavascriptmessage", "scorm"), "generalbox boxaligncenter forcejavascriptmessage");
+    echo $OUTPUT->box(get_string("forcejavascriptmessage", "scorm"), "container forcejavascriptmessage");
 }
 
 if (!empty($scorm->popup)) {
index 36b618d..38a6e7e 100644 (file)
@@ -627,11 +627,11 @@ function survey_print_single($question) {
 
 
     if ($question->type == 0) {           // Plain text field
-        echo "<textarea rows=\"3\" cols=\"30\" name=\"q$question->id\" id=\"q$question->id\">$question->options</textarea>";
+        echo "<textarea rows=\"3\" cols=\"30\" class=\"form-control\" name=\"q$question->id\" id=\"q$question->id\">$question->options</textarea>";
 
     } else if ($question->type > 0) {     // Choose one of a number
         $strchoose = get_string("choose");
-        echo "<select name=\"q$question->id\" id=\"q$question->id\">";
+        echo "<select name=\"q$question->id\" id=\"q$question->id\" class=\"custom-select\">";
         echo "<option value=\"0\" selected=\"selected\">$strchoose...</option>";
         $options = explode( ",", $question->options);
         foreach ($options as $key => $val) {
index 0ca661d..7c147cd 100644 (file)
          echo "<form action=\"report.php\" method=\"post\">";
          echo "<h3>$strnotes:</h3>";
          echo "<blockquote>";
-         echo "<textarea name=\"notes\" rows=\"10\" cols=\"60\">";
+         echo "<textarea class=\"form-control\" name=\"notes\" rows=\"10\" cols=\"60\">";
          p($notes);
          echo "</textarea><br />";
          echo "<input type=\"hidden\" name=\"action\" value=\"student\" />";
          echo "<input type=\"hidden\" name=\"sesskey\" value=\"".sesskey()."\" />";
          echo "<input type=\"hidden\" name=\"student\" value=\"$student\" />";
          echo "<input type=\"hidden\" name=\"id\" value=\"$cm->id\" />";
-         echo "<input type=\"submit\" value=\"".get_string("savechanges")."\" />";
+         echo "<input type=\"submit\" class=\"btn btn-primary\" value=\"".get_string("savechanges")."\" />";
          echo "</blockquote>";
          echo "</form>";
          echo "</div>";
index 2932fb5..7336415 100644 (file)
@@ -191,7 +191,7 @@ $PAGE->requires->string_for_js('questionsnotanswered', 'survey');
 $PAGE->requires->js_init_call('M.mod_survey.init', $checkarray, true, $module);
 
 echo '<br />';
-echo '<input type="submit" value="'.get_string("clicktocontinue", "survey").'" />';
+echo '<input type="submit" class="btn btn-primary" value="'.get_string("clicktocontinue", "survey").'" />';
 echo '</div>';
 echo "</form>";
 
index a361b24..740a041 100644 (file)
@@ -315,7 +315,7 @@ class mod_workshop_portfolio_caller extends portfolio_module_caller_base {
             $output .= $content;
         }
 
-        return $output;
+        return html_writer::div($output);
     }
 
     /**
index e38fe9b..48da10b 100644 (file)
@@ -558,7 +558,7 @@ class core_renderer extends \core_renderer {
 
                     // We only add a list to the full settings menu if we didn't include every node in the short menu.
                     if ($skipped) {
-                        $text = get_string('frontpagesettings');
+                        $text = get_string('morenavigationlinks');
                         $url = new moodle_url('/course/admin.php', array('courseid' => $this->page->course->id));
                         $link = new action_link($url, $text, null, null, new pix_icon('t/edit', $text));
                         $menu->add_secondary_action($link);
@@ -572,7 +572,7 @@ class core_renderer extends \core_renderer {
 
                     // We only add a list to the full settings menu if we didn't include every node in the short menu.
                     if ($skipped) {
-                        $text = get_string('courseadministration');
+                        $text = get_string('morenavigationlinks');
                         $url = new moodle_url('/course/admin.php', array('courseid' => $this->page->course->id));
                         $link = new action_link($url, $text, null, null, new pix_icon('t/edit', $text));
                         $menu->add_secondary_action($link);
@@ -619,7 +619,7 @@ class core_renderer extends \core_renderer {
                 $navbarnode = end($items);
                 // We only want to show the menu on the first page of the activity. This means
                 // the breadcrumb has no additional nodes.
-                if ($navbarnode->key == $node->key && $navbarnode->type == $node->type) {
+                if ($navbarnode && ($navbarnode->key == $node->key && $navbarnode->type == $node->type)) {
                     $buildmenu = true;
                 }
             }
@@ -631,6 +631,18 @@ class core_renderer extends \core_renderer {
                     $this->build_action_menu_from_navigation($menu, $node);
                 }
             }
+        } else {
+            $items = $this->page->navbar->get_items();
+            $navbarnode = end($items);
+
+            if ($navbarnode && ($navbarnode->key == 'participants')) {
+                $node = $this->page->settingsnav->find('users', navigation_node::TYPE_CONTAINER);
+                if ($node) {
+                    // Build an action menu based on the visible nodes from this navigation tree.
+                    $this->build_action_menu_from_navigation($menu, $node);
+                }
+
+            }
         }
         return $this->render($menu);
     }
index 39bece5..c4ccf7f 100644 (file)
 defined('MOODLE_INTERNAL') || die();
 
 $bodyattributes = $OUTPUT->body_attributes([]);
-$regionmainsettingsmenu = $OUTPUT->region_main_settings_menu();
 
 $templatecontext = [
     'sitename' => format_string($SITE->shortname, true, array('context' => context_course::instance(SITEID))),
     'output' => $OUTPUT,
-    'bodyattributes' => $bodyattributes,
-    'regionmainsettingsmenu' => $regionmainsettingsmenu,
-    'hasregionmainsettingsmenu' => !empty($regionmainsettingsmenu)
+    'bodyattributes' => $bodyattributes
 ];
 
 echo $OUTPUT->render_from_template('theme_boost/columns1', $templatecontext);
index c702c1b..30b65f3 100644 (file)
     text-align: left;
 }
 
-#notice {
-    width: 60%;
-    min-width: 220px;
-    margin: auto;
-    text-align: center;
-
-    .buttons {
-        padding: 0;
-    }
-}
-
 #page-admin-index .releasenoteslink,
 #page-admin-index .adminwarning,
 #page-admin-index .adminerror {
 #adminsettings .form-overridden {
     @extend .form-control-feedback;
 }
+
+.modal.modal-in-page {
+    z-index: 0;
+}
index b0f6084..efed01d 100644 (file)
 .path-backup .mform {
-    fieldset {
-        margin-left: 0;
-    }
-
     .grouped_settings {
         clear: both;
         overflow: hidden;
 
+        /* Use card styles but avoid extend because that brings in too much. */
         &.section_level {
-            @extend .card;
-            @extend .card-block;
+            background-color: $card-bg;
+            @include border-radius($card-border-radius);
+            border: $card-border-width solid $card-border-color;
+            @include clearfix;
+            padding: $card-spacer-x;
+            margin-bottom: $card-spacer-x;
         }
     }
-}
 
-.path-backup {
-    .include_setting,
-    .grouped_settings .normal_setting {
+    /* These are long labels with checkboxes on the right. */
+    .include_setting {
+        width: 50%;
         display: inline-block;
+        float: left;
+        padding: $table-sm-cell-padding;
+    }
+    .normal_setting {
         width: 50%;
-
-        .form-group {
-            margin-top: 0;
-            margin-bottom: 0;
-        }
+        display: inline-block;
+        float: left;
+        padding: $table-sm-cell-padding;
     }
+}
 
-    .include_setting.section_level label {
+.path-backup {
+    /* Bold section labels */
+    .section_level {
         font-weight: bold;
     }
+    .section_level .activity_level {
+        font-weight: normal;
+    }
+
+    .proceedbutton {
+        float: right;
+    }
 }
 
-@include media-breakpoint-up(md) {
-    /* Override the columns width to leave more room for the labels. */
-    .path-backup .mform {
-        .grouped_settings {
-            .form-group {
-                .col-md-3 {
-                    width: 75%;
-                }
-                .col-md-9 {
-                    width: 25%;
-                }
+/* Override the columns width to leave more room for the labels. */
+.path-backup .mform {
+    .root_setting,
+    .grouped_settings {
+        /* Striped rows like a table */
+        &:nth-of-type(odd) {
+            background-color: $table-bg-accent;
+        }
+        &:nth-of-type(even) {
+            background-color: $card-bg;
+        }
+        .form-group {
+            /* These checkboxes with no label on the left. */
+            .col-md-3.checkbox {
+                width: 0%;
+            }
+            .col-md-9.checkbox {
+                width: 100%;
+                left: 0;
+            }
+            /* These are labels with a value on the right */
+            .col-md-3 {
+                width: 50%;
+            }
+            .col-md-9 {
+                width: 50%;
+                left: 0;
+            }
+            .icon:first-of-type {
+                margin-left: 1rem;
             }
         }
     }
 }
 
-.path-backup .mform .fitem .fitemtitle {
-    width: 260px;
+/* Detail pair is (usually) some short label with a longer value */
+.path-backup .detail-pair {
+    .detail-pair-label {
+        width: 25%;
+        float: left;
+        clear: left;
+    }
+    .detail-pair-value {
+        width: 75%;
+        float: left;
+    }
+}
+
+.path-backup .backup-restore .singlebutton {
+    float: right;
+}
+/* Make these bits full width and work with the detail-pair */
+.path-backup .backup-section {
+    .sub-header,
+    .backup-sub-section,
+    .singlebutton,
+    .header {
+        width: 100%;
+        float: left;
+        clear: both;
+    }
+    /* Fix for nested table headers */
+    th.header {
+        width: auto;
+        float: none;
+    }
+
+    /* Add card styles to backup sections */
+    ::after {
+        content: "";
+        display: table;
+        clear: both;
+    }
+
+    background-color: $card-bg;
+    @include border-radius($card-border-radius);
+    border: $card-border-width solid $card-border-color;
+    @include clearfix;
+    padding: $card-spacer-x;
+    margin-bottom: $card-spacer-x;
 }
 
 .path-backup .notification.dependencies_enforced {
-    text-align: center;
     color: $state-danger-text;
     font-weight: bold;
 }
 
 .path-backup .backup_progress {
-    text-align: center;
     margin-top: $spacer-y;
     margin-bottom: $spacer-y;
 
     background-color: $state-danger-bg;
 
     .aliaseslist {
-        width: 90%;
-        margin: 0.8em auto;
         background-color: $body-bg;
-        border: 1px dotted #666;
-    }
-}
-
-#page-backup-restorefile h2 + div {
-    margin-bottom: $spacer-y;
-}
-
-.backup-restore {
-    .backup-section,
-    .backup-sub-section {
-        margin-top: $spacer-y;
-    }
-
-    .backup-section {
-        span.error {
-            @extend .alert;
-            @extend .alert-danger;
-            display: inline-block;
-        }
-
-        fieldset.error {
-            border: $border-width solid $state-danger-border;
-        }
-
-        .noticebox {
-            margin: 1em auto;
-            width: 60%;
-            text-align: center;
-        }
-    }
-}
-
-.backup-restore .backup-section.settings-section {
-    .detail-pair {
-        width: 50%;
-        display: inline-block;
-    }
-
-    .detail-pair-label {
-        width: 75%;
-    }
-
-    .detail-pair-value {
-        width: 25%;
     }
 }
 
-.backup-restore .activitytable {
-    min-width: 500px;
-
-    .modulename {
-        width: 100px;
-    }
-
-    .moduleincluded {
-        width: 50px;
-    }
-
-    .userinfoincluded {
-        width: 50px;
-    }
-}
-
-.backup-restore .detail-pair-label {
-    display: inline-block;
-    width: 25%;
-    padding: 8px;
-    margin: 0;
-    text-align: right;
-    font-weight: bold;
-    vertical-align: top;
-}
-
-.backup-restore .detail-pair-value {
-    display: inline-block;
-    width: 65%;
-    padding: 8px;
-    margin: 0;
-
-    > .sub-detail {
-        display: block;
-        font-size: $font-size-sm;
-        @extend .text-muted;
-    }
-}
-
-.backup-restore > .singlebutton {
-    float: right;
-}
-
-.path-backup .mform {
-    .proceedbutton,
-    .oneclickbackup {
-        float: right;
-    }
-}
-
-.restore-course-search .rcs-results {
-    width: 70%;
-    min-width: 400px;
-
-    table {
-        width: 100%;
-        margin: 0;
-        border-width: 0;
-
-        .no-overflow {
-            max-width: 600px;
-        }
-    }
-
-    .paging {
-        text-align: left;
-        margin: 0;
-        background-color: #eee;
-        padding: 3px;
-    }
-}
-
-.restore-course-category .rcs-results {
-    width: 70%;
-    min-width: 400px;
-    border: 1px solid #ddd;
-    margin: 5px 0;
-
-    table {
-        width: 100%;
-        margin: 0;
-        border-width: 0;
-        .no-overflow {
-            max-width: 600px;
-        }
-    }
-
-    .paging {
-        text-align: left;
-        margin: 0;
-        background-color: #eee;
-        padding: 3px;
-    }
-}
 
 .path-backup .wibbler {
     width: 500px;
     }
 }
 
-.path-backup .backup_log {
-    margin-top: 2em;
-
-    h2 {
-        font-size: 1em;
-    }
-}
-
-.path-backup .backup_log_contents {
-    border: $border-width solid $card-border-color;
-    padding: $spacer;
-    height: 300px;
-    overflow-y: scroll;
-}
index 90af3b9..9f7c1a8 100644 (file)
@@ -24,9 +24,8 @@ p.arrow_button {
 }
 
 #addcontrols {
-    // The margin top is equal the combination of the line-height and margin of a <p>,
-    // because the visual parent is a <p>.
-    margin-top: $line-height-base + ($line-height-base / 2);
+    // This is displayed in a column between 2 20 row multi-selects. This should be just short of half way.
+    margin-top: 8 * $line-height-base * $font-size-base;
     text-align: center;
     margin-bottom: 3em;
 
@@ -47,21 +46,3 @@ p.arrow_button {
     margin: 0 0 10px 5px;
 }
 
-input {
-    &.fp-btn-choose {
-        @extend .btn-sm;
-    }
-}
-
-.user-enroller-panel {
-    .uep-search-results {
-        .user,
-        .cohort {
-            .options {
-                .enrol {
-                    @extend .btn-sm;
-                }
-            }
-        }
-    }
-}
index 11f547d..3d4ea5a 100644 (file)
@@ -14,6 +14,7 @@
     .yui-layout-unit-right,
     .yui-layout-unit-bottom {
         @extend .card;
+        @extend .card-block;
         border-radius: 0;
     }
 
         border: 0;
         padding: 3px 15px;
         white-space: nowrap;
-
-        input {
-            margin: 0 10px;
-
-            &#input-message {
-                width: 45%;
-                margin: auto;
-            }
-        }
-
-        a {
-            margin: 0 5px;
-        }
     }
 
     #chat-userlist {
         }
 
         .chat-message.course-theme {
-            background-color: $body-bg;
-            border: 1px dotted #ddd;
-
-            @include border-radius(4px);
-            padding: 4px 10px;
-            margin: 10px 0;
+            @extend .card;
+            @extend .card-block;
+            background-color: $card-bg !important; /* stylelint-disable declaration-no-important */
 
             .time {
                 float: right;
index 100ae5f..bfe738f 100644 (file)
@@ -131,6 +131,7 @@ a.autolink.glossary:hover {
 /* Block which is hidden if javascript enabled, prevents fickering visible when JS from footer used! */
 .collapsibleregioncaption {
     white-space: nowrap;
+    min-height: $line-height-base * $font-size-base;
 }
 
 .pagelayout-mydashboard.jsenabled .collapsibleregioncaption {
@@ -155,6 +156,7 @@ a.autolink.glossary:hover {
 
 .jsenabled .collapsibleregion {
     overflow: hidden;
+    box-sizing: content-box;
 }
 
 .jsenabled .collapsed .collapsibleregioninner {
@@ -1084,14 +1086,6 @@ tr.flagged-tag a {
         @extend .card;
         @extend .card-block;
     }
-
-    .user-enroller-panel .uep-search-results .user .details {
-        width: 237px;
-    }
-
-    .user-enroller-panel .uep-search-results .cohort .details {
-        width: 237px;
-    }
 }
 
 #page-enrol-users .enrol-users-page-action input {
@@ -1340,6 +1334,9 @@ body#page-lib-editor-tinymce-plugins-moodlemedia-preview {
 .moodle-dialogue-confirm .confirmation-message {
     margin: 0.5em 1em;
 }
+.moodle-dialogue-confirm .confirmation-buttons {
+    text-align: right;
+}
 
 .moodle-dialogue-confirm .confirmation-dialogue input {
     min-width: 80px;
@@ -1434,7 +1431,7 @@ body#page-lib-editor-tinymce-plugins-moodlemedia-preview {
 /* Center the submit buttons within the area */
 .choosercontainer #chooseform .submitbuttons {
     padding: 0.7em 0;
-    text-align: center;
+    text-align: right;
 }
 /* Fixed for safari browser on iPhone4S with ios7@mixin */
 
index db0033f..7662edc 100644 (file)
     }
 }
 
+.path-course-view.editing #region-main > .card-block {
+    padding-bottom: 13rem;
+}
+
 .path-course-view .completionprogress {
     margin-left: 25px;
 }
index fc97402..bcf5451 100644 (file)
@@ -32,8 +32,6 @@ table#listdirectories {
     @extend .table-sm;
 }
 
-.user-enroller-panel .uep-search-results .users,
-.user-enroller-panel .uep-search-results .cohorts,
 table.grading-report,
 .forumheaderlist,
 .generaltable,
index a34fc52..3d07b0b 100644 (file)
@@ -118,11 +118,6 @@ div.backup-section + form,
     padding-left: 0;
 }
 
-.path-admin .buttons,
-.form-buttons { // Add back the padding it would usually get from being inside a .form-horizontal.
-    padding-left: $display1-size;
-}
-
 .form-item .form-setting .defaultsnext > input {
     display: inline-block;
 }
@@ -301,9 +296,15 @@ fieldset.coursesearchbox label {
     font-size: large;
 }
 
+/* Non-bootstrap selects with a size show their contents outside of the element.
+ * Remove when we update to stable bootstrap 4. (MDL-56511) */
+select[size],
 select[multiple] {
     overflow: auto;
 }
+select[size=1] {
+    overflow: visible;
+}
 
 textarea[data-auto-rows] {
     overflow-x: hidden;
index 1ec5dc3..e7f3141 100644 (file)
@@ -12,6 +12,7 @@ select {
 .path-mod-feedback .feedback_form .col-form-label {
     display: block !important; /* stylelint-disable-line declaration-no-important */
 }
+
 // Feedback module
 .path-mod-feedback .itemactions {
     float: right;
@@ -148,6 +149,10 @@ select {
 #page-mod-forum-discuss .discussioncontrols {
     width: auto;
     margin: 0;
+
+    .form-inline input {
+        margin-top: -1px;
+    }
 }
 
 .maincalendar .calendarmonth td,
@@ -208,21 +213,6 @@ div#dock {
 
 // Choice module
 
-.path-mod-choice {
-    .horizontal  .choices {
-        margin: 0;
-
-        .option {
-            display: inline-block;
-            padding: 10px;
-        }
-    }
-
-    .results .data {
-        white-space: normal;
-    }
-}
-
 // Lesson module
 
 /** General styles (scope: all of lesson) **/
@@ -262,13 +252,6 @@ div#dock {
         }
     }
 
-    .resultgraph,
-    .reportsummary,
-    .studentreport,
-    .reportbuttons,
-    .centerpara {
-        text-align: center;
-    }
 }
 
 .nav .caret {
index 1d490ad..9556e7b 100644 (file)
@@ -163,3 +163,9 @@ body:not(.jsenabled) .langmenu:hover > .dropdown-menu,
 ol {
     margin: 0 0 $line-height-base / 2 2.5em;
 }
+
+// Set menus in the fixed header to scroll vertically when they are longer than the page.
+.navbar-static-top .dropdown .dropdown-menu {
+    max-height: calc(100vh - #{$navbar-height});
+    overflow-y: auto;
+}
index 92c3bc2..7554f4f 100644 (file)
     font-size: 0.8em;
 }
 
-#userselector_options {
-    padding: 0.3em 0;
-}
-
 #userselector_options .collapsibleregioncaption {
     font-weight: bold;
 }
         }
     }
 }
+
+// Remove the little cog from participants page because we are putting a cog menu there.
+.userlist h3 .action-icon {
+    display: none;
+}
+
+#page-enrol-users .popover {
+    max-width: none;
+}
+
+.user-enroller-panel {
+    width: 600px;
+}
index 79fa387..9c6c6da 100644 (file)
     * sitename - The name of the site
     * output - The core renderer for the page
     * bodyattributes - attributes for the body tag as a string of html attributes
-    * regionmainsettingsmenu - HTML for the region main settings menu
-    * hasregionmainsettingsmenu - There is a region main settings menu on this page.
 
     Example context (json):
     {
         "sitename": "Moodle",
         "output": {"doctype": "<!DOCTYPE html>", "page_title": "Test page", "favicon": "favicon.ico"},
-        "bodyattributes":"",
-        "regionmainsettingsmenu": "",
-        "hasregionmainsettingsmenu": false
+        "bodyattributes":""
     }
 }}
 {{{ output.doctype }}}
     <div id="page" class="container-fluid">
         <div id="page-content" class="row">
             <div id="region-main-box" class="col-xs-12">
-                {{#hasregionmainsettingsmenu}}
-                    <div id="region-main-settings-menu" class="has-blocks">
-                        <div> {{{ regionmainsettingsmenu }}} </div>
-                    </div>
-                {{/hasregionmainsettingsmenu}}
                 <section id="region-main">
                     <div class="card card-block">
-                    {{#hasregionmainsettingsmenu}}
-                        <div class="region_main_settings_menu_proxy"></div>
-                    {{/hasregionmainsettingsmenu}}
                     {{{ output.course_content_header }}}
                     {{{ output.main_content }}}
                     {{{ output.course_content_footer }}}
index 1a424ca..c91bb97 100644 (file)
@@ -1,10 +1,70 @@
+{{!
+    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/navbar
+
+    Navbar template.
+
+    Classes required for JS:
+    * none
+
+    Data attributes required for JS:
+    * none
+
+    Context variables required for this template:
+    * get_items - array of items
+      * has_action - boolean
+      * action - string
+      * get_title - string
+      * text - HTML
+
+    Example context (json):
+    {
+        "get_items": [
+            {
+                "has_action": true,
+                "action": "#",
+                "get_title": "Test title",
+                "text": "First & fresh"
+            },
+            {
+                "has_action": true,
+                "action": "#",
+                "get_title": "Second item & a title",
+                "text": "Second item"
+            },
+            {
+                "has_action": false,
+                "text": "Third item"
+            },
+            {
+                "has_action": false,
+                "text": "Fourth & last"
+            }
+        ]
+    }
+}}
 <ol class="breadcrumb" role="navigation">
     {{#get_items}}
         {{#has_action}}
-        <li class="breadcrumb-item"><a href="{{{action}}}" {{#get_title}}title="{{get_title}}"{{/get_title}}>{{text}}</a></li>
+            <li class="breadcrumb-item"><a href="{{{action}}}" {{#get_title}}title="{{get_title}}"{{/get_title}}>{{{text}}}</a></li>
         {{/has_action}}
         {{^has_action}}
-        <li class="breadcrumb-item">{{text}}</li>
+            <li class="breadcrumb-item">{{{text}}}</li>
         {{/has_action}}
     {{/get_items}}
 </ol>
index c2ff816..265cc1d 100644 (file)
@@ -1,9 +1,56 @@
+{{!
+    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/single_button
+
+    Moodle template for a single button submit form.
+
+    Classes required for JS:
+    * none
+
+    Data attributes required for JS:
+    * none
+
+    Context variables required for this template:
+    * classes - a list of classes to wrap the form.
+    * method - get or post
+    * url - the action url to submit to
+    * formid - optional id value for the form
+    * params - array of params with name and value attributes
+    * primary - true if this is a primary action button
+    * id - id for the element
+    * tooltip - tooltip text for the button
+    * disabled - true if this element is disabled
+    * label - text to show on the button
+
+    Example context (json):
+    { "method" : "get",
+      "url" : "#",
+      "primary" : true,
+      "tooltip" : "This is a tooltip",
+      "label" : "This is a the button text"
+    }
+}}
 <div class="{{classes}}">
     <form method="{{method}}" action="{{url}}" {{#formid}}id="{{formid}}"{{/formid}}>
         {{#params}}
             <input type="hidden" name="{{name}}" value="{{value}}">
         {{/params}}
-        <button type="submit" class="btn btn-secondary"
+        <button type="submit" class="btn {{#primary}}btn-primary{{/primary}}{{^primary}}btn-secondary{{/primary}}"
             id="{{id}}"
             title={{#quote}}{{tooltip}}{{/quote}}
             {{#disabled}}disabled{{/disabled}}>{{label}}</button>
index 3b8d99b..572a98a 100644 (file)
@@ -10,7 +10,7 @@
             {{#helpicon}}
                 {{>core/help_icon}}
             {{/helpicon}}
-            <select {{#attributes}}{{name}}="{{value}}" {{/attributes}} id="{{id}}" class="form-control {{classes}}" name="jump">
+            <select {{#attributes}}{{name}}="{{value}}" {{/attributes}} id="{{id}}" class="custom-select {{classes}}" name="jump">
                 {{#options}}
                     {{#isgroup}}
                         <optgroup label="{{name}}">
index 6266c04..a3c8725 100644 (file)
@@ -1,4 +1,4 @@
-<label class="form-check-inline {{#error}}has-danger{{/error}} fitem {{#advanced}}advanced{{/advanced}} {{{element.extraclasses}}}">
+<label class="form-check {{#error}}has-danger{{/error}} fitem {{#advanced}}advanced{{/advanced}} {{{element.extraclasses}}}">
 {{^element.hardfrozen}}
     {{#element.frozen}}
         <input type="hidden" name="{{element.name}}" value="{{element.frozenvalue}}">
index 3458ad8..fa3b35a 100644 (file)
@@ -1,37 +1,92 @@
+{{!
+    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 theme_boost/flat_navigation
+
+    Display the flat navigation for the boost theme
+
+    Classes required for JS:
+    * none
+
+    Data attributes required for JS:
+    * none
+
+    Context variables required for this template:
+    * flatnavigation - array of flat_navigation_nodes
+      * showdivider - boolean
+      * action - string
+      * isactive - boolean
+      * get_indent - integer
+      * is_section - boolean
+      * text - HTML
+
+    Example context (json):
+    {
+        "flatnavigation" : [
+            {
+                "showdivider": false,
+                "action": "#",
+                "isactive": true,
+                "get_indent": 1,
+                "is_section": false,
+                "text": "First"
+            },{
+                "showdivider": true,
+                "action": "#",
+                "isactive": false,
+                "get_indent": 0,
+                "is_section": true,
+                "text": "Last &amp; Second"
+            }
+        ]
+    }
+}}
 <nav class="list-group">
 {{# flatnavigation }}
     {{#showdivider}}
 </nav>
 <nav class="list-group m-t-1">
     {{/showdivider}}
-    {{#get_smscreenonly}}
-    <div class="hidden-lg-up">
-    {{/get_smscreenonly}}
     {{#action}}
-    <a class="list-group-item list-group-item-action {{#isactive}}font-weight-bold{{/isactive}}" href="{{{action}}}"><span class="m-l-{{get_indent}}">
-        {{#is_section}}
-            <div class="media">
-                <span class="media-left">
-                    {{#pix}}i/folder{{/pix}}
-                </span>
-                <span class="media-body">{{text}}</span>
-            </div>
-        {{/is_section}}
-        {{^is_section}}
-            {{text}}
-        {{/is_section}}
-    </span></a>
+    <a class="list-group-item list-group-item-action {{#isactive}}font-weight-bold{{/isactive}}" href="{{{action}}}">
+        <div class="m-l-{{get_indent}}">
+            {{#is_section}}
+                <div class="media">
+                    <span class="media-left">
+                        {{#pix}}i/folder{{/pix}}
+                    </span>
+                    <span class="media-body">{{{text}}}</span>
+                </div>
+            {{/is_section}}
+            {{^is_section}}
+                {{{text}}}
+            {{/is_section}}
+        </div>
+    </a>
     {{/action}}
     {{^action}}
-    <div class="list-group-item"><span class="m-l-{{get_indent}}">
-        {{#is_section}}
-            {{#pix}}i/folder{{/pix}}
-        {{/is_section}}
-        {{text}}
-    </span></div>
-    {{/action}}
-    {{#get_smscreenonly}}
+    <div class="list-group-item">
+        <div class="m-l-{{get_indent}}">
+            {{#is_section}}
+                {{#pix}}i/folder{{/pix}}
+            {{/is_section}}
+            {{{text}}}
+        </div>
     </div>
-    {{/get_smscreenonly}}
+    {{/action}}
 {{/ flatnavigation }}
 </nav>
index d0c2ed3..3fe8f59 100644 (file)
@@ -200,12 +200,11 @@ img.iconsmall {
     text-align: left;
 }
 
-#notice {
-    // Would like the use the alert stuff below for this,
-    // but the way buttons are used makes it tricky.
-    width: 60%;
-    min-width: 220px;
-    margin: auto;
+// Allow in-page modals like bootstrap 4.
+.modal.modal-in-page {
+    position: static;
+    z-index: 0;
+    margin: 0;
 }
 
 #page-admin-index .releasenoteslink,
index 5f7ee4b..326f417 100644 (file)
@@ -113,21 +113,3 @@ button {
     }
 }
 
-input {
-    &.fp-btn-choose {
-        .btn-small
-    }
-}
-
-.user-enroller-panel {
-    .uep-search-results {
-        .user,
-        .cohort {
-            .options {
-                .enrol {
-                    .btn-mini
-                }
-            }
-        }
-    }
-}
index 3514265..130c9f9 100644 (file)
@@ -291,15 +291,9 @@ div#dock {
 // Choice module
 
 .path-mod-choice {
-    .horizontal  .choices {
-        margin: 0;
-        .option {
-            display: inline-block;
-            padding: 10px;
-        }
-    }
-    .results .data {
-        white-space: normal;
+    .media-left {
+        float: left;
+        padding-right: 1em;
     }
 }
 
@@ -917,3 +911,17 @@ div#dock {
         background-color: @grayLight;
     }
 }
+
+// Scorm.
+.path-mod-scorm .scorm-center {
+    text-align: center;
+}
+
+.path-mod-scorm.forcejavascript .scorm-center {
+    display: none;
+}
+
+.path-mod-scorm.jsenabled .scorm-center {
+    display: block;
+}
+
index 4c02691..1736f90 100644 (file)
@@ -2786,10 +2786,10 @@ img.iconsmall {
   margin: 1em 10% 2em 10%;
   text-align: left;
 }
-#notice {
-  width: 60%;
-  min-width: 220px;
-  margin: auto;
+.modal.modal-in-page {
+  position: static;
+  z-index: 0;
+  margin: 0;
 }
 #page-admin-index .releasenoteslink,
 #page-admin-index .adminwarning,
@@ -14704,15 +14704,9 @@ canvas {
 div#dock {
   display: none;
 }
-.path-mod-choice .horizontal .choices {
-  margin: 0;
-}
-.path-mod-choice .horizontal .choices .option {
-  display: inline-block;
-  padding: 10px;
-}
-.path-mod-choice .results .data {
-  white-space: normal;
+.path-mod-choice .media-left {
+  float: left;
+  padding-right: 1em;
 }
 .path-mod-lesson .firstpageoptions {
   margin: auto;
@@ -15209,6 +15203,15 @@ div#dock {
 .path-mod-quiz #mod_quiz_navblock .qnbutton.requiresgrading .trafficlight {
   background-color: #999;
 }
+.path-mod-scorm .scorm-center {
+  text-align: center;
+}
+.path-mod-scorm.forcejavascript .scorm-center {
+  display: none;
+}
+.path-mod-scorm.jsenabled .scorm-center {
+  display: block;
+}
 .yui-skin-sam .yui-layout.path-mod-chat-gui_ajax {
   background-color: #fff;
 }
@@ -15887,27 +15890,6 @@ button.yui3-button.closebutton {
 button.yui3-button.closebutton:hover {
   background-position: 0 0;
 }
-input.fp-btn-choose {
-  padding: 2px 10px;
-  font-size: 11.9px;
-  -webkit-border-radius: 3px;
-  -moz-border-radius: 3px;
-  border-radius: 3px;
-}
-.user-enroller-panel .uep-search-results .user .options .enrol,
-.user-enroller-panel .uep-search-results .cohort .options .enrol {
-  padding: 0 6px;
-  font-size: 10.5px;
-  -webkit-border-radius: 3px;
-  -moz-border-radius: 3px;
-  border-radius: 3px;
-}
-.user-enroller-panel .uep-search-results .user .options .enrol .label,
-.user-enroller-panel .uep-search-results .cohort .options .enrol .label,
-.user-enroller-panel .uep-search-results .user .options .enrol .badge,
-.user-enroller-panel .uep-search-results .cohort .options .enrol .badge {
-  top: 0;
-}
 .gradetreebox h4 {
   font-size: 14px;
 }
index 3a55c42..901e44d 100644 (file)
@@ -3,6 +3,8 @@ information provided here is intended especially for theme designer.
 
 === 3.2 ===
 
+* mod_chat will not display the 'course theme' option for all themes (previously it was only displayed on
+  bootstrap2 based themes).
 * A new theme config 'undeletableblocktypes' allows a theme to define which blocks are deletable.
 * A new core setting now enables admins to upload the logos of their site. Using the
   following methods, themers can instantly support branding logos without the need
index 570b7f9..b8c20c2 100644 (file)
@@ -227,19 +227,19 @@ abstract class user_selector_base {
         }
         $output = '<div class="userselector" id="' . $this->name . '_wrapper">' . "\n" .
                 '<select name="' . $name . '" id="' . $this->name . '" ' .
-                $multiselect . 'size="' . $this->rows . '">' . "\n";
+                $multiselect . 'size="' . $this->rows . '" class="form-control no-overflow">' . "\n";
 
         // Populate the select.
         $output .= $this->output_options($groupedusers, $search);
 
         // Output the search controls.
-        $output .= "</select>\n<div>\n";
+        $output .= "</select>\n<div class=\"form-inline\">\n";
         $output .= '<input type="text" name="' . $this->name . '_searchtext" id="' .
-                $this->name . '_searchtext" size="15" value="' . s($search) . '" />';
+                $this->name . '_searchtext" size="15" value="' . s($search) . '" class="form-control"/>';
         $output .= '<input type="submit" name="' . $this->name . '_searchbutton" id="' .
-                $this->name . '_searchbutton" value="' . $this->search_button_caption() . '" />';
+                $this->name . '_searchbutton" value="' . $this->search_button_caption() . '" class="btn btn-secondary"/>';
         $output .= '<input type="submit" name="' . $this->name . '_clearbutton" id="' .
-                $this->name . '_clearbutton" value="' . get_string('clear') . '" />';
+                $this->name . '_clearbutton" value="' . get_string('clear') . '" class="btn btn-secondary"/>';
 
         // And the search options.
         $optionsoutput = false;
@@ -624,11 +624,14 @@ abstract class user_selector_base {
             $checked = '';
         }
         $name = 'userselector_' . $name;
-        $output = '<p><input type="hidden" name="' . $name . '" value="0" />' .
-                // For the benefit of brain-dead IE, the id must be different from the name of the hidden form field above.
-                // It seems that document.getElementById('frog') in IE will return and element with name="frog".
-                '<input type="checkbox" id="' . $name . 'id" name="' . $name . '" value="1"' . $checked . ' /> ' .
-                '<label for="' . $name . 'id">' . $label . "</label></p>\n";
+        // For the benefit of brain-dead IE, the id must be different from the name of the hidden form field above.
+        // It seems that document.getElementById('frog') in IE will return and element with name="frog".
+        $output = '<div class="form-check"><input type="hidden" name="' . $name . '" value="0" />' .
+                    '<label class="form-check-label" for="' . $name . 'id">' .
+                        '<input class="form-check-input" type="checkbox" id="' . $name . 'id" name="' . $name .
+                            '" value="1"' . $checked . ' /> ' . $label .
+                    "</label>
+                   </div>\n";
         user_preference_allow_ajax_update($name, PARAM_BOOL);
         return $output;
     }
index 061bc04..696e8d8 100644 (file)
@@ -77,7 +77,7 @@ M.core_user.init_user_selector = function (Y, name, hash, extrafields, lastsearc
 
             // Replace the Clear submit button with a clone that is not a submit button.
             var clearbtn = Y.one('#' + this.name + '_clearbutton');
-            this.clearbutton = Y.Node.create('<input type="button" value="' + clearbtn.get('value') + '" />');
+            this.clearbutton = Y.Node.create('<input type="button" value="' + clearbtn.get('value') + '" class="btn btn-secondary m-x-1"/>');
             clearbtn.replace(Y.Node.getDOMNode(this.clearbutton));
             this.clearbutton.set('id', this.name + "_clearbutton");
             this.clearbutton.on('click', this.handle_clear, this);
index 9b214dc..e49f805 100644 (file)
 
 defined('MOODLE_INTERNAL') || die();
 
-$version  = 2016111100.00;              // YYYYMMDD      = weekly release date of this DEV branch.
+$version  = 2016111500.00;              // YYYYMMDD      = weekly release date of this DEV branch.
                                         //         RR    = release increments - 00 in DEV branches.
                                         //           .XX = incremental changes.
 
-$release  = '3.2beta+ (Build: 20161111)'; // Human-friendly version name
+$release  = '3.2beta+ (Build: 20161115)'; // Human-friendly version name
 
 $branch   = '32';                       // This version's branch.
 $maturity = MATURITY_BETA;             // This version's maturity level.