Merge branch 'MDL-56844-master' of git://github.com/damyon/moodle
authorAndrew Nicols <andrew@nicols.co.uk>
Tue, 15 Nov 2016 03:20:48 +0000 (11:20 +0800)
committerDan Poltawski <dan@moodle.com>
Tue, 15 Nov 2016 09:07:38 +0000 (09:07 +0000)
101 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/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/launch.php
auth/email/auth.php
auth/email/classes/external.php
auth/upgrade.txt
backup/util/ui/renderer.php
lang/en/deprecated.txt
lang/en/message.php
lang/en/moodle.php
lib/classes/component.php
lib/classes/plugininfo/theme.php
lib/classes/plugininfo/webservice.php
lib/classes/session/redis.php
lib/deprecatedlib.php
lib/moodlelib.php
lib/navigationlib.php
lib/outputcomponents.php
lib/outputrenderers.php
lib/portfoliolib.php
lib/upgrade.txt
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
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/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/tests/behat/preview.feature [new file with mode: 0644]
mod/quiz/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/filemanager.scss
theme/boost/scss/moodle/forms.scss
theme/boost/scss/moodle/modules.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/bootstrapbase/less/moodle/admin.less
theme/bootstrapbase/less/moodle/buttons.less
theme/bootstrapbase/less/moodle/filemanager.less
theme/bootstrapbase/less/moodle/modules.less
theme/bootstrapbase/style/moodle.css
theme/upgrade.txt
user/selector/lib.php
user/selector/module.js

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 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 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 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 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 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 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 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 f1451d5..ecff98b 100644 (file)
@@ -6134,9 +6134,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 +6152,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 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 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 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 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..6d81300 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 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'));
+
+                        $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'));
+                        $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,
+                });
             });
         }
     };
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 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..cb33500 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,12 +46,6 @@ p.arrow_button {
     margin: 0 0 10px 5px;
 }
 
-input {
-    &.fp-btn-choose {
-        @extend .btn-sm;
-    }
-}
-
 .user-enroller-panel {
     .uep-search-results {
         .user,
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..18a504e 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 {
@@ -1340,6 +1342,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 +1439,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..73c02ed 100644 (file)
     }
 }
 
+.path-course-view #region-main > .card-block {
+    padding-bottom: 13rem;
+}
+
 .path-course-view .completionprogress {
     margin-left: 25px;
 }
index b012aad..9985275 100644 (file)
@@ -607,10 +607,6 @@ a.ygtvspacer:hover {
     background-color: #ebebe4;
 }
 
-.fitem.disabled .fp-btn-choose {
-    @extend .text-muted;
-}
-
 .fitem.disabled .filepicker-filelist .filepicker-filename {
     display: none;
 }
index a34fc52..df3cdcb 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;
 }
index a96f62a..e7f3141 100644 (file)
@@ -149,6 +149,10 @@ select {
 #page-mod-forum-discuss .discussioncontrols {
     width: auto;
     margin: 0;
+
+    .form-inline input {
+        margin-top: -1px;
+    }
 }
 
 .maincalendar .calendarmonth td,
@@ -209,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) **/
@@ -263,13 +252,6 @@ div#dock {
         }
     }
 
-    .resultgraph,
-    .reportsummary,
-    .studentreport,
-    .reportbuttons,
-    .centerpara {
-        text-align: center;
-    }
 }
 
 .nav .caret {
index 92c3bc2..409a539 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;
+}
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 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..de9d52a 100644 (file)
@@ -113,12 +113,6 @@ button {
     }
 }
 
-input {
-    &.fp-btn-choose {
-        .btn-small
-    }
-}
-
 .user-enroller-panel {
     .uep-search-results {
         .user,
index e093669..ac46cc5 100644 (file)
@@ -800,9 +800,6 @@ a.ygtvspacer:hover {
 .fitem.disabled .filemanager-container {
     background-color: #ebebe4;
 }
-.fitem.disabled .fp-btn-choose {
-    .muted
-}
 .fitem.disabled .filepicker-filelist .filepicker-filename {
     display: none;
 }
index a507f87..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;
     }
 }
 
index 41c731b..a0497eb 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,
@@ -5500,9 +5500,6 @@ a.ygtvspacer:hover {
 .fitem.disabled .filemanager-container {
   background-color: #ebebe4;
 }
-.fitem.disabled .fp-btn-choose {
-  color: #999;
-}
 .fitem.disabled .filepicker-filelist .filepicker-filename {
   display: none;
 }
@@ -14704,15 +14701,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;
@@ -15896,13 +15887,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;
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..ffe714c 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;
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);