Merge branch 'master' into install_master
authorAMOS bot <amos@moodle.org>
Wed, 23 Nov 2016 16:06:13 +0000 (00:06 +0800)
committerAMOS bot <amos@moodle.org>
Wed, 23 Nov 2016 16:06:13 +0000 (00:06 +0800)
82 files changed:
admin/tool/mobile/launch.php
admin/tool/usertours/amd/build/tour.min.js
admin/tool/usertours/amd/src/tour.js
admin/tool/usertours/classes/cache.php
admin/tool/usertours/classes/manager.php
admin/tool/usertours/classes/tour.php
admin/tool/usertours/lang/en/tool_usertours.php
admin/tool/usertours/tests/cache_test.php
admin/tool/usertours/tests/tour_test.php
admin/tool/usertours/thirdpartylibs.xml
auth/email/classes/external.php
auth/tests/behat/behat_auth.php
backup/moodle2/restore_stepslib.php
backup/util/dbops/backup_controller_dbops.class.php
blocks/course_summary/block_course_summary.php
blocks/course_summary/tests/behat/block_course_summary_course.feature
blocks/course_summary/tests/behat/block_course_summary_frontpage.feature
completion/tests/behat/behat_completion.php
course/edit_form.php
course/format/lib.php
course/format/topics/lib.php
course/format/upgrade.txt
course/format/weeks/lib.php
course/lib.php
course/renderer.php
course/tests/courseformat_test.php
course/yui/build/moodle-course-categoryexpander/moodle-course-categoryexpander-debug.js
course/yui/build/moodle-course-categoryexpander/moodle-course-categoryexpander-min.js
course/yui/build/moodle-course-categoryexpander/moodle-course-categoryexpander.js
course/yui/src/categoryexpander/js/categoryexpander.js
filter/mediaplugin/styles.css
group/autogroup_form.php
lang/en/admin.php
lib/amd/build/event.min.js
lib/amd/src/event.js
lib/behat/classes/util.php
lib/behat/form_field/behat_form_passwordunmask.php
lib/behat/lib.php
lib/deprecatedlib.php
lib/navigationlib.php
lib/requirejs/moodle-config.js
lib/tests/behat/behat_hooks.php
message/classes/api.php
message/classes/output/messagearea/messages.php
message/externallib.php
message/templates/message_area_messages_area.mustache
message/tests/api_test.php
mod/assign/locallib.php
mod/assign/styles.css
mod/feedback/tests/behat/behat_mod_feedback.php
mod/forum/classes/observer.php
mod/lesson/tests/behat/lesson_user_override.feature
mod/quiz/lib.php
mod/scorm/tests/behat/completion_condition_require_status.feature
mod/wiki/editors/html.php
mod/wiki/editors/wiki_editor.php
mod/wiki/editors/wikieditor.php
mod/wiki/locallib.php
mod/wiki/pagelib.php
mod/wiki/styles.css
repository/skydrive/microsoftliveapi.php
repository/webdav/lib.php
theme/boost/amd/build/loader.min.js
theme/boost/amd/src/loader.js
theme/boost/classes/output/core_renderer.php
theme/boost/layout/secure.php
theme/boost/scss/moodle/admin.scss
theme/boost/scss/moodle/filemanager.scss
theme/boost/scss/moodle/message.scss
theme/boost/scss/moodle/modules.scss
theme/boost/templates/core/navbar.mustache
theme/boost/templates/core_form/element-select.mustache
theme/boost/templates/header-secure.mustache [new file with mode: 0644]
theme/boost/templates/header.mustache
theme/boost/templates/secure.mustache
theme/boost/tests/behat/behat_theme_boost_behat_auth.php [deleted file]
theme/bootstrapbase/less/moodle/core.less
theme/bootstrapbase/less/moodle/forms.less
theme/bootstrapbase/less/moodle/message.less
theme/bootstrapbase/less/moodle/modules.less
theme/bootstrapbase/style/moodle.css
version.php

index 7600862..a5a7154 100644 (file)
@@ -94,7 +94,8 @@ $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));
+    $params = array('service' => $serviceshortname, 'passport' => $passport, 'urlscheme' => $urlscheme, 'confirmed' => $confirmed);
+    $PAGE->set_url("/$CFG->admin/tool/mobile/launch.php", $params);
 
     echo $OUTPUT->header();
     if ($confirmed) {
index 20f5d4f..eaeb288 100644 (file)
Binary files a/admin/tool/usertours/amd/build/tour.min.js and b/admin/tool/usertours/amd/build/tour.min.js differ
index 3a77250..d09a505 100644 (file)
@@ -37,6 +37,20 @@ function Tour(config) {
  */
 Tour.prototype.tourName;
 
+/**
+ * The name of the tour storage key.
+ *
+ * @property    {String}    storageKey
+ */
+Tour.prototype.storageKey;
+
+/**
+ * The session storage object
+ *
+ * @property    {Storage}   storage
+ */
+Tour.prototype.storage;
+
 /**
  * The original configuration as passed into the constructor.
  *
@@ -120,6 +134,14 @@ Tour.prototype.init = function (config) {
     // Apply configuration.
     this.configure.apply(this, arguments);
 
+    try {
+        this.storage = window.sessionStorage;
+        this.storageKey = 'tourstate_' + this.tourName;
+    } catch (e) {
+        this.storage = false;
+        this.storageKey = '';
+    }
+
     return this;
 };
 
@@ -280,6 +302,15 @@ Tour.prototype.getCurrentStepNumber = function () {
  */
 Tour.prototype.setCurrentStepNumber = function (stepNumber) {
     this.currentStepNumber = stepNumber;
+    if (this.storage) {
+        try {
+            this.storage.setItem(this.storageKey, stepNumber);
+        } catch (e) {
+            if (e.code === DOMException.QUOTA_EXCEEDED_ERR) {
+                this.storage.removeItem(this.storageKey);
+            }
+        }
+    }
 };
 
 /**
@@ -957,6 +988,16 @@ Tour.prototype.handleKeyDown = function (e) {
  * @chainable
  */
 Tour.prototype.startTour = function (startAt) {
+    if (this.storage && typeof startAt === 'undefined') {
+        var storageStartValue = this.storage.getItem(this.storageKey);
+        if (storageStartValue) {
+            var storageStartAt = parseInt(storageStartValue, 10);
+            if (storageStartAt <= this.steps.length) {
+                startAt = storageStartAt;
+            }
+        }
+    }
+
     if (typeof startAt === 'undefined') {
         startAt = this.getCurrentStepNumber();
     }
index cefc779..71857e4 100644 (file)
@@ -76,15 +76,22 @@ EOF;
     /**
      * Fetch all enabled tours matching the specified target.
      *
-     * @param   string      $targetmatch    The URL to match.
+     * @param   moodle_url  $targetmatch    The URL to match.
      */
-    public static function get_matching_tourdata($targetmatch) {
+    public static function get_matching_tourdata(\moodle_url $targetmatch) {
         $tours = self::get_enabled_tourdata();
 
-        return array_filter($tours, function($tour) use ($targetmatch) {
+        // Attempt to determine whether this is the front page.
+        // This is a special case because the frontpage uses a shortened page path making it difficult to detect exactly.
+        $isfrontpage = $targetmatch->compare(new \moodle_url('/'), URL_MATCH_BASE);
+        $target = $targetmatch->out_as_local_url();
+        return array_filter($tours, function($tour) use ($isfrontpage, $target) {
+            if ($isfrontpage && $tour->pathmatch === 'FRONTPAGE') {
+                return true;
+            }
             $pattern = preg_quote($tour->pathmatch, '@');
             $pattern = str_replace('%', '.*', $pattern);
-            return !!preg_match("@{$pattern}@", $targetmatch);
+            return !!preg_match("@{$pattern}@", $target);
         });
     }
 
index af94d76..f4ffa04 100644 (file)
@@ -559,7 +559,7 @@ class manager {
     public static function get_matching_tours(\moodle_url $pageurl) {
         global $PAGE;
 
-        $tours = cache::get_matching_tourdata($pageurl->out_as_local_url());
+        $tours = cache::get_matching_tourdata($pageurl);
 
         foreach ($tours as $record) {
             $tour = tour::load_from_record($record);
index 473a077..5717e24 100644 (file)
@@ -608,6 +608,8 @@ class tour {
      * This is used in the session cookie to determine whether the user has seen this tour before.
      */
     public function get_tour_key() {
+        global $USER;
+
         $tourtime = $this->get_config('majorupdatetime', null);
 
         if ($tourtime === null) {
@@ -622,7 +624,7 @@ class tour {
             $tourtime = max($tourtime, $userresetdate);
         }
 
-        return sprintf('tool_usertours_%d_%s', $this->get_id(), $tourtime);
+        return sprintf('tool_usertours_%d_%d_%s', $USER->id, $this->get_id(), $tourtime);
     }
 
     /**
index 32cd9b4..87508d5 100644 (file)
@@ -79,7 +79,10 @@ Some example values include:
 * /my/% - to match the Dashboard
 * /course/view.php?id=2 - to match a specific course
 * /mod/forum/view.php% - to match the forum discussion list
-* /user/profile.php% - to match the user profile page';
+* /user/profile.php% - to match the user profile page
+
+If you wish to display a tour on the Site Home page, you can use the value: "FRONTPAGE".
+';
 $string['placement'] = 'Placement';
 $string['pluginname'] = 'User tours';
 $string['resettouronpage'] = 'Reset user tour on this page';
index fdbdde0..4e0c2b1 100644 (file)
@@ -150,6 +150,14 @@ class cache_testcase extends advanced_testcase {
                 'name' => 'my_glob_2',
                 'pathmatch' => '/my/%'
             ],
+            (object) [
+                'name' => 'frontpage_only',
+                'pathmatch' => 'FRONTPAGE'
+            ],
+            (object) [
+                'name' => 'frontpage_match',
+                'pathmatch' => '/?%'
+            ],
         ];
 
         return [
@@ -163,6 +171,16 @@ class cache_testcase extends advanced_testcase {
                 '/my/view.php',
                 ['my_exact_1', 'my_glob_1', 'my_glob_2'],
             ],
+            'Special constant FRONTPAGE must match front page only' => [
+                $tourconfigs,
+                '/',
+                ['frontpage_only'],
+            ],
+            'Standard frontpage URL matches both the special constant, and a correctly formed pathmatch' => [
+                $tourconfigs,
+                '/?redirect=0',
+                ['frontpage_only', 'frontpage_match'],
+            ],
         ];
     }
 
@@ -181,7 +199,7 @@ class cache_testcase extends advanced_testcase {
             $this->helper_create_step((object) ['tourid' => $tour->get_id()]);
         }
 
-        $matches = \tool_usertours\cache::get_matching_tourdata((new moodle_url($targetmatch))->out_as_local_url());
+        $matches = \tool_usertours\cache::get_matching_tourdata(new moodle_url($targetmatch));
         $this->assertCount(count($expected), $matches);
 
         for ($i = 0; $i < count($matches); $i++) {
index 75a6494..d08a9df 100644 (file)
@@ -729,7 +729,7 @@ class tour_testcase extends advanced_testcase {
                     $this->greaterThanOrEqual($time),
                     true,
                     null,
-                    sprintf('tool_usertours_%d_%s', $id, $time),
+                    sprintf('tool_usertours_\d_%d_%s', $id, $time),
                 ],
 
             'Initial tour time, no user pref' => [
@@ -738,7 +738,7 @@ class tour_testcase extends advanced_testcase {
                     null,
                     false,
                     null,
-                    sprintf('tool_usertours_%d_%s', $id, $time),
+                    sprintf('tool_usertours_\d_%d_%s', $id, $time),
                 ],
             'Initial tour time, with user reset lower' => [
                     $id,
@@ -746,7 +746,7 @@ class tour_testcase extends advanced_testcase {
                     null,
                     false,
                     $time - DAYSECS,
-                    sprintf('tool_usertours_%d_%s', $id, $time),
+                    sprintf('tool_usertours_\d_%d_%s', $id, $time),
                 ],
             'Initial tour time, with user reset higher' => [
                     $id,
@@ -754,7 +754,7 @@ class tour_testcase extends advanced_testcase {
                     null,
                     false,
                     $time + DAYSECS,
-                    sprintf('tool_usertours_%d_%s', $id, $time + DAYSECS),
+                    sprintf('tool_usertours_\d_%d_%s', $id, $time + DAYSECS),
                 ],
         ];
     }
@@ -823,8 +823,8 @@ class tour_testcase extends advanced_testcase {
             set_user_preference(\tool_usertours\tour::TOUR_REQUESTED_BY_USER . $id, $userpref);
         }
 
-        $this->assertEquals(
-                $expectation,
+        $this->assertRegExp(
+                '/' . $expectation . '/',
                 $tour->get_tour_key()
             );
     }
index 3904052..0d845ab 100644 (file)
@@ -4,7 +4,7 @@
     <location>amd/src/tour.js</location>
     <name>Flexitour</name>
     <license>GPLv3</license>
-    <version>0.9.9</version>
+    <version>0.9.10</version>
     <licenseversion>3</licenseversion>
   </library>
   <library>
index c7bfde7..1f4f7cf 100644 (file)
@@ -203,7 +203,8 @@ class auth_email_external extends external_api {
                         )
                     ), '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, ''),
+                'redirect' => new external_value(PARAM_LOCALURL, 'Redirect the user to this site url after confirmation.',
+                                                    VALUE_DEFAULT, ''),
             )
         );
     }
@@ -221,7 +222,7 @@ 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
+     * @param  string $redirect               Site url to redirect the user after confirmation
      * @return array settings and possible warnings
      * @since Moodle 3.2
      * @throws moodle_exception
index d5ddb59..685503c 100644 (file)
@@ -61,18 +61,7 @@ class behat_auth extends behat_base {
      * @Given /^I log out$/
      */
     public function i_log_out() {
-        // There is no longer any need to worry about whether the navigation
-        // bar needs to be expanded; user_menu now lives outside the
-        // hamburger.
-
-        // However, the user menu *always* needs to be expanded. if running JS.
-        if ($this->running_javascript()) {
-            $xpath = "//div[@class='usermenu']//a[contains(concat(' ', @class, ' '), ' toggle-display ')]";
-
-            $this->execute('behat_general::i_click_on', array($xpath, "xpath_element"));
-        }
-
-        // No need to check for exceptions as it will checked after this step execution.
-        $this->execute('behat_general::click_link', get_string('logout'));
+        // Click on logout link in footer, as it's much faster.
+        $this->execute('behat_general::i_click_on_in_the', array(get_string('logout'), 'link', '#page-footer', "css_element"));
     }
 }
index d303ba1..e07b50f 100644 (file)
@@ -4331,6 +4331,14 @@ class restore_create_categories_and_questions extends restore_structure_step {
         }
         $data->contextid = $mapping->parentitemid;
 
+        // Before 3.1, the 'stamp' field could be erroneously duplicated.
+        // From 3.1 onwards, there's a unique index of (contextid, stamp).
+        // If we encounter a duplicate in an old restore file, just generate a new stamp.
+        // This is the same as what happens during an upgrade to 3.1+ anyway.
+        if ($DB->record_exists('question_categories', ['stamp' => $data->stamp, 'contextid' => $data->contextid])) {
+            $data->stamp = make_unique_id_code();
+        }
+
         // Let's create the question_category and save mapping
         $newitemid = $DB->insert_record('question_categories', $data);
         $this->set_mapping('question_category', $oldid, $newitemid);
index ffaa34d..d203db4 100644 (file)
@@ -628,7 +628,9 @@ abstract class backup_controller_dbops extends backup_dbops {
             $locked = $uselocks && (get_config('backup', $config.'_locked') == true);
             if ($plan->setting_exists($settingname)) {
                 $setting = $plan->get_setting($settingname);
-                if ($setting->get_value() != $value || 1==1) {
+                // We can only update the setting if it isn't already locked by config or permission.
+                if ($setting->get_status() !== base_setting::LOCKED_BY_CONFIG
+                        && $setting->get_status() !== base_setting::LOCKED_BY_PERMISSION) {
                     $setting->set_value($value);
                     if ($locked) {
                         $setting->set_status(base_setting::LOCKED_BY_CONFIG);
index 858987b..516c360 100644 (file)
@@ -65,14 +65,6 @@ class block_course_summary extends block_base {
         $context = context_course::instance($this->page->course->id);
         $this->page->course->summary = file_rewrite_pluginfile_urls($this->page->course->summary, 'pluginfile.php', $context->id, 'course', 'summary', NULL);
         $this->content->text = format_text($this->page->course->summary, $this->page->course->summaryformat, $options);
-        if ($this->page->user_is_editing()) {
-            if($this->page->course->id == SITEID) {
-                $editpage = $CFG->wwwroot.'/'.$CFG->admin.'/settings.php?section=frontpagesettings';
-            } else {
-                $editpage = $CFG->wwwroot.'/course/edit.php?id='.$this->page->course->id;
-            }
-            $this->content->text .= "<div class=\"editbutton\"><a href=\"$editpage\"><img src=\"" . $OUTPUT->pix_url('t/edit') . "\" alt=\"".get_string('edit')."\" /></a></div>";
-        }
         $this->content->footer = '';
 
         return $this->content;
index a33f66e..defb9f7 100644 (file)
@@ -29,15 +29,6 @@ Feature: Course summary block used in a course
     And I should see "Course summary" in the "Course summary" "block"
     And I should see "Proved the course summary block works!" in the "Course summary" "block"
 
-  Scenario: Teacher can see an edit icon when edit mode is on and follow it to the course edit page
-    When I log in as "teacher1"
-    And I follow "Course 1"
-    And I turn editing mode on
-    Then I should see "Proved the course summary block works!" in the "Course summary" "block"
-    And I should see "Course summary" in the "Course summary" "block"
-    And I click on ".editbutton a" "css_element" in the "Course summary" "block"
-    Then I should see "Edit course settings" in the "h2" "css_element"
-
   Scenario: Teacher can not see edit icon when edit mode is off
     When I log in as "teacher1"
     And I follow "Course 1"
index 6299f59..a48fb70 100644 (file)
@@ -22,15 +22,6 @@ Feature: Course summary block used on the frontpage
     And I should not see "Course summary" in the "Course/site summary" "block"
     And I should see "Proved the summary block works!" in the "Course/site summary" "block"
 
-  Scenario: Admin can see an edit icon when edit mode is on and follow it to the front page settings
-    When I log in as "admin"
-    And I am on site homepage
-    And I follow "Turn editing on"
-    Then I should see "Proved the summary block works!" in the "Course/site summary" "block"
-    And I should not see "Course summary" in the "Course/site summary" "block"
-    And I click on ".editbutton a" "css_element" in the "Course/site summary" "block"
-    Then I should see "Front page settings" in the "h2" "css_element"
-
   Scenario: Admin can not see edit icon when edit mode is off
     When I log in as "admin"
     And I am on site homepage
index 8a0925e..5d80be0 100644 (file)
@@ -124,11 +124,12 @@ class behat_completion extends behat_base {
         } else {
             $imgalttext = get_string("completion-alt-auto-y", 'core_completion', $activityname);
         }
-        $csselementforactivitytype = "li.modtype_".strtolower($activitytype);
+        $activityxpath = "//li[contains(concat(' ', @class, ' '), ' modtype_" . strtolower($activitytype) . " ')]";
+        $activityxpath .= "[descendant::*[contains(text(), '" . $activityname . "')]]";
 
         $xpathtocheck = "//img[contains(@alt, '$imgalttext')]";
         $this->execute("behat_general::should_exist_in_the",
-            array($xpathtocheck, "xpath_element", $csselementforactivitytype, "css_element")
+            array($xpathtocheck, "xpath_element", $activityxpath, "xpath_element")
         );
 
     }
@@ -144,12 +145,12 @@ class behat_completion extends behat_base {
         } else {
             $imgalttext = get_string("completion-alt-auto-n", 'core_completion', $activityname);
         }
-        $csselementforactivitytype = "li.modtype_".strtolower($activitytype);
+        $activityxpath = "//li[contains(concat(' ', @class, ' '), ' modtype_" . strtolower($activitytype) . " ')]";
+        $activityxpath .= "[descendant::*[contains(text(), '" . $activityname . "')]]";
 
         $xpathtocheck = "//img[contains(@alt, '$imgalttext')]";
         $this->execute("behat_general::should_exist_in_the",
-            array($xpathtocheck, "xpath_element", $csselementforactivitytype, "css_element")
+            array($xpathtocheck, "xpath_element", $activityxpath, "xpath_element")
         );
-
     }
 }
index c549c9e..3011e19 100644 (file)
@@ -217,11 +217,6 @@ class course_edit_form extends moodleform {
             $mform->addElement('select', 'calendartype', get_string('forcecalendartype', 'calendar'), $calendars);
         }
 
-        $options = range(0, 10);
-        $mform->addElement('select', 'newsitems', get_string('newsitemsnumber'), $options);
-        $mform->addHelpButton('newsitems', 'newsitemsnumber');
-        $mform->setDefault('newsitems', $courseconfig->newsitems);
-
         $mform->addElement('selectyesno', 'showgrades', get_string('showgrades'));
         $mform->addHelpButton('showgrades', 'showgrades');
         $mform->setDefault('showgrades', $courseconfig->showgrades);
@@ -372,6 +367,21 @@ class course_edit_form extends moodleform {
                 $mform->insertElementBefore($mform->removeElement($elements[$i]->getName(), false),
                         'addcourseformatoptionshere');
             }
+
+            if ($courseformat->supports_news()) {
+                // Show the news items select field if the course format supports news.
+                $options = range(0, 10);
+                $newsitems = $mform->createElement('select', 'newsitems', get_string('newsitemsnumber'), $options);
+                $mform->insertElementBefore($newsitems, 'showgrades');
+                $courseconfig = get_config('moodlecourse');
+                $mform->setDefault('newsitems', $courseconfig->newsitems);
+                $mform->addHelpButton('newsitems', 'newsitemsnumber');
+            } else {
+                // Otherwise, create a hidden newsitems element and set its value to 0.
+                $newsitems = $mform->addElement('hidden', 'newsitems', 0);
+                $mform->setType('newsitems', PARAM_INT);
+                $newsitems->setValue(0);
+            }
         }
     }
 
index 35d371d..3bb9830 100644 (file)
@@ -1139,6 +1139,26 @@ abstract class format_base {
         return $startdate + $courseduration;
     }
 
+    /**
+     * Indicates whether the course format supports the creation of the Announcements forum.
+     *
+     * For course format plugin developers, please override this to return true if you want the Announcements forum
+     * to be created upon course creation.
+     *
+     * @return bool
+     */
+    public function supports_news() {
+        // For backwards compatibility, check if default blocks include the news_items block.
+        $defaultblocks = $this->get_default_blocks();
+        foreach ($defaultblocks as $blocks) {
+            if (in_array('news_items', $blocks)) {
+                return true;
+            }
+        }
+        // Return false by default.
+        return false;
+    }
+
     /**
      * Get the start date value from the course settings page form.
      *
index 5acb246..f5e0488 100644 (file)
@@ -401,6 +401,15 @@ class format_topics extends format_base {
         }
         return parent::inplace_editable_render_section_name($section, $linkifneeded, $editable, $edithint, $editlabel);
     }
+
+    /**
+     * Indicates whether the course format supports the creation of a news forum.
+     *
+     * @return bool
+     */
+    public function supports_news() {
+        return true;
+    }
 }
 
 /**
index 8b80ae6..ccc38b6 100644 (file)
@@ -7,6 +7,9 @@ Overview of this plugin type at http://docs.moodle.org/dev/Course_formats
 * Course formats can overwrite get_default_course_enddate function to set the default course end date for new courses.
   format_base::get_default_course_enddate uses the new "Course duration" site setting to calculate the default course end date
   from the default course start date.
+* New method format_base::supports_news() which is used to determine whether an Announcements forum will be automatically created on
+  course creation. For course format plugin developers, please override format_base::supports_news() to return true if you want the
+  Announcements forum to be created upon course creation and remove the block names defined in format_base::get_default_blocks().
 
 === 3.1 ===
 * Course format may use the inplace_editable template to allow quick editing of section names, see
index e9237c6..af0fba4 100644 (file)
@@ -488,6 +488,15 @@ class format_weeks extends format_base {
         $dates = $this->get_section_dates(intval($numsections), $startdate);
         return $dates->end;
     }
+
+    /**
+     * Indicates whether the course format supports the creation of a news forum.
+     *
+     * @return bool
+     */
+    public function supports_news() {
+        return true;
+    }
 }
 
 /**
index 07616ea..238da62 100644 (file)
@@ -3935,7 +3935,7 @@ function course_check_module_updates_since($cm, $from, $fileareas = array(), $fi
     }
     if (!empty($fileareas) and (empty($filter) or in_array('fileareas', $filter))) {
         $fs = get_file_storage();
-        $files = $fs->get_area_files($context->id, $component, $fileareas, false, "filearea, timemodified DESC", true, $from);
+        $files = $fs->get_area_files($context->id, $component, $fileareas, false, "filearea, timemodified DESC", false, $from);
         foreach ($fileareas as $filearea) {
             $updates->{$filearea . 'files'} = (object) array('updated' => false);
         }
@@ -3993,6 +3993,7 @@ function course_check_module_updates_since($cm, $from, $fileareas = array(), $fi
     // Check comments.
     if (plugin_supports('mod', $cm->modname, FEATURE_COMMENT) and (empty($filter) or in_array('comments', $filter))) {
         $updates->comments = (object) array('updated' => false);
+        require_once($CFG->dirroot . '/comment/lib.php');
         require_once($CFG->dirroot . '/comment/locallib.php');
         $manager = new comment_manager();
         $comments = $manager->get_component_comments_since($course, $context, $component, $from, $cm);
index 96d1690..ff60653 100644 (file)
@@ -1506,14 +1506,11 @@ class core_course_renderer extends plugin_renderer_base {
         if ($coursecat->get_children_count()) {
             $classes = array(
                 'collapseexpand',
-                'collapse-all',
             );
-            if ($chelper->get_subcat_depth() == 1) {
-                $classes[] = 'disabled';
-            }
+
             // Only show the collapse/expand if there are children to expand.
             $content .= html_writer::start_tag('div', array('class' => 'collapsible-actions'));
-            $content .= html_writer::link('#', get_string('collapseall'),
+            $content .= html_writer::link('#', get_string('expandall'),
                     array('class' => implode(' ', $classes)));
             $content .= html_writer::end_tag('div');
             $this->page->requires->strings_for_js(array('collapseall', 'expandall'), 'moodle');
index c7529cb..47d7dab 100644 (file)
@@ -101,4 +101,66 @@ class core_course_courseformat_testcase extends advanced_testcase {
         $this->assertFalse($modinfoteacher->get_cm($assign1->cmid)->available);
         $this->assertTrue($modinfoteacher->get_cm($assign1->cmid)->uservisible);
     }
+
+    /**
+     * Test for supports_news() with a course format plugin that doesn't define 'news_items' in default blocks.
+     */
+    public function test_supports_news() {
+        $this->resetAfterTest();
+        $format = course_get_format((object)['format' => 'testformat']);
+        $this->assertFalse($format->supports_news());
+    }
+
+    /**
+     * Test for supports_news() for old course format plugins that defines 'news_items' in default blocks.
+     */
+    public function test_supports_news_legacy() {
+        $this->resetAfterTest();
+        $format = course_get_format((object)['format' => 'testlegacy']);
+        $this->assertTrue($format->supports_news());
+    }
+}
+
+/**
+ * Class format_testformat.
+ *
+ * A test class that simulates a course format that doesn't define 'news_items' in default blocks.
+ *
+ * @copyright 2016 Jun Pataleta <jun@moodle.com>
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class format_testformat extends format_base {
+    /**
+     * Returns the list of blocks to be automatically added for the newly created course.
+     *
+     * @return array
+     */
+    public function get_default_blocks() {
+        return [
+            BLOCK_POS_RIGHT => [],
+            BLOCK_POS_LEFT => []
+        ];
+    }
+}
+
+/**
+ * Class format_testlegacy.
+ *
+ * A test class that simulates old course formats that define 'news_items' in default blocks.
+ *
+ * @copyright 2016 Jun Pataleta <jun@moodle.com>
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class format_testlegacy extends format_base {
+    /**
+     * Returns the list of blocks to be automatically added for the newly created course.
+     *
+     * @return array
+     */
+    public function get_default_blocks() {
+        return [
+            BLOCK_POS_RIGHT => ['news_items'],
+            BLOCK_POS_LEFT => []
+        ];
+    }
 }
index 92f0c02..e4f866c 100644 (file)
Binary files a/course/yui/build/moodle-course-categoryexpander/moodle-course-categoryexpander-debug.js and b/course/yui/build/moodle-course-categoryexpander/moodle-course-categoryexpander-debug.js differ
index 49f5138..f655b42 100644 (file)
Binary files a/course/yui/build/moodle-course-categoryexpander/moodle-course-categoryexpander-min.js and b/course/yui/build/moodle-course-categoryexpander/moodle-course-categoryexpander-min.js differ
index b7e1e9b..38a5b34 100644 (file)
Binary files a/course/yui/build/moodle-course-categoryexpander/moodle-course-categoryexpander.js and b/course/yui/build/moodle-course-categoryexpander/moodle-course-categoryexpander.js differ
index a679c79..2176497 100644 (file)
@@ -23,10 +23,12 @@ var CSS = {
         HASCHILDREN: 'with_children'
     },
     SELECTORS = {
+        WITHCHILDRENTREES: '.with_children',
         LOADEDTREES: '.with_children.loaded',
         CONTENTNODE: '.content',
         CATEGORYLISTENLINK: '.category .info .categoryname',
         CATEGORYSPINNERLOCATION: '.categoryname',
+        CATEGORYWITHCOLLAPSEDCHILDREN: '.category.with_children.collapsed',
         CATEGORYWITHCOLLAPSEDLOADEDCHILDREN: '.category.with_children.loaded.collapsed',
         CATEGORYWITHMAXIMISEDLOADEDCHILDREN: '.category.with_children.loaded:not(.collapsed)',
         COLLAPSEEXPAND: '.collapseexpand',
@@ -80,6 +82,58 @@ NS.setup_keyboard_listeners = function() {
     Y.one(Y.config.doc).delegate('key', this.collapse_expand_all, 'enter', SELECTORS.COLLAPSEEXPAND, this);
 };
 
+/**
+ * Expand all categories.
+ *
+ * @method expand_category
+ * @private
+ * @param {Node} categorynode The node to expand
+ */
+NS.expand_category = function(categorynode) {
+    // Load the actual dependencies now that we've been called.
+    Y.use('io-base', 'json-parse', 'moodle-core-notification', 'anim-node-plugin', function() {
+        // Overload the expand_category with the _expand_category function to ensure that
+        // this function isn't called in the future, and call it for the first time.
+        NS.expand_category = NS._expand_category;
+        NS.expand_category(categorynode);
+    });
+};
+
+NS._expand_category = function(categorynode) {
+    var categoryid,
+        depth;
+
+    if (!categorynode.hasClass(CSS.HASCHILDREN)) {
+        // Nothing to do here - this category has no children.
+        return;
+    }
+
+    if (categorynode.hasClass(CSS.LOADED)) {
+        // We've already loaded this content so we just need to toggle the view of it.
+        this.run_expansion(categorynode);
+        return;
+    }
+
+    // We use Data attributes to store the category.
+    categoryid = categorynode.getData('categoryid');
+    depth = categorynode.getData('depth');
+    if (typeof categoryid === "undefined" || typeof depth === "undefined") {
+        return;
+    }
+
+    this._toggle_generic_expansion({
+        parentnode: categorynode,
+        childnode: categorynode.one(SELECTORS.CONTENTNODE),
+        spinnerhandle: SELECTORS.CATEGORYSPINNERLOCATION,
+        data: {
+            categoryid: categoryid,
+            depth: depth,
+            showcourses: categorynode.getData('showcourses'),
+            type: TYPE_CATEGORY
+        }
+    });
+};
+
 /**
  * Toggle the animation of the clicked category node.
  *
@@ -308,12 +362,12 @@ NS._collapse_expand_all = function(e) {
 NS.expand_all = function(ancestor) {
     var finalexpansions = [];
 
-    ancestor.all(SELECTORS.CATEGORYWITHCOLLAPSEDLOADEDCHILDREN)
+    ancestor.all(SELECTORS.CATEGORYWITHCOLLAPSEDCHILDREN)
         .each(function(c) {
-        if (c.ancestor(SELECTORS.CATEGORYWITHCOLLAPSEDLOADEDCHILDREN)) {
+        if (c.ancestor(SELECTORS.CATEGORYWITHCOLLAPSEDCHILDREN)) {
             // Expand the hidden children first without animation.
             c.removeClass(CSS.SECTIONCOLLAPSED);
-            c.all(SELECTORS.LOADEDTREES).removeClass(CSS.SECTIONCOLLAPSED);
+            c.all(SELECTORS.WITHCHILDRENTREES).removeClass(CSS.SECTIONCOLLAPSED);
         } else {
             finalexpansions.push(c);
         }
@@ -321,7 +375,7 @@ NS.expand_all = function(ancestor) {
 
     // Run the final expansion with animation on the visible items.
     Y.all(finalexpansions).each(function(c) {
-        this.run_expansion(c);
+        this.expand_category(c);
     }, this);
 
 };
index 75a63ce..3c0a1a8 100644 (file)
@@ -8,6 +8,14 @@
     text-align: center;
 }
 
+.mediaplugin,
+.mediaplugin video {
+    /* Make videos as wide as possible without being wider than their containers */
+    width: 100vw;
+    max-width: 100%;
+    height: auto;
+}
+
 .mediaplugin > div {
     margin: auto;
 }
index 8e17c17..cb13808 100644 (file)
@@ -83,7 +83,7 @@ class autogroup_form extends moodleform {
         }
 
         $coursecontext = context_course::instance($COURSE->id);
-        if ($cohorts = cohort_get_available_cohorts($coursecontext, COHORT_WITH_ENROLLED_MEMBERS_ONLY)) {
+        if ($cohorts = cohort_get_available_cohorts($coursecontext, COHORT_WITH_ENROLLED_MEMBERS_ONLY, 0, 0)) {
             $options = array(0 => get_string('anycohort', 'cohort'));
             foreach ($cohorts as $c) {
                 $options[$c->id] = format_string($c->name, true, context::instance_by_id($c->contextid));
index 354f075..dd1e581 100644 (file)
@@ -170,15 +170,14 @@ $string['configcronremotepassword'] = 'This means that the cron.php script canno
     http://site.example.com/admin/cron.php?password=opensesame
 </pre>If this is left empty, no password is required.';
 $string['configcurlcache'] = 'Time-to-live for cURL cache, in seconds.';
-$string['configcustommenuitems'] = 'You can configure a custom menu here to be shown by themes. Each line consists of some menu text, a link URL (optional), a tooltip title (optional) and a language code or comma-separated list of codes (optional, for displaying the line to users of the specified language only), separated by pipe characters. You can specify a structure using hyphens, and dividers can be used by adding a line of one or more # characters where desired. For example:
+$string['configcustommenuitems'] = 'You can configure a custom menu here to be shown by themes. Each line consists of some menu text, a link URL (optional), a tooltip title (optional) and a language code or comma-separated list of codes (optional, for displaying the line to users of the specified language only), separated by pipe characters. Lines starting with a hyphen will appear as menu items in the previous top level menu, and dividers can be used by adding a line of one or more # characters where desired. For example:
 <pre>
 Moodle community|https://moodle.org
 -Moodle free support|https://moodle.org/support
+-Moodle Docs|http://docs.moodle.org|Moodle Docs
+-German Moodle Docs|http://docs.moodle.org/de|Documentation in German|de
 -###
 -Moodle development|https://moodle.org/development
---Moodle Docs|http://docs.moodle.org|Moodle Docs
---German Moodle Docs|http://docs.moodle.org/de|Documentation in German|de
-#####
 Moodle.com|http://moodle.com/
 </pre>';
 $string['configcustomusermenuitems'] = 'You can configure the contents of the user menu (with the exception of the log out link, which is automatically added). Each line is separated by | characters and consists of 1) a string in "langstringname, componentname" form or as plain text, 2) a URL, and 3) an icon either as a pix icon or as a URL. Dividers can be used by adding a line of one or more # characters where desired.';
index 5f05de6..310efb4 100644 (file)
Binary files a/lib/amd/build/event.min.js and b/lib/amd/build/event.min.js differ
index 568f4df..c67b5dd 100644 (file)
@@ -35,6 +35,20 @@ define(['jquery', 'core/yui'],
             FORM_FIELD_VALIDATION: "core_form-field-validation"
         },
 
+        /**
+         * Load the legacy YUI module which defines events in M.core.event and return it.
+         *
+         * @method getLegacyEvents
+         * @return {Promise}
+         */
+        getLegacyEvents: function() {
+            var result = $.Deferred();
+            Y.use('event', 'moodle-core-event', function() {
+                result.resolve(window.M.core.event);
+            });
+            return result.promise();
+        },
+
         /**
          * Trigger an event using both JQuery and YUI
          *
@@ -45,7 +59,7 @@ define(['jquery', 'core/yui'],
             nodes = $(nodes);
             Y.use('event', 'moodle-core-event', function(Y) {
                 // Trigger it the JQuery way.
-                $(document).trigger(M.core.event.FILTER_CONTENT_UPDATED, nodes);
+                $(document).trigger(M.core.event.FILTER_CONTENT_UPDATED, [nodes]);
 
                 // Create a YUI NodeList from our JQuery Object.
                 var yuiNodes = new Y.NodeList(nodes.get());
index 5cfb8fd..cf70cfa 100644 (file)
@@ -117,6 +117,9 @@ class behat_util extends testing_util {
         // Enable web cron.
         set_config('cronclionly', 0);
 
+        // Set editor autosave to high value, so as to avoid unwanted ajax.
+        set_config('autosavefrequency', '604800', 'editor_atto');
+
         // Keeps the current version of database and dataroot.
         self::store_versions_hash();
 
index 439643b..824085e 100644 (file)
@@ -60,5 +60,10 @@ JS;
         }
 
         $this->field->setValue($value);
+
+        // Ensure all pending JS is finished.
+        if ($this->running_javascript()) {
+            $this->session->wait(behat_base::TIMEOUT * 1000, behat_base::PAGE_READY_JS);
+        }
     }
 }
index b121e5e..6dd4784 100644 (file)
@@ -134,6 +134,13 @@ function behat_error_handler($errno, $errstr, $errfile, $errline, $errcontext) {
         return true;
     }
 
+    // No need to report the before_scenario warning generated to clear last error.
+    // As error_clear_last is only available in php 7.0+, we trigger E_USER_WARNING
+    // to clear any last error which was generated during reset in before_scenario.
+    if (($errno === E_USER_WARNING) && $errstr == 'before_scenario') {
+        return;
+    }
+
     // This error handler receives E_ALL | E_STRICT, running the behat test site the debug level is
     // set to DEVELOPER and will always include E_NOTICE,E_USER_NOTICE... as part of E_ALL, if the current
     // error_reporting() value does not include one of those levels is because it has been forced through
@@ -175,6 +182,14 @@ function behat_shutdown_function() {
     if ($error = error_get_last()) {
         // Ignore E_WARNING, as they might come via ( @ )suppression and might lead to false failure.
         if (isset($error['type']) && !($error['type'] & E_WARNING)) {
+
+            // No need to report the before_scenario warning generated to clear last error.
+            // As error_clear_last is only available in php 7.0+, we trigger E_USER_WARNING
+            // to clear any last error which was generated during reset in before_scenario.
+            if (($error['type'] & E_USER_WARNING) && $error['message'] == 'before_scenario') {
+                return;
+            }
+
             $errors = behat_get_shutdown_process_errors();
 
             $errors[] = $error;
index 0c24640..79775c8 100644 (file)
@@ -5616,7 +5616,11 @@ function message_is_user_blocked($recipient, $sender = null) {
     debugging('message_is_user_blocked() is deprecated and is no longer used, please use
         \core_message\api::is_user_blocked() instead.', DEBUG_DEVELOPER);
 
-    return \core_message\api::is_user_blocked($recipient, $sender);
+    $senderid = null;
+    if ($sender !== null && isset($sender->id)) {
+        $senderid = $sender->id;
+    }
+    return \core_message\api::is_user_blocked($recipient->id, $senderid);
 }
 
 /**
index 2f6c610..45b1dee 100644 (file)
@@ -1246,11 +1246,7 @@ class global_navigation extends navigation_node {
         } else {
             $this->rootnodes['courses']->isexpandable = true;
         }
-
-        // Load the users enrolled courses if they are viewing the My Moodle page AND the admin has not
-        // set that they wish to keep the My Courses branch collapsed by default.
         $this->rootnodes['mycourses']->forceopen = true;
-        $this->load_courses_enrolled();
 
         $canviewcourseprofile = true;
 
index 716d41c..3474e91 100644 (file)
@@ -7,7 +7,7 @@ var require = {
 
     paths: {
         jquery: '[JSURL]lib/jquery/jquery-3.1.0.min[JSEXT]',
-        jqueryui: '[JSURL]lib/jquery/ui-1.11.4/jquery-ui.min[JSEXT]',
+        jqueryui: '[JSURL]lib/jquery/ui-1.12.1/jquery-ui.min[JSEXT]',
         jqueryprivate: '[JSURL]lib/requirejs/jquery-private[JSEXT]'
     },
 
index b3b4ed3..e1be9fd 100644 (file)
@@ -103,6 +103,13 @@ class behat_hooks extends behat_base {
      */
     protected static $runningsuite = '';
 
+    /**
+     * Keeps track of php error generated during reset.
+     *
+     * @var int keep track of how many php errors were generated.
+     */
+    public static $phperrorduringresetcounter = 0;
+
     /**
      * Hook to capture BeforeSuite event so as to give access to moodle codebase.
      * This will try and catch any exception and exists if anything fails.
@@ -339,7 +346,30 @@ class behat_hooks extends behat_base {
         // Reset $SESSION.
         \core\session\manager::init_empty_session();
 
+        // Set custom handler to try reset all data, if failed because of previous ajax.
+        set_error_handler(
+            function($errno, $errstr, $errfile, $errline) {
+                behat_hooks::$phperrorduringresetcounter++;
+                if (behat_hooks::$phperrorduringresetcounter < self::TIMEOUT) {
+                    sleep(1);
+                    behat_util::reset_all_data();
+                }
+                return true;
+            }, -1 & ~E_NOTICE & ~E_WARNING);
         behat_util::reset_all_data();
+        restore_error_handler();
+
+        // Trigger an error which will be ignored by behat_shutdown_function, this is hacky way to clear last error in php < 7.0.
+        if (self::$phperrorduringresetcounter > 0) {
+            if (function_exists('error_clear_last')) {
+                error_clear_last();
+            } else {
+                trigger_error('before_scenario', E_USER_WARNING);
+            }
+        }
+
+        // Reset the counter here, as this won't be required.
+        self::$phperrorduringresetcounter = 0;
 
         // Assign valid data to admin user (some generator-related code needs a valid user).
         $user = $DB->get_record('user', array('username' => 'admin'));
@@ -381,6 +411,29 @@ class behat_hooks extends behat_base {
         $this->resize_window('medium');
     }
 
+    /**
+     * Executed after scenario to go to a page where no JS is executed.
+     * This will ensure there are no unwanted ajax calls from browser and
+     * site can be reset safely.
+     *
+     * @param AfterScenarioScope $scope scope passed by event fired after scenario.
+     * @AfterScenario
+     */
+    public function after_scenario(AfterScenarioScope $scope) {
+        try {
+            $this->wait_for_pending_js();
+            $this->getSession()->visit($this->locate_path('/README.txt'));
+            $this->getSession()->reset();
+        } catch (DriverException $e) {
+            // Try restart session, if DriverException caught.
+            try {
+                $this->getSession()->restart();
+            } catch (DriverException $e) {
+                // Do nothing, as this will be caught while starting session in before_scenario.
+            }
+        }
+    }
+
     /**
      * Wait for JS to complete before beginning interacting with the DOM.
      *
index 25910da..fe8ca68 100644 (file)
@@ -348,58 +348,54 @@ class api {
      * @return \stdClass
      */
     public static function get_profile($userid, $otheruserid) {
-        global $CFG, $DB;
+        global $CFG, $DB, $PAGE;
 
         require_once($CFG->dirroot . '/user/lib.php');
 
-        if ($user = \core_user::get_user($otheruserid)) {
-            // Create the data we are going to pass to the renderable.
-            $userfields = user_get_user_details($user, null, array('city', 'country', 'email',
-                'profileimageurl', 'profileimageurlsmall', 'lastaccess'));
-            if ($userfields) {
-                $data = new \stdClass();
-                $data->userid = $userfields['id'];
-                $data->fullname = $userfields['fullname'];
-                $data->city = isset($userfields['city']) ? $userfields['city'] : '';
-                $data->country = isset($userfields['country']) ? $userfields['country'] : '';
-                $data->email = isset($userfields['email']) ? $userfields['email'] : '';
-                $data->profileimageurl = isset($userfields['profileimageurl']) ? $userfields['profileimageurl'] : '';
-                if (isset($userfields['profileimageurlsmall'])) {
-                    $data->profileimageurlsmall = $userfields['profileimageurlsmall'];
-                } else {
-                    $data->profileimageurlsmall = '';
-                }
-                if (isset($userfields['lastaccess'])) {
-                    $data->isonline = helper::is_online($userfields['lastaccess']);
-                } else {
-                    $data->isonline = false;
-                }
-            } else {
-                // Technically the access checks in user_get_user_details are correct,
-                // but messaging has never obeyed them. In order to keep messaging working
-                // we at least need to return a minimal user record.
-                $data = new \stdClass();
-                $data->userid = $otheruserid;
-                $data->fullname = fullname($user);
-                $data->city = '';
-                $data->country = '';
-                $data->email = '';
-                $data->profileimageurl = '';
-                $data->profileimageurlsmall = '';
-                $data->isonline = false;
+        $user = \core_user::get_user($otheruserid, '*', MUST_EXIST);
+
+        // Create the data we are going to pass to the renderable.
+        $data = new \stdClass();
+        $data->userid = $otheruserid;
+        $data->fullname = fullname($user);
+        $data->city = '';
+        $data->country = '';
+        $data->email = '';
+        $data->isonline = false;
+        // Get the user picture data - messaging has always shown these to the user.
+        $userpicture = new \user_picture($user);
+        $userpicture->size = 1; // Size f1.
+        $data->profileimageurl = $userpicture->get_url($PAGE)->out(false);
+        $userpicture->size = 0; // Size f2.
+        $data->profileimageurlsmall = $userpicture->get_url($PAGE)->out(false);
+
+        $userfields = user_get_user_details($user, null, array('city', 'country', 'email', 'lastaccess'));
+        if ($userfields) {
+            if (isset($userfields['city'])) {
+                $data->city = $userfields['city'];
             }
-            // Check if the contact has been blocked.
-            $contact = $DB->get_record('message_contacts', array('userid' => $userid, 'contactid' => $otheruserid));
-            if ($contact) {
-                $data->isblocked = (bool) $contact->blocked;
-                $data->iscontact = true;
-            } else {
-                $data->isblocked = false;
-                $data->iscontact = false;
+            if (isset($userfields['country'])) {
+                $data->country = $userfields['country'];
+            }
+            if (isset($userfields['email'])) {
+                $data->email = $userfields['email'];
             }
+            if (isset($userfields['lastaccess'])) {
+                $data->isonline = helper::is_online($userfields['lastaccess']);
+            }
+        }
 
-            return $data;
+        // Check if the contact has been blocked.
+        $contact = $DB->get_record('message_contacts', array('userid' => $userid, 'contactid' => $otheruserid));
+        if ($contact) {
+            $data->isblocked = (bool) $contact->blocked;
+            $data->iscontact = true;
+        } else {
+            $data->isblocked = false;
+            $data->iscontact = false;
         }
+
+        return $data;
     }
 
     /**
@@ -620,8 +616,12 @@ class api {
             return false;
         }
 
+        $senderid = null;
+        if ($sender !== null && isset($sender->id)) {
+            $senderid = $sender->id;
+        }
         // The recipient has specifically blocked this sender.
-        if (self::is_user_blocked($recipient, $sender)) {
+        if (self::is_user_blocked($recipient->id, $senderid)) {
             return false;
         }
 
@@ -667,27 +667,25 @@ class api {
      * Note: This function will always return false if the sender has the
      * readallmessages capability at the system context level.
      *
-     * @param object $recipient User object.
-     * @param object $sender User object.
+     * @param int $recipientid User ID of the recipient.
+     * @param int $senderid User ID of the sender.
      * @return bool true if $sender is blocked, false otherwise.
      */
-    public static function is_user_blocked($recipient, $sender = null) {
+    public static function is_user_blocked($recipientid, $senderid = null) {
         global $USER, $DB;
 
-        if (is_null($sender)) {
+        if (is_null($senderid)) {
             // The message is from the logged in user, unless otherwise specified.
-            $sender = $USER;
+            $senderid = $USER->id;
         }
 
         $systemcontext = \context_system::instance();
-        if (has_capability('moodle/site:readallmessages', $systemcontext, $sender)) {
+        if (has_capability('moodle/site:readallmessages', $systemcontext, $senderid)) {
             return false;
         }
 
-        if ($contact = $DB->get_record('message_contacts', array('userid' => $recipient->id, 'contactid' => $sender->id))) {
-            if ($contact->blocked) {
-                return true;
-            }
+        if ($DB->get_field('message_contacts', 'blocked', ['userid' => $recipientid, 'contactid' => $senderid])) {
+            return true;
         }
 
         return false;
index 368d1d0..e967ec1 100644 (file)
@@ -26,6 +26,7 @@ namespace core_message\output\messagearea;
 
 defined('MOODLE_INTERNAL') || die();
 
+use core_message\api;
 use renderable;
 use templatable;
 
@@ -95,6 +96,8 @@ class messages implements templatable, renderable {
             $data->messages[] = $message->export_for_template($output);
         }
 
+        $data->isblocked = api::is_user_blocked($this->currentuserid, $this->otheruserid);
+
         return $data;
     }
 }
index a3b4247..8ec40ad 100644 (file)
@@ -996,7 +996,8 @@ class core_message_external extends external_api {
                 'isonline' => new external_value(PARAM_BOOL, 'The user\'s online status'),
                 'messages' => new external_multiple_structure(
                     self::get_messagearea_message_structure()
-                )
+                ),
+                'isblocked' => new external_value(PARAM_BOOL, 'Is this user blocked by the current user?', VALUE_DEFAULT, false),
             )
         );
     }
index d27a1b8..b7d0ccc 100644 (file)
     You should have received a copy of the GNU General Public License
     along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
 }}
+{{!
+    @template core_message/message_area_messages_area
+
+    Messages area template.
+
+    Classes required for JS:
+    * none
+
+    Data attributes required for JS:
+    * none
+
+    Context variables required for this template:
+    * isonline - boolean
+    * isblocked - boolean
+    * otheruserid - int
+    * otheruserfullname - string
+    * messages - array of messages
+
+    Example context (json):
+    {
+        "isonline": true,
+        "isblocked": true,
+        "otheruserid": 1,
+        "otheruserfullname": "Sam Student",
+        "messages": [
+            {
+                "text": "Hello there!"
+            }
+        ]
+    }
+}}
 {{#otheruserid}}
 <div class="messages-header">
     <div class="view-toggle btn-container">
     <div class="name-container">
         <div class="name">
             <button class="btn btn-link" data-action="view-contact-profile" data-userid="{{otheruserid}}">{{otheruserfullname}}</button>
+            {{#isblocked}}
+                <span data-region="contact-icon-blocked">
+                    {{#pix}} t/block, core, {{#str}} contactblocked, message {{/str}} {{/pix}}
+                </span>
+            {{/isblocked}}
         </div>
         <div class="status {{#isonline}}online{{/isonline}}">
             <span class="offline-text">{{#str}} offline, message {{/str}}</span>
index 9c52f86..c6ff6b4 100644 (file)
@@ -839,13 +839,17 @@ class core_message_api_testcase extends core_message_messagelib_testcase {
         $this->setUser($user1);
 
         // User shouldn't be blocked.
-        $this->assertFalse(\core_message\api::is_user_blocked($user1, $user2));
+        $this->assertFalse(\core_message\api::is_user_blocked($user1->id, $user2->id));
 
         // Block the user.
         message_block_contact($user2->id);
 
         // User should be blocked.
-        $this->assertTrue(\core_message\api::is_user_blocked($user1, $user2));
+        $this->assertTrue(\core_message\api::is_user_blocked($user1->id, $user2->id));
+
+        // Unblock the user.
+        message_unblock_contact($user2->id);
+        $this->assertFalse(\core_message\api::is_user_blocked($user1->id, $user2->id));
     }
 
     /**
@@ -865,7 +869,7 @@ class core_message_api_testcase extends core_message_messagelib_testcase {
         $this->setAdminUser();
 
         // As the admin you should still be able to send messages to the user.
-        $this->assertFalse(\core_message\api::is_user_blocked($user1));
+        $this->assertFalse(\core_message\api::is_user_blocked($user1->id));
     }
 
     /*
index 7bf4b84..6e2c71e 100644 (file)
@@ -4872,7 +4872,7 @@ class assign {
 
         if ($this->can_view_submission($user->id)) {
 
-            if (has_capability('mod/assign:submit', $this->get_context(), $user)) {
+            if (has_capability('mod/assign:submit', $this->get_context(), $user, false)) {
                 $submissionstatus = $this->get_assign_submission_status_renderable($user, $showlinks);
                 $o .= $this->get_renderer()->render($submissionstatus);
             }
index cdbadb2..087102e 100644 (file)
     display: table-row;
 }
 
-.path-mod-assign #page-content [data-region="grade-panel"] .mform:not(.unresponsive) #id_attemptsettings .fitem .fitemtitle,
-.path-mod-assign #page-content [data-region="grade-panel"] .mform:not(.unresponsive) #id_attemptsettings .fitem .felement {
-    display: table-cell;
-    float: none;
-    border-top: 1px solid #ddd;
-    padding: 8px 0;
-}
-
 .path-mod-assign #page-content [data-region="grade-panel"] .mform:not(.unresponsive) #id_attemptsettings .fitem:last-of-type .fitemtitle,
 .path-mod-assign #page-content [data-region="grade-panel"] .mform:not(.unresponsive) #id_attemptsettings .fitem:last-of-type .felement {
     border-bottom: 1px solid #ddd;
index 1f0a898..1ee2e43 100644 (file)
@@ -146,13 +146,17 @@ class behat_mod_feedback extends behat_base {
 
         // If chart data is not visible then expand.
         $node = $this->get_selected_node("xpath_element", $charttabledataxpath);
-        if ($node && !$node->isVisible()) {
-            $this->execute('behat_general::i_click_on_in_the', array(
-                get_string('showchartdata'),
-                'link',
-                $feedbackxpath,
-                'xpath_element'
-            ));
+        if ($node) {
+            if (!$node->isVisible()) {
+                // Focus on node, before checking if it's visible.
+                $node->focus();
+                $this->execute('behat_general::i_click_on_in_the', array(
+                    get_string('showchartdata'),
+                    'link',
+                    $feedbackxpath,
+                    'xpath_element'
+                ));
+            }
         }
     }
 
index ceac72c..8361c9f 100644 (file)
@@ -124,8 +124,8 @@ class mod_forum_observer {
         global $CFG;
 
         $course = $event->get_record_snapshot('course', $event->objectid);
-
-        if (!empty($course->newsitems)) {
+        $format = course_get_format($course);
+        if ($format->supports_news() && !empty($course->newsitems)) {
             require_once($CFG->dirroot . '/mod/forum/lib.php');
             // Auto create the announcements forum.
             forum_get_course_forum($event->objectid, 'news');
@@ -142,8 +142,8 @@ class mod_forum_observer {
         global $CFG;
 
         $course = $event->get_record_snapshot('course', $event->objectid);
-
-        if (!empty($course->newsitems)) {
+        $format = course_get_format($course);
+        if ($format->supports_news() && !empty($course->newsitems)) {
             require_once($CFG->dirroot . '/mod/forum/lib.php');
             // Auto create the announcements forum.
             forum_get_course_forum($event->objectid, 'news');
index 911a93e..2267d38 100644 (file)
@@ -134,6 +134,7 @@ Feature: Lesson user override
     And I set the following fields to these values:
       | Password protected lesson | Yes |
       | id_password               | moodle_rules |
+    And I wait until the page is ready
     And I press "Save and display"
     And I navigate to "User overrides" node in "Lesson administration"
     And I press "Add user override"
index 16b22d1..fd2b579 100644 (file)
@@ -1961,7 +1961,7 @@ function quiz_check_updates_since(cm_info $cm, $from, $filter = array()) {
         $select = 'id ' . $questionsql . ' AND (timemodified > :time1 OR timecreated > :time2)';
         $params['time1'] = $from;
         $params['time2'] = $from;
-        $questions = $DB->count_records_select('question', $select, $params) > 0;
+        $questions = $DB->get_records_select('question', $select, $params, '', 'id');
         if (!empty($questions)) {
             $updates->questions->updated = true;
             $updates->questions->itemids = array_keys($questions);
index 084e231..b92186c 100644 (file)
@@ -45,6 +45,7 @@ Feature: Scorm multi-sco completion
     And I should see "Play of the game"
     And I switch to the main frame
     And I follow "Exit activity"
+    And I wait until the page is ready
     Then I should see "Basic Multi-sco SCORM package"
     And I log out
     And I log in as "teacher1"
@@ -80,6 +81,7 @@ Feature: Scorm multi-sco completion
     And I should see "Play of the game"
     And I switch to the main frame
     And I follow "Exit activity"
+    And I wait until the page is ready
     Then I should see "ADV Multi-sco SCORM package"
     And I log out
     And I log in as "teacher1"
index a3deb0c..96b4e80 100644 (file)
@@ -24,9 +24,10 @@ function wiki_print_editor_html($pageid, $content, $version = -1, $section = nul
         $action .= "&section=".urlencode($section);
     }
 
-    echo $OUTPUT->container_start('mdl-align');
+    echo $OUTPUT->container_start('container');
     echo '<form method="post" action="'.$action.'">';
-    echo $OUTPUT->container(print_textarea(true, 20, 100, 0, 0, "newcontent", $content, 0, true, '', 'form-textarea-advanced'), 'wiki_editor');
+    $textarea = print_textarea(true, 20, 100, 0, 0, "newcontent", $content, 0, true, '', 'form-textarea-advanced');
+    echo $OUTPUT->container($textarea, 'wiki_editor');
     wiki_print_edit_form_default_fields('html', $pageid, $version, $upload, $deleteuploads);
     echo '</form>';
     echo $OUTPUT->container_end();
index 8b29bfa..07166e4 100644 (file)
@@ -64,7 +64,7 @@ function wiki_print_editor_wiki($pageid, $content, $editor, $version = -1, $sect
 
     $PAGE->requires->js('/mod/wiki/editors/wiki/buttons.js');
 
-    echo $OUTPUT->container_start('mdl-align');
+    echo $OUTPUT->container_start();
     foreach ($wiki_editor as $button) {
         echo "<a href=\"javascript:insertTags";
         echo "('" . $button[2] . "','" . $button[3] . "','" . $button[4] . "');\">";
@@ -73,7 +73,7 @@ function wiki_print_editor_wiki($pageid, $content, $editor, $version = -1, $sect
     }
     echo $OUTPUT->container_end();
 
-    echo $OUTPUT->container_start('mdl-align');
+    echo $OUTPUT->container_start();
     echo '<form method="post" id="mform1" action="' . $action . '">';
     echo $OUTPUT->container(print_textarea(false, 20, 60, 0, 0, "newcontent", $content, 0, true), false, 'wiki_editor');
     echo $OUTPUT->container_start();
index 44d3c3b..10e4ba9 100644 (file)
@@ -53,8 +53,6 @@ class MoodleQuickForm_wikieditor extends MoodleQuickForm_textarea {
             $this->files = $attributes['files'];
             unset($attributes['files']);
         }
-
-
         parent::__construct($elementName, $elementLabel, $attributes);
         $this->_type = 'wikieditor';
     }
@@ -144,7 +142,8 @@ class MoodleQuickForm_wikieditor extends MoodleQuickForm_textarea {
             $html .= "</a>";
         }
         $html .= "<label class='accesshide' for='addtags'>" . get_string('insertimage', 'wiki')  . "</label>";
-        $html .= "<select id='addtags' onchange=\"insertTags('{$imagetag[0]}', '{$imagetag[1]}', this.value)\">";
+        $html .= "<select id='addtags' class='custom-select m-x-1' " .
+                 "onchange=\"insertTags('{$imagetag[0]}', '{$imagetag[1]}', this.value)\">";
         $html .= "<option value='" . s(get_string('wikiimage', 'wiki')) . "'>" . get_string('insertimage', 'wiki') . '</option>';
         foreach ($this->files as $filename) {
             $html .= "<option value='".s($filename)."'>";
@@ -178,7 +177,15 @@ class MoodleQuickForm_wikieditor extends MoodleQuickForm_textarea {
 
     public function export_for_template(renderer_base $output) {
         $context = $this->export_for_template_base($output);
+
+        // We do want the form-control class on the output from toHTML - but we dont' want it when calling export_for_template.
+        // This is because in this type of form element the export_for_template calls toHTML to get the html for the context.
+        // If we did both we would be duplicating the form-control which messes up the styles.
+        $saved = $this->getAttribute('class');
+        $this->updateAttributes(['class' => $saved . ' form-control']);
+
         $context['html'] = $this->toHtml();
+        $this->updateAttributes(['class' => $saved]);
 
         return $context;
     }
index 65f5014..d19e7df 100644 (file)
@@ -1438,14 +1438,14 @@ function wiki_print_edit_form_default_fields($format, $pageid, $version = -1, $u
 
     echo "<fieldset class=\"wiki-upload-section clearfix\"><legend class=\"ftoggler\">" . get_string("uploadtitle", 'wiki') . "</legend>";
 
-    echo $OUTPUT->container_start('mdl-align wiki-form-center aaaaa');
+    echo $OUTPUT->container_start('container');
     print $filemanager->toHtml();
     echo $OUTPUT->container_end();
 
     $cm = $PAGE->cm;
     $context = context_module::instance($cm->id);
 
-    echo $OUTPUT->container_start('mdl-align wiki-form-center wiki-upload-table');
+    echo $OUTPUT->container_start('container wiki-upload-table');
     wiki_print_upload_table($context, 'wiki_upload', $pageid, $deleteuploads);
     echo $OUTPUT->container_end();
 
index 87e0e5d..8317ccb 100644 (file)
@@ -1282,7 +1282,6 @@ class page_wiki_history extends page_wiki {
                 $table = new html_table();
                 $table->head = array('', get_string('version'), get_string('user'), get_string('modified'), '');
                 $table->data = $contents;
-                $table->attributes['class'] = 'mdl-align';
 
                 echo html_writer::table($table);
 
@@ -1314,15 +1313,15 @@ class page_wiki_history extends page_wiki {
 
                 $table->head = array(get_string('diff', 'wiki') . $icon, get_string('version'), get_string('user'), get_string('modified'), '');
                 $table->data = $contents;
-                $table->attributes['class'] = 'generaltable mdl-align';
+                $table->attributes['class'] = 'table generaltable';
                 $table->rowclasses = $rowclass;
 
                 // Print the form.
                 echo html_writer::start_tag('form', array('action'=>new moodle_url('/mod/wiki/diff.php'), 'method'=>'get', 'id'=>'diff'));
                 echo html_writer::tag('div', html_writer::empty_tag('input', array('type'=>'hidden', 'name'=>'pageid', 'value'=>$pageid)));
                 echo html_writer::table($table);
-                echo html_writer::start_tag('div', array('class'=>'mdl-align'));
-                echo html_writer::empty_tag('input', array('type'=>'submit', 'class'=>'wiki_form-button', 'value'=>get_string('comparesel', 'wiki')));
+                echo html_writer::start_tag('div');
+                echo html_writer::empty_tag('input', array('type'=>'submit', 'class'=>'wiki_form-button btn btn-secondary', 'value'=>get_string('comparesel', 'wiki')));
                 echo html_writer::end_tag('div');
                 echo html_writer::end_tag('form');
             }
@@ -1336,11 +1335,11 @@ class page_wiki_history extends page_wiki {
             //print_paging_bar($vcount, $paging, $rowsperpage,$CFG->wwwroot.'/mod/wiki/history.php?pageid='.$pageid.'&amp;','paging');
             } else {
             $link = new moodle_url('/mod/wiki/history.php', array('pageid' => $pageid));
-            $OUTPUT->container(html_writer::link($link->out(false), get_string('viewperpage', 'wiki', $this->rowsperpage)), 'mdl-align');
+            $OUTPUT->container(html_writer::link($link->out(false), get_string('viewperpage', 'wiki', $this->rowsperpage)));
         }
         if ($vcount > $this->rowsperpage && !$this->allversion) {
             $link = new moodle_url('/mod/wiki/history.php', array('pageid' => $pageid, 'allversion' => 1));
-            $OUTPUT->container(html_writer::link($link->out(false), get_string('viewallhistory', 'wiki')), 'mdl-align');
+            $OUTPUT->container(html_writer::link($link->out(false), get_string('viewallhistory', 'wiki')));
         }
     }
 
@@ -1484,7 +1483,7 @@ class page_wiki_map extends page_wiki {
 
         $table = new html_table();
         $table->head = array(get_string('contributions', 'wiki') . $OUTPUT->help_icon('contributions', 'wiki'));
-        $table->attributes['class'] = 'wiki_editor generalbox';
+        $table->attributes['class'] = 'generalbox table';
         $table->data = array();
         $table->rowclasses = array();
 
@@ -1548,7 +1547,7 @@ class page_wiki_map extends page_wiki {
         $fromlinks = wiki_get_linked_from_pages($page->id);
 
         $table = new html_table();
-        $table->attributes['class'] = 'wiki_navigation_from';
+        $table->attributes['class'] = 'wiki_navigation_from table';
         $table->head = array(get_string('navigationfrom', 'wiki') . $OUTPUT->help_icon('navigationfrom', 'wiki') . ':');
         $table->data = array();
         $table->rowclasses = array();
@@ -1556,13 +1555,12 @@ class page_wiki_map extends page_wiki {
             $lpage = wiki_get_page($link->frompageid);
             $link = new moodle_url('/mod/wiki/view.php', array('pageid' => $lpage->id));
             $table->data[] = array(html_writer::link($link->out(false), format_string($lpage->title)));
-            $table->rowclasses[] = 'mdl-align';
         }
 
-        $table_left = html_writer::table($table);
+        $table_left = $OUTPUT->container(html_writer::table($table), 'col-md-6 span6');
 
         $table = new html_table();
-        $table->attributes['class'] = 'wiki_navigation_to';
+        $table->attributes['class'] = 'wiki_navigation_to table';
         $table->head = array(get_string('navigationto', 'wiki') . $OUTPUT->help_icon('navigationto', 'wiki') . ':');
         $table->data = array();
         $table->rowclasses = array();
@@ -1575,10 +1573,9 @@ class page_wiki_map extends page_wiki {
                 $viewlink = new moodle_url('/mod/wiki/view.php', array('pageid' => $lpage->id));
                 $table->data[] = array(html_writer::link($viewlink->out(false), format_string($lpage->title)));
             }
-            $table->rowclasses[] = 'mdl-align';
         }
-        $table_right = html_writer::table($table);
-        echo $OUTPUT->container($table_left . $table_right, 'wiki_navigation_container');
+        $table_right = $OUTPUT->container(html_writer::table($table), 'col-md-6 span6');
+        echo $OUTPUT->container($table_left . $table_right, 'wiki_navigation_container row');
     }
 
     /**
@@ -1604,7 +1601,7 @@ class page_wiki_map extends page_wiki {
 
         $table = new html_table();
         $table->head = array(get_string('pageindex', 'wiki') . $OUTPUT->help_icon('pageindex', 'wiki'));
-        $table->attributes['class'] = 'wiki_editor generalbox';
+        $table->attributes['class'] = 'generalbox table';
         $table->data[] = array($this->render_navigation_node($tree));
 
         echo html_writer::table($table);
@@ -1643,8 +1640,7 @@ class page_wiki_map extends page_wiki {
 
         $table = new html_table();
         $table->head = array(get_string('pagelist', 'wiki') . $OUTPUT->help_icon('pagelist', 'wiki'));
-        $table->attributes['class'] = 'wiki_editor generalbox';
-        $table->align = array('center');
+        $table->attributes['class'] = 'generalbox table';
         foreach ($stdaux as $key => $elem) {
             $table->data[] = array($key);
             foreach ($elem as $e) {
@@ -1673,7 +1669,7 @@ class page_wiki_map extends page_wiki {
 
         $table = new html_table();
         $table->head = array(get_string('orphaned', 'wiki') . $OUTPUT->help_icon('orphaned', 'wiki'));
-        $table->attributes['class'] = 'wiki_editor generalbox';
+        $table->attributes['class'] = 'generalbox table';
         $table->data = array();
         $table->rowclasses = array();
 
@@ -1709,7 +1705,7 @@ class page_wiki_map extends page_wiki {
 
         $table = new html_table();
         $table->head = array(get_string('updatedpages', 'wiki') . $OUTPUT->help_icon('updatedpages', 'wiki'));
-        $table->attributes['class'] = 'wiki_editor generalbox';
+        $table->attributes['class'] = 'generalbox table';
         $table->data = array();
         $table->rowclasses = array();
 
@@ -1894,7 +1890,7 @@ class page_wiki_restoreversion extends page_wiki {
         $restoreurl = new moodle_url('/mod/wiki/restoreversion.php', $optionsyes);
         $return = new moodle_url('/mod/wiki/viewversion.php', array('pageid'=>$this->page->id, 'versionid'=>$version->id));
 
-        echo $OUTPUT->container_start('wiki-form-center');
+        echo $OUTPUT->container_start();
         echo html_writer::tag('div', get_string('restoreconfirm', 'wiki', $version->version));
         echo $OUTPUT->container_start(false, 'wiki_restoreform');
         echo '<form class="wiki_restore_yes" action="' . $restoreurl . '" method="post" id="restoreversion">';
@@ -1964,7 +1960,7 @@ class page_wiki_deletecomment extends page_wiki {
         $deleteurl = new moodle_url('/mod/wiki/instancecomments.php', $optionsyes);
         $return = new moodle_url('/mod/wiki/comments.php', array('pageid'=>$this->page->id));
 
-        echo $OUTPUT->container_start('wiki-form-center');
+        echo $OUTPUT->container_start();
         echo html_writer::tag('p', $strdeletecheckfull);
         echo $OUTPUT->container_start(false, 'wiki_deletecommentform');
         echo '<form class="wiki_deletecomment_yes" action="' . $deleteurl . '" method="post" id="deletecomment">';
@@ -2137,7 +2133,7 @@ class page_wiki_viewversion extends page_wiki {
             $viewlink = new moodle_url('/user/view.php', array('id' => $userinfo->id));
             $heading .= '&nbsp;&nbsp;&nbsp;<strong>' . get_string('user') . ':</strong>&nbsp;' . html_writer::link($viewlink->out(false), fullname($userinfo));
             $heading .= '&nbsp;&nbsp;&rarr;&nbsp;' . $OUTPUT->user_picture(wiki_get_user_info($pageversion->userid), array('popup' => true)) . '</p>';
-            echo $OUTPUT->container($heading, 'wiki_headingtime', 'mdl-align wiki_modifieduser');
+            echo $OUTPUT->container($heading, 'wiki_headingtime', 'wiki_modifieduser');
             $options = array('swid' => $this->subwiki->id, 'pretty_print' => true, 'pageid' => $this->page->id);
 
             $pageversion->content = file_rewrite_pluginfile_urls($pageversion->content, 'pluginfile.php', $this->modcontext->id, 'mod_wiki', 'attachments', $this->subwiki->id);
@@ -2509,7 +2505,7 @@ class page_wiki_admin extends page_wiki {
         $contents = array();
         $table = new html_table();
         $table->head = array('', get_string('pagename','wiki'));
-        $table->attributes['class'] = 'generaltable mdl-align';
+        $table->attributes['class'] = 'table generaltable';
         $swid = $this->subwiki->id;
         if ($showorphan) {
             if ($orphanedpages = wiki_get_orphaned_pages($swid)) {
@@ -2536,7 +2532,7 @@ class page_wiki_admin extends page_wiki {
 
         echo html_writer::empty_tag('input', array('type' => 'hidden', 'name' => 'option', 'value' => $this->view));
         echo html_writer::table($table);
-        echo html_writer::start_tag('div', array('class' => 'mdl-align'));
+        echo html_writer::start_tag('div');
         if (!$showorphan) {
             echo html_writer::empty_tag('input', array(
                                                      'type'    => 'submit',
@@ -2547,7 +2543,7 @@ class page_wiki_admin extends page_wiki {
             echo html_writer::empty_tag('input', array('type'=>'hidden', 'name'=>'listall', 'value'=>'1'));
             echo html_writer::empty_tag('input', array(
                                                      'type'    => 'submit',
-                                                     'class'   => 'wiki_form-button',
+                                                     'class'   => 'wiki_form-button btn btn-secondary',
                                                      'value'   => get_string('listall', 'wiki'),
                                                      'sesskey' => sesskey()));
         }
@@ -2629,7 +2625,6 @@ class page_wiki_admin extends page_wiki {
                 $table = new html_table();
                 $table->head = array('', get_string('version'), get_string('user'), get_string('modified'), '');
                 $table->data = $contents;
-                $table->attributes['class'] = 'mdl-align';
 
                 echo html_writer::table($table);
             } else {
@@ -2668,7 +2663,7 @@ class page_wiki_admin extends page_wiki {
                 $table = new html_table();
                 $table->head = array(get_string('deleteversions', 'wiki'), get_string('version'), get_string('user'), get_string('modified'), '');
                 $table->data = $contents;
-                $table->attributes['class'] = 'generaltable mdl-align';
+                $table->attributes['class'] = 'table generaltable';
                 $table->rowclasses = $rowclass;
 
                 ///Print the form
@@ -2677,8 +2672,8 @@ class page_wiki_admin extends page_wiki {
                 echo html_writer::empty_tag('input', array('type' => 'hidden', 'name' => 'option', 'value' => $this->view));
                 echo html_writer::empty_tag('input', array('type' => 'hidden', 'name' => 'sesskey', 'value' =>  sesskey()));
                 echo html_writer::table($table);
-                echo html_writer::start_tag('div', array('class' => 'mdl-align'));
-                echo html_writer::empty_tag('input', array('type' => 'submit', 'class' => 'wiki_form-button', 'value' => get_string('deleteversions', 'wiki')));
+                echo html_writer::start_tag('div');
+                echo html_writer::empty_tag('input', array('type' => 'submit', 'class' => 'wiki_form-button btn btn-secondary', 'value' => get_string('deleteversions', 'wiki')));
                 echo html_writer::end_tag('div');
                 echo html_writer::end_tag('form');
             }
index ca5d05b..8ee518f 100644 (file)
     min-height: 100px;
 }
 
-.wiki_editor {
-    width: 50%;
-    margin: auto;
-    margin-top: 10px;
-    margin-bottom: 10px;
-}
-
 .wiki_previewbox {
     width: 50%;
     margin: auto;
     margin-left: 0%;
 }
 
-.wiki-form-center {
-    text-align: center;
-    margin: auto;
-    width: 320px;
-}
-
 .wiki-upload-table {
     margin: 8px auto;
     clear: both;
@@ -364,12 +351,6 @@ a.wiki_edit_section {
     text-align: right;
 }
 
-.wikieditor-toolbar img {
-    width: 22px;
-    height: 22px;
-    vertical-align: middle;
-}
-
 .path-mod-wiki .printicon {
     background: url([[pix:t/print]]) no-repeat scroll 2px center transparent;
     padding-left: 20px;
index 710777b..9fe541a 100644 (file)
@@ -60,18 +60,6 @@ class microsoft_skydrive extends oauth2_client {
         $this->foldernamecache = cache::make('repository_skydrive', 'foldername');
     }
 
-    /**
-     * Should HTTP GET be used instead of POST?
-     *
-     * The Microsoft API does not support POST, so we should use
-     * GET instead (with the auth_token passed as a GET param).
-     *
-     * @return bool true if GET should be used
-     */
-    protected function use_http_get() {
-        return true;
-    }
-
     /**
      * Returns the auth url for OAuth 2.0 request
      * @return string the auth url
@@ -88,6 +76,20 @@ class microsoft_skydrive extends oauth2_client {
         return 'https://login.live.com/oauth20_token.srf';
     }
 
+    /**
+     * Post request.
+     *
+     * Overridden to convert the data to a string, else curl will set the wrong headers.
+     *
+     * @param string $url The URL.
+     * @param array|string $params The parameters.
+     * @param array $options The options.
+     * @return bool
+     */
+    public function post($url, $params = '', $options = array()) {
+        return parent::post($url, format_postdata_for_curlcall($params), $options);
+    }
+
     /**
      * Downloads a file to a  file from skydrive using authenticated request
      *
index 72c27c2..11cb7db 100644 (file)
@@ -72,7 +72,7 @@ class repository_webdav extends repository {
     }
     public function get_file($url, $title = '') {
         $url = urldecode($url);
-        $path = $this->prepare_file($title);
+        $path = $this->prepare_file();
         if (!$this->dav->open()) {
             return false;
         }
index d3adac7..a4885a4 100644 (file)
Binary files a/theme/boost/amd/build/loader.min.js and b/theme/boost/amd/build/loader.min.js differ
index cf908f8..ac95dea 100644 (file)
@@ -23,7 +23,7 @@
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  * @since      2.9
  */
-define(['jquery', './tether'], function(jQuery, Tether) {
+define(['jquery', './tether', 'core/event'], function(jQuery, Tether, Event) {
 
     window.jQuery = jQuery;
     window.Tether = Tether;
@@ -40,11 +40,23 @@ define(['jquery', './tether'], function(jQuery, Tether) {
             'theme_boost/tooltip',
             'theme_boost/popover'],
             function() {
+
+        jQuery('body').popover({
+            selector: '[data-toggle="popover"]',
+            trigger: 'focus'
+        });
+
+        // We need to call popover automatically if nodes are added to the page later.
+        Event.getLegacyEvents().done(function(events) {
+            jQuery(document).on(events.FILTER_CONTENT_UPDATED, function() {
                 jQuery('body').popover({
                     selector: '[data-toggle="popover"]',
                     trigger: 'focus'
                 });
+            });
+        });
     });
 
+
     return {};
 });
index b4828c6..f715c4e 100644 (file)
@@ -694,4 +694,12 @@ class core_renderer extends \core_renderer {
         return $skipped;
     }
 
+    /**
+     * Secure login info.
+     *
+     * @return string
+     */
+    public function secure_login_info() {
+        return $this->login_info(false);
+    }
 }
index b0ae36a..4765c2c 100644 (file)
 
 defined('MOODLE_INTERNAL') || die();
 
+$blockshtml = $OUTPUT->blocks('side-pre');
+$hasblocks = strpos($blockshtml, 'data-block=') !== false;
+$bodyattributes = $OUTPUT->body_attributes();
+
 $templatecontext = [
     'sitename' => format_string($SITE->shortname, true, array('context' => context_course::instance(SITEID))),
     'output' => $OUTPUT,
-    'sidepreblocks' => $OUTPUT->blocks('side-pre'),
+    'bodyattributes' => $bodyattributes,
+    'sidepreblocks' => $blockshtml,
+    'hasblocks' => $hasblocks
 ];
 
 echo $OUTPUT->render_from_template('theme_boost/secure', $templatecontext);
index 30b65f3..9330486 100644 (file)
 
 #adminsettings .form-overridden {
     @extend .form-control-feedback;
+    @extend .alert-info;
 }
 
 .modal.modal-in-page {
index b012aad..0b830b2 100644 (file)
@@ -64,6 +64,9 @@
     width: 98%;
     height: 98%;
 }
+.file-picker .fp-def-search {
+    margin-top: 0;
+}
 // Repositories on fp-repo-area (File Picker only)
 .file-picker .fp-list {
     list-style-type: none;
index 6c5305b..de13dc1 100644 (file)
         border: 1px solid #e3e3e3;
         clear: both;
 
+        img {
+            max-width: 100%;
+        }
+
         .contacts-area {
             border-right: 1px solid #e3e3e3;
             height: 600px;
index 33f7c55..91dcd52 100644 (file)
@@ -355,7 +355,7 @@ div#dock {
     float: left;
     width: 20rem;
     display: inline-block;
-    height: 12rem;
+    min-height: 12rem;
 }
 
 #page-mod-quiz-mod #id_reviewoptionshdr .btn-link {
@@ -366,7 +366,7 @@ div#dock {
     float: left;
     clear: left;
 }
-#page-mod-quiz-mod #id_reviewoptionshdr .form-check-inline {
+#page-mod-quiz-mod #id_reviewoptionshdr .form-check {
     width: 90%;
     height: 22px;
 }
@@ -462,9 +462,8 @@ div#dock {
     }
 }
 
-/**
- * Assign.
- */
+
+// Assign.
 .path-mod-assign [data-region="grade-actions-panel"] [data-region="grade-actions"] .collapse-buttons {
     top: auto;
 }
@@ -472,38 +471,69 @@ div#dock {
     overflow: initial;
 }
 
-.path-mod-assign [data-region="grade-panel"] .has-popout {
-    background-color: $card-bg;
+// This section removes the responsiveness from the form in the grading panel
+$popout-header-font-size: 1.5 * $font-size-base;
+// This can't be calculated from modal-title-padding because we are mixing px and rem units.
+$popout-header-height: 4rem;
 
-    @include border-radius($card-border-radius);
-    border: $card-border-width solid $card-border-color;
+.path-mod-assign #page-content [data-region="grade-panel"] .mform:not(.unresponsive) .fcontainer .fitem.popout .felement {
+    height: calc(100% - #{$popout-header-height});
+}
+
+.path-mod-assign [data-region="grade-panel"] {
+    padding-top: $spacer;
+}
+.path-mod-assign [data-region="grade-panel"] .fitem > .col-md-3,
+.path-mod-assign [data-region="grade-panel"] .fitem > .col-md-9 {
+    width: 100%;
+    padding: 0;
+}
+.path-mod-assign [data-region="grade-panel"] fieldset,
+.path-mod-assign [data-region="grade-panel"] .fitem.row {
+    margin: 0;
+}
+
+.path-mod-assign [data-region="grade-panel"] .fitem > .col-md-3 > .pull-xs-right {
+    float: none !important; /* stylelint-disable-line declaration-no-important */
+}
+
+.path-mod-assign [data-region="grade-panel"] .mform .fitem.has-popout .felement {
+    width: 100%;
 }
 
+.path-mod-assign [data-region="grade-panel"] .mform .fitem .felement {
+    width: auto;
+}
+
+// Now styles for the popout sections.
+
 .path-mod-assign [data-region="grade-panel"] .popout {
     background-color: $modal-content-bg;
 }
 
-.path-mod-assign [data-region="grade-panel"] .col-md-3 {
-    width: 100%;
+.path-mod-assign [data-region="grade-panel"] .fitem.has-popout {
+    background-color: $card-bg;
+    @include border-radius($card-border-radius);
+    border: $card-border-width solid $card-border-color;
+    padding: $card-spacer-x;
+    margin-bottom: $spacer;
+}
+.path-mod-assign [data-region="grade-panel"] .has-popout .col-md-3 {
+    border-bottom: $hr-border-width solid $hr-border-color;
+    margin-bottom: $spacer;
 }
 
+
 .path-mod-assign [data-region="grade-panel"] .popout > .col-md-3 {
     @extend .modal-header;
-    font-size: 1.5rem;
-}
-
-.path-mod-assign [data-region="grade-panel"] [data-region="popout-button"] {
-    margin-top: 0.5rem;
+    font-size: $popout-header-font-size;
 }
 
 .path-mod-assign [data-region="grade-panel"] .popout [data-region="popout-button"] {
     margin-top: 0;
 }
 
-.path-mod-assign [data-region="grade-panel"] h3 {
-    margin: 10px 0;
-    line-height: 40px;
-}
+// Now style the fixed header elements.
 
 .path-mod-assign [data-region="assignment-info"] {
     overflow-y: hidden;
@@ -525,6 +555,15 @@ div#dock {
     height: 85px;
 }
 
+@media (max-width: 767px) {
+    .path-mod-assign [data-region="grading-navigation-panel"] {
+        height: auto;
+    }
+    .path-mod-assign [data-region="user-info"] {
+        margin-top: 1rem;
+    }
+}
+
 .path-mod-assign [data-region="grading-navigation"] [data-region="input-field"] input {
     width: auto;
     display: inline-block;
index c91bb97..0f2c157 100644 (file)
         ]
     }
 }}
-<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>
-        {{/has_action}}
-        {{^has_action}}
-            <li class="breadcrumb-item">{{{text}}}</li>
-        {{/has_action}}
-    {{/get_items}}
-</ol>
+<nav role="navigation">
+    <ol class="breadcrumb">
+        {{#get_items}}
+            {{#has_action}}
+                <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>
+            {{/has_action}}
+        {{/get_items}}
+    </ol>
+</nav>
index 344f1df..ac43dd8 100644 (file)
@@ -1,3 +1,94 @@
+{{!
+    This file is part of Moodle - http://moodle.org/
+
+    Moodle is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    Moodle is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+}}
+{{!
+    @template core_form/element-select
+
+    Simple select form element template.
+
+    Context variables required for this template:
+    * id - Element id,
+    * name - Element name,
+    * label -  Element label,
+    * multiple - multi select?,
+    * checked - checked?,
+    * error - Is there an error associated with this element?,
+    * size - Element size,
+    * value - Element value,
+    * helpbutton - Helpbutton,
+    * hiddenlabel - Element hidden flag,
+    * frozen - Element frozen flag,
+    * hardfrozen - Element hard fronzen flag,
+    * extraclasses - Extra classes assocaited,
+    * type - Element type,
+    * attributes - Element attributes,
+    * options - [
+        {
+            text - Option text,
+            value - Option value,
+            selected - Selected?,
+            disabled - Disabled?,
+            optionattributes - Option attributes
+        }
+    ]
+
+    Example context (json):
+    {
+        "element": {
+            "id": "id_maildisplay",
+            "name": "maildisplay",
+            "label": null,
+            "multiple": null,
+            "checked": null,
+            "error": null,
+            "size": null,
+            "value": null,
+            "helpbutton": "",
+            "hiddenlabel": false,
+            "frozen": false,
+            "hardfrozen": false,
+            "extraclasses": null,
+            "type": "select",
+            "attributes": "",
+            "options": [
+                {
+                    "text": "Hide my email address from everyone",
+                    "value": 0,
+                    "selected": false,
+                    "disabled": false,
+                    "optionattributes": ""
+                },
+                {
+                    "text": "Allow everyone to see my email address",
+                    "value": 1,
+                    "selected": true,
+                    "disabled": false,
+                    "optionattributes": ""
+                },
+                {
+                    "text": "Allow only other course members to see my email address",
+                    "value": 2,
+                    "selected": false,
+                    "disabled": false,
+                    "optionattributes": ""
+                }
+            ]
+        }
+    }
+}}
 {{< core_form/element-template }}
     {{$element}}
         {{^element.frozen}}
             {{/error}}
             {{{element.attributes}}} >
             {{#element.options}}
-            <option value="{{value}}" {{#selected}}selected{{/selected}} {{#disabled}}disabled{{/disabled}} {{{optionattributes}}}>{{text}}</option>
+            <option value="{{value}}" {{#selected}}selected{{/selected}} {{#disabled}}disabled{{/disabled}}
+                {{{optionattributes}}}>{{{text}}}</option>
             {{/element.options}}
         </select>
         {{/element.frozen}}
diff --git a/theme/boost/templates/header-secure.mustache b/theme/boost/templates/header-secure.mustache
new file mode 100644 (file)
index 0000000..8a5dced
--- /dev/null
@@ -0,0 +1,43 @@
+{{!
+    This file is part of Moodle - http://moodle.org/
+
+    Moodle is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    Moodle is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+}}
+{{!
+    Page header.
+}}
+<header role="banner" class="pos-f-t navbar navbar-full navbar-light bg-faded navbar-static-top moodle-has-zindex">
+
+    <div class="container-fluid">
+
+        <span class="navbar-brand {{# output.should_display_navbar_logo }}has-logo{{/ output.should_display_navbar_logo }}
+            {{^ output.should_display_navbar_logo }}
+                hidden-sm-down
+            {{/ output.should_display_navbar_logo }}
+                ">
+
+            {{# output.should_display_navbar_logo }}
+                <span class="logo hidden-xs-down">
+                    <img src="{{output.get_compact_logo_url}}" alt="{{sitename}}">
+                </span>
+            {{/ output.should_display_navbar_logo }}
+            <span class="site-name hidden-sm-down">{{{ sitename }}}</span>
+        </span>
+        <div class="pull-xs-right nav-link">
+            {{{ output.secure_login_info }}}
+        </div>
+
+    </div>
+</header>
+
index e881acb..bda544c 100644 (file)
@@ -25,7 +25,7 @@
         <button aria-expanded="{{#navdraweropen}}true{{/navdraweropen}}{{^navdraweropen}}false{{/navdraweropen}}" aria-controls="nav-drawer" type="button" class="btn pull-xs-left m-r-1 btn-secondary" data-action="toggle-drawer" data-side="left" data-preference="drawer-open-nav">&#9776;<span class="sr-only">{{#str}}expand, core{{/str}}</span></button>
         </div>
 
-        <a role="banner" href="{{{ config.wwwroot }}}" class="navbar-brand {{# output.should_display_navbar_logo }}has-logo{{/ output.should_display_navbar_logo }}
+        <a href="{{{ config.wwwroot }}}" class="navbar-brand {{# output.should_display_navbar_logo }}has-logo{{/ output.should_display_navbar_logo }}
             {{^ output.should_display_navbar_logo }}
                 hidden-sm-down
             {{/ output.should_display_navbar_logo }}
index b75d798..8513a96 100644 (file)
     <meta name="viewport" content="width=device-width, initial-scale=1.0">
 </head>
 
-<body {{{ output.body_attributes }}}>
+<body {{{ bodyattributes }}}>
 
-{{{ output.standard_top_of_body_html }}}
+<div id="page-wrapper">
 
-<header class="navbar navbar-full navbar-light bg-faded navbar-static-top moodle-has-zindex">
-    <div class="container">
-        <div class="clearfix">
-            <button class="navbar-toggler pull-xs-right hidden-sm-up" data-toggle="collapse" data-target="#bd-main-mav">☰<span class="sr-only">{{#str}}expand{{/str}}</span></button>
-        </div>
+    {{{ output.standard_top_of_body_html }}}
 
-        <a role="banner" href="{{{ config.wwwroot }}}" class="navbar-brand">
-            {{# output.should_display_navbar_logo }}
-                <div class="logo">
-                    <img src="{{output.get_compact_logo_url}}" alt={{#quote}}{{sitename}}{{/quote}}>
-                </div>
-            {{/ output.should_display_navbar_logo }}
-            <div class="site-name">{{{ sitename }}}</div>
-        </a>
+    {{>theme_boost/header-secure}}
 
-        <div class="collapse navbar-toggleable-xs" id="bd-main-nav">
-            <nav class="nav navbar-nav">
-                <!-- search_box -->
-                {{{ output.search_box }}}
-                <!-- custom_menu -->
-                {{{ output.custom_menu }}}
-                <!-- page_heading_menu -->
-                {{{ output.page_heading_menu }}}
-                <!-- login info -->
-                {{{ output.login_info }}}
-            </nav>
-        </div>
-    </div>
-</header>
+    <div id="page" class="container-fluid">
+        {{! Secured full header }}
 
-<div id="page" class="container">
-    {{{ output.full_header }}}
+        <div id="page-header" class="row">
+            <div class="col-xs-12 p-y-1">
+                <div class="page-context-header">
+                    <div class="page-header-headings">
+                        {{{ output.page_heading }}}
+                    </div>
+                </div>
+            </div>
+        </div>
 
-    <div id="page-content" class="row">
-        <div id="region-main-box" class="col-xs-12">
-            <div class="row">
-                <section id="region-main" class="col-xs-12">
+        <div id="page-content" class="row">
+            <div id="region-main-box" class="col-xs-12">
+                <section id="region-main" {{#hasblocks}}class="has-blocks"{{/hasblocks}}>
+                    <div class="card card-block">
+                    {{{ output.course_content_header }}}
                     {{{ output.main_content }}}
+                    {{{ output.course_content_footer }}}
+                    </div>
+                </section>
+                {{#hasblocks}}
+                <section data-region="blocks-column">
+                    {{{ sidepreblocks }}}
                 </section>
+                {{/hasblocks}}
             </div>
         </div>
     </div>
+</div>
+<footer id="page-footer" class="p-y-1 bg-inverse">
+    <div class="container">
+        <div id="course-footer">{{{ output.course_footer }}}</div>
 
-    {{{ output.standard_end_of_body_html }}}
+        {{{ output.standard_end_of_body_html }}}
+    </div>
+</footer>
 
-</div>
 </body>
 </html>
 {{#js}}
diff --git a/theme/boost/tests/behat/behat_theme_boost_behat_auth.php b/theme/boost/tests/behat/behat_theme_boost_behat_auth.php
deleted file mode 100644 (file)
index ecd0461..0000000
+++ /dev/null
@@ -1,56 +0,0 @@
-<?php
-// This file is part of Moodle - http://moodle.org/
-//
-// Moodle is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// Moodle is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
-
-/**
- * Basic authentication steps definitions.
- *
- * @package    core_auth
- * @category   test
- * @copyright  2012 David Monllaó
- * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-
-// NOTE: no MOODLE_INTERNAL test here, this file may be required by behat before including /config.php.
-
-require_once(__DIR__ . '/../../../../lib/behat/behat_base.php');
-require_once(__DIR__ . '/../../../../auth/tests/behat/behat_auth.php');
-
-/**
- * Log in log out steps definitions.
- *
- * @package    core_auth
- * @category   test
- * @copyright  2012 David Monllaó
- * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-class behat_theme_boost_behat_auth extends behat_auth {
-
-    public function i_log_out() {
-        // There is no longer any need to worry about whether the navigation
-        // bar needs to be expanded; user_menu now lives outside the
-        // hamburger.
-
-        // However, the user menu *always* needs to be expanded. if running JS.
-        if ($this->running_javascript()) {
-            $xpath = "//div[@class='usermenu']//a[contains(concat(' ', @class, ' '), ' dropdown-toggle ')]";
-
-            $this->execute('behat_general::i_click_on', array($xpath, "xpath_element"));
-        }
-
-        // No need to check for exceptions as it will checked after this step execution.
-        $this->execute('behat_general::click_link', get_string('logout'));
-    }
-}
index 8cca61c..fb1dcc4 100644 (file)
@@ -192,7 +192,7 @@ a.autolink.glossary:hover {
 }
 /*rtl:raw:
 .collapsible-actions .collapseexpand {
-    background: url([[pix:t/collapsed_rtl]]);
+    background: url([[pix:t/collapsed_rtl]]) right center no-repeat;
 }
 */
 .collapsible-actions .collapse-all {
index 62876c8..299592d 100644 (file)
@@ -35,7 +35,7 @@ form {
 .mform fieldset.collapsible legend a.fheader {
     padding: 0 5px 0 20px;
     margin-left: -20px;
-    background: url([[pix:t/expanded]]) 2px center no-repeat;
+    background: url([[pix:t/expanded]]) left center no-repeat;
 }
 .mform fieldset.collapsed legend a.fheader {
     /*rtl:raw:
index d7f9611..602ed2b 100644 (file)
         clear: both;
         border-radius: 4px;
 
+        img {
+            max-width: 100%;
+        }
+
         .contacts-area {
             border-right: 1px solid #e3e3e3;
             height: 600px;
index 130c9f9..34f9598 100644 (file)
@@ -9,6 +9,17 @@ select {
     width: auto;
 }
 
+// Assign
+.path-mod-assign #page-content [data-region="grade-panel"] .mform:not(.unresponsive) #id_attemptsettings .fitem {
+    .fitemtitle,
+    .felement {
+        display: table-cell;
+        float: none;
+        border-top: 1px solid #ddd;
+        padding: 8px 0;
+    }
+}
+
 // Feedback module
 .path-mod-feedback .mform.feedback_form .fitem .fitemtitle {
     display: block;
index c9b970d..0bcaad4 100644 (file)
@@ -162,7 +162,7 @@ a.autolink.glossary:hover {
 }
 /*rtl:raw:
 .collapsible-actions .collapseexpand {
-    background: url([[pix:t/collapsed_rtl]]);
+    background: url([[pix:t/collapsed_rtl]]) right center no-repeat;
 }
 */
 .collapsible-actions .collapse-all {
@@ -5838,6 +5838,9 @@ a.ygtvspacer:hover {
   clear: both;
   border-radius: 4px;
 }
+.messaging-area-container .messaging-area img {
+  max-width: 100%;
+}
 .messaging-area-container .messaging-area .contacts-area {
   border-right: 1px solid #e3e3e3;
   height: 600px;
@@ -13821,7 +13824,7 @@ form {
 .mform fieldset.collapsible legend a.fheader {
   padding: 0 5px 0 20px;
   margin-left: -20px;
-  background: url([[pix:t/expanded]]) 2px center no-repeat;
+  background: url([[pix:t/expanded]]) left center no-repeat;
 }
 .mform fieldset.collapsed legend a.fheader {
   /*rtl:raw:
@@ -14456,6 +14459,13 @@ body.modal-open {
 select {
   width: auto;
 }
+.path-mod-assign #page-content [data-region="grade-panel"] .mform:not(.unresponsive) #id_attemptsettings .fitem .fitemtitle,
+.path-mod-assign #page-content [data-region="grade-panel"] .mform:not(.unresponsive) #id_attemptsettings .fitem .felement {
+  display: table-cell;
+  float: none;
+  border-top: 1px solid #ddd;
+  padding: 8px 0;
+}
 .path-mod-feedback .mform.feedback_form .fitem .fitemtitle {
   display: block;
   margin-top: 4px;
index 7ad865e..17b2534 100644 (file)
 
 defined('MOODLE_INTERNAL') || die();
 
-$version  = 2016111800.00;              // YYYYMMDD      = weekly release date of this DEV branch.
+$version  = 2016112200.00;              // YYYYMMDD      = weekly release date of this DEV branch.
                                         //         RR    = release increments - 00 in DEV branches.
                                         //           .XX = incremental changes.
 
-$release  = '3.2rc1 (Build: 20161118)'; // Human-friendly version name
+$release  = '3.2rc2 (Build: 20161122)'; // Human-friendly version name
 
 $branch   = '32';                       // This version's branch.
 $maturity = MATURITY_RC;             // This version's maturity level.