Merge branch 'master' into install_master

This commit is contained in:
AMOS bot
2016-11-24 00:06:13 +08:00
82 changed files with 1010 additions and 389 deletions
+2 -1
View 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) {
File diff suppressed because one or more lines are too long
+41
View 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();
}
+11 -4
View 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);
});
}
+1 -1
View 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);
+3 -1
View 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);
}
/**
@@ -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';
+19 -1
View 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++) {
+6 -6
View 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()
);
}
+1 -1
View 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>
+3 -2
View 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
+2 -13
View 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"));
}
}
+8
View 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);
@@ -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);
@@ -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;
@@ -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"
@@ -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
+6 -5
View 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")
);
}
}
+15 -5
View 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);
}
}
}
+20
View 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.
*
+9
View 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;
}
}
/**
+3
View 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
+9
View 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;
}
}
/**
+2 -1
View 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);
+2 -5
View 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');
+62
View 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 <[email protected]>
* @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 <[email protected]>
* @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 => []
];
}
}
@@ -25,10 +25,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',
@@ -82,6 +84,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.
*
@@ -310,12 +364,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);
}
@@ -323,7 +377,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);
};
File diff suppressed because one or more lines are too long
@@ -25,10 +25,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',
@@ -81,6 +83,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.
*
@@ -309,12 +363,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);
}
@@ -322,7 +376,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);
};
+58 -4
View 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);
};
+8
View 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;
}
+1 -1
View 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));
+3 -4
View 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.';
+1 -1
View File
@@ -1 +1 @@
define(["jquery","core/yui"],function(a,b){return{Events:{FORM_FIELD_VALIDATION:"core_form-field-validation"},notifyFilterContentUpdated:function(c){c=a(c),b.use("event","moodle-core-event",function(b){a(document).trigger(M.core.event.FILTER_CONTENT_UPDATED,c);var d=new b.NodeList(c.get());b.fire(M.core.event.FILTER_CONTENT_UPDATED,{nodes:d})})},notifyEditorContentRestored:function(){b.use("event","moodle-core-event",function(b){a(document).trigger(M.core.event.EDITOR_CONTENT_RESTORED),b.fire(M.core.event.EDITOR_CONTENT_RESTORED)})}}});
define(["jquery","core/yui"],function(a,b){return{Events:{FORM_FIELD_VALIDATION:"core_form-field-validation"},getLegacyEvents:function(){var c=a.Deferred();return b.use("event","moodle-core-event",function(){c.resolve(window.M.core.event)}),c.promise()},notifyFilterContentUpdated:function(c){c=a(c),b.use("event","moodle-core-event",function(b){a(document).trigger(M.core.event.FILTER_CONTENT_UPDATED,[c]);var d=new b.NodeList(c.get());b.fire(M.core.event.FILTER_CONTENT_UPDATED,{nodes:d})})},notifyEditorContentRestored:function(){b.use("event","moodle-core-event",function(b){a(document).trigger(M.core.event.EDITOR_CONTENT_RESTORED),b.fire(M.core.event.EDITOR_CONTENT_RESTORED)})}}});
+15 -1
View 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());
+3
View 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();
@@ -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);
}
}
}
+15
View 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;
+5 -1
View 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);
}
/**
-4
View 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;
+1 -1
View 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]'
},
+53
View 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.
*
+56 -58
View 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;
}
// 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;
}
$user = \core_user::get_user($otheruserid, '*', MUST_EXIST);
return $data;
// 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'];
}
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']);
}
}
// 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;
@@ -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;
}
}
+2 -1
View 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),
)
);
}
@@ -14,6 +14,37 @@
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">
@@ -25,6 +56,11 @@
<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>
+7 -3
View 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));
}
/*
+1 -1
View 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);
}
-8
View File
@@ -927,14 +927,6 @@
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;
@@ -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'
));
}
}
}
+4 -4
View 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');
@@ -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"
+1 -1
View 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);
@@ -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"
+3 -2
View 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();
+2 -2
View 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();
+10 -3
View 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;
}
+2 -2
View 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();
+24 -29
View 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');
}
-19
View File
@@ -35,13 +35,6 @@
min-height: 100px;
}
.wiki_editor {
width: 50%;
margin: auto;
margin-top: 10px;
margin-bottom: 10px;
}
.wiki_previewbox {
width: 50%;
margin: auto;
@@ -92,12 +85,6 @@
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;
+14 -12
View 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
*
+1 -1
View 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;
}
+1 -1
View File
@@ -1 +1 @@
define(["jquery","./tether"],function(a,b){return window.jQuery=a,window.Tether=b,require(["theme_boost/util","theme_boost/alert","theme_boost/button","theme_boost/carousel","theme_boost/collapse","theme_boost/dropdown","theme_boost/modal","theme_boost/scrollspy","theme_boost/tab","theme_boost/tooltip","theme_boost/popover"],function(){a("body").popover({selector:'[data-toggle="popover"]',trigger:"focus"})}),{}});
define(["jquery","./tether","core/event"],function(a,b,c){return window.jQuery=a,window.Tether=b,require(["theme_boost/util","theme_boost/alert","theme_boost/button","theme_boost/carousel","theme_boost/collapse","theme_boost/dropdown","theme_boost/modal","theme_boost/scrollspy","theme_boost/tab","theme_boost/tooltip","theme_boost/popover"],function(){a("body").popover({selector:'[data-toggle="popover"]',trigger:"focus"}),c.getLegacyEvents().done(function(b){a(document).on(b.FILTER_CONTENT_UPDATED,function(){a("body").popover({selector:'[data-toggle="popover"]',trigger:"focus"})})})}),{}});
+13 -1
View 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 {};
});
@@ -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);
}
}
+7 -1
View File
@@ -24,10 +24,16 @@
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);
+1
View File
@@ -792,6 +792,7 @@
#adminsettings .form-overridden {
@extend .form-control-feedback;
@extend .alert-info;
}
.modal.modal-in-page {
+3
View 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;
+4
View File
@@ -50,6 +50,10 @@
border: 1px solid #e3e3e3;
clear: both;
img {
max-width: 100%;
}
.contacts-area {
border-right: 1px solid #e3e3e3;
height: 600px;
+59 -20
View 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;
+12 -10
View File
@@ -58,13 +58,15 @@
]
}
}}
<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>
@@ -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}}
@@ -12,7 +103,8 @@
{{/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}}
@@ -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>
+1 -1
View 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 }}
+35 -38
View File
@@ -43,56 +43,53 @@
<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}}>
{{>theme_boost/header-secure}}
<div id="page" class="container-fluid">
{{! Secured 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>
{{/ output.should_display_navbar_logo }}
<div class="site-name">{{{ sitename }}}</div>
</a>
<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>
</div>
</header>
<div id="page" class="container">
{{{ output.full_header }}}
<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>
{{{ output.standard_end_of_body_html }}}
</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 }}}
</div>
</footer>
</body>
</html>
{{#js}}
@@ -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'));
}
}
+1 -1
View 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 {
+1 -1
View 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:
@@ -47,6 +47,10 @@
clear: both;
border-radius: 4px;
img {
max-width: 100%;
}
.contacts-area {
border-right: 1px solid #e3e3e3;
height: 600px;
@@ -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;
+12 -2
View 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;
+2 -2
View File
@@ -29,11 +29,11 @@
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.