Merge branch 'MOODLE_36_STABLE' into install_36_STABLE
This commit is contained in:
@@ -108,6 +108,16 @@ class category_bin extends base_bin {
|
||||
|
||||
require_once($CFG->dirroot . '/backup/util/includes/backup_includes.php');
|
||||
|
||||
// As far as recycle bin is using MODE_AUTOMATED, it observes the backup_auto_storage
|
||||
// setting (storing backups @ real location. For recycle bin we want to ensure that
|
||||
// backup files are always stored in Moodle file area. In order to achieve that, we
|
||||
// hack the setting here via $CFG->forced_plugin_settings, so it won't interfere other
|
||||
// operations. See MDL-65218 for more information.
|
||||
// This hack will be removed once recycle bin switches to use its own backup mode, with
|
||||
// own preferences and 100% appart from MODLE_AUTOMATED.
|
||||
// TODO: Remove this as part of MDL-65228.
|
||||
$CFG->forced_plugin_settings['backup'] = ['backup_auto_storage' => 0];
|
||||
|
||||
// Backup the course.
|
||||
$user = get_admin();
|
||||
$controller = new \backup_controller(
|
||||
@@ -115,11 +125,15 @@ class category_bin extends base_bin {
|
||||
$course->id,
|
||||
\backup::FORMAT_MOODLE,
|
||||
\backup::INTERACTIVE_NO,
|
||||
\backup::MODE_GENERAL,
|
||||
\backup::MODE_AUTOMATED,
|
||||
$user->id
|
||||
);
|
||||
$controller->execute_plan();
|
||||
|
||||
// We don't need the forced setting anymore, hence unsetting it.
|
||||
// TODO: Remove this as part of MDL-65228.
|
||||
unset($CFG->forced_plugin_settings['backup']);
|
||||
|
||||
// Grab the result.
|
||||
$result = $controller->get_results();
|
||||
if (!isset($result['backup_destination'])) {
|
||||
@@ -233,7 +247,7 @@ class category_bin extends base_bin {
|
||||
$tempdir,
|
||||
$course->id,
|
||||
\backup::INTERACTIVE_NO,
|
||||
\backup::MODE_GENERAL,
|
||||
\backup::MODE_AUTOMATED,
|
||||
$user->id,
|
||||
\backup::TARGET_NEW_COURSE
|
||||
);
|
||||
|
||||
@@ -112,6 +112,16 @@ class course_bin extends base_bin {
|
||||
return;
|
||||
}
|
||||
|
||||
// As far as recycle bin is using MODE_AUTOMATED, it observes the backup_auto_storage
|
||||
// setting (storing backups @ real location. For recycle bin we want to ensure that
|
||||
// backup files are always stored in Moodle file area. In order to achieve that, we
|
||||
// hack the setting here via $CFG->forced_plugin_settings, so it won't interfere other
|
||||
// operations. See MDL-65218 for more information.
|
||||
// This hack will be removed once recycle bin switches to use its own backup mode, with
|
||||
// own preferences and 100% appart from MODLE_AUTOMATED.
|
||||
// TODO: Remove this as part of MDL-65228.
|
||||
$CFG->forced_plugin_settings['backup'] = ['backup_auto_storage' => 0];
|
||||
|
||||
// Backup the activity.
|
||||
$user = get_admin();
|
||||
$controller = new \backup_controller(
|
||||
@@ -124,6 +134,10 @@ class course_bin extends base_bin {
|
||||
);
|
||||
$controller->execute_plan();
|
||||
|
||||
// We don't need the forced setting anymore, hence unsetting it.
|
||||
// TODO: Remove this as part of MDL-65228.
|
||||
unset($CFG->forced_plugin_settings['backup']);
|
||||
|
||||
// Grab the result.
|
||||
$result = $controller->get_results();
|
||||
if (!isset($result['backup_destination'])) {
|
||||
@@ -344,4 +358,4 @@ class course_bin extends base_bin {
|
||||
$context = \context_course::instance($this->_courseid);
|
||||
return has_capability('tool/recyclebin:deleteitems', $context);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -176,4 +176,116 @@ class tool_recyclebin_category_bin_tests extends advanced_testcase {
|
||||
$course = reset($courses);
|
||||
$this->assertEquals('Test course 2', $course->fullname);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provider for test_course_restore_with_userdata() and test_course_restore_without_userdata()
|
||||
*
|
||||
* Used to verify that recycle bin is immune to various settings. Provides plugin, name, value for
|
||||
* direct usage with set_config()
|
||||
*/
|
||||
public function recycle_bin_settings_provider() {
|
||||
return [
|
||||
'backup/backup_auto_storage moodle' => [[
|
||||
(object)['plugin' => 'backup', 'name' => 'backup_auto_storage', 'value' => 0],
|
||||
]],
|
||||
|
||||
'backup/backup_auto_storage external' => [[
|
||||
(object)['plugin' => 'backup', 'name' => 'backup_auto_storage', 'value' => 1],
|
||||
(object)['plugin' => 'backup', 'name' => 'backup_auto_destination', 'value' => true],
|
||||
]],
|
||||
|
||||
'backup/backup_auto_storage mixed' => [[
|
||||
(object)['plugin' => 'backup', 'name' => 'backup_auto_storage', 'value' => 2],
|
||||
(object)['plugin' => 'backup', 'name' => 'backup_auto_destination', 'value' => true],
|
||||
]],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that user data is restored when course is restored.
|
||||
*
|
||||
* @dataProvider recycle_bin_settings_provider
|
||||
* @param array $settings array of plugin, name, value stdClass().
|
||||
*/
|
||||
public function test_course_restore_with_userdata($settings) {
|
||||
global $DB;
|
||||
|
||||
// Force configuration changes from provider.
|
||||
foreach ($settings as $setting) {
|
||||
// Need to create a directory for backup_auto_destination.
|
||||
if ($setting->plugin === 'backup' && $setting->name === 'backup_auto_destination' && $setting->value === true) {
|
||||
$setting->value = make_request_directory();
|
||||
}
|
||||
set_config($setting->name, $setting->value, $setting->plugin);
|
||||
}
|
||||
|
||||
// We want user data to be included for this test.
|
||||
set_config('backup_auto_users', true, 'backup');
|
||||
|
||||
$student = $this->getDataGenerator()->create_and_enrol($this->course, 'student');
|
||||
|
||||
// Delete course.
|
||||
delete_course($this->course, false);
|
||||
$this->assertFalse($DB->record_exists('course', ['id' => $this->course->id]));
|
||||
|
||||
// Verify there is now a backup @ cat recycle bin file area.
|
||||
$recyclebin = new \tool_recyclebin\category_bin($this->course->category);
|
||||
$this->assertEquals(1, count($recyclebin->get_items()));
|
||||
|
||||
// Restore the recycle bin item.
|
||||
$recyclebin->restore_item(current($recyclebin->get_items()));
|
||||
|
||||
// Get the new course.
|
||||
$newcourse = $DB->get_record('course', ['shortname' => $this->course->shortname], '*', MUST_EXIST);
|
||||
|
||||
// Check that it was removed from the recycle bin.
|
||||
$this->assertEquals(0, count($recyclebin->get_items()));
|
||||
|
||||
// Verify that student DOES continue enrolled.
|
||||
$this->assertTrue(is_enrolled(context_course::instance($newcourse->id), $student->id));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that user data is not restored when course is restored.
|
||||
*
|
||||
* @dataProvider recycle_bin_settings_provider
|
||||
* @param array $settings array of plugin, name, value stdClass().
|
||||
*/
|
||||
public function test_course_restore_without_userdata($settings) {
|
||||
global $DB;
|
||||
|
||||
// Force configuration changes from provider.
|
||||
foreach ($settings as $setting) {
|
||||
// Need to create a directory for backup_auto_destination.
|
||||
if ($setting->plugin === 'backup' && $setting->name === 'backup_auto_destination' && $setting->value === true) {
|
||||
$setting->value = make_request_directory();
|
||||
}
|
||||
set_config($setting->name, $setting->value, $setting->plugin);
|
||||
}
|
||||
|
||||
// We want user data to be included for this test.
|
||||
set_config('backup_auto_users', false, 'backup');
|
||||
|
||||
$student = $this->getDataGenerator()->create_and_enrol($this->course, 'student');
|
||||
|
||||
// Delete course.
|
||||
delete_course($this->course, false);
|
||||
$this->assertFalse($DB->record_exists('course', ['id' => $this->course->id]));
|
||||
|
||||
// Verify there is now a backup @ cat recycle bin file area.
|
||||
$recyclebin = new \tool_recyclebin\category_bin($this->course->category);
|
||||
$this->assertEquals(1, count($recyclebin->get_items()));
|
||||
|
||||
// Restore the recycle bin item.
|
||||
$recyclebin->restore_item(current($recyclebin->get_items()));
|
||||
|
||||
// Get the new course.
|
||||
$newcourse = $DB->get_record('course', ['shortname' => $this->course->shortname], '*', MUST_EXIST);
|
||||
|
||||
// Check that it was removed from the recycle bin.
|
||||
$this->assertEquals(0, count($recyclebin->get_items()));
|
||||
|
||||
// Verify that student DOES NOT continue enrolled.
|
||||
$this->assertFalse(is_enrolled(context_course::instance($newcourse->id), $student->id));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -175,9 +175,45 @@ class tool_recyclebin_course_bin_tests extends advanced_testcase {
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that user data is restored when module is restored.
|
||||
* Provider for test_coursemodule_restore_with_userdata() and test_coursemodule_restore_without_userdata()
|
||||
*
|
||||
* Used to verify that recycle bin is immune to various settings. Provides plugin, name, value for
|
||||
* direct usage with set_config()
|
||||
*/
|
||||
public function test_coursemodule_restore_with_userdata() {
|
||||
public function recycle_bin_settings_provider() {
|
||||
return [
|
||||
'backup/backup_auto_storage moodle' => [[
|
||||
(object)['plugin' => 'backup', 'name' => 'backup_auto_storage', 'value' => 0],
|
||||
]],
|
||||
|
||||
'backup/backup_auto_storage external' => [[
|
||||
(object)['plugin' => 'backup', 'name' => 'backup_auto_storage', 'value' => 1],
|
||||
(object)['plugin' => 'backup', 'name' => 'backup_auto_destination', 'value' => true],
|
||||
]],
|
||||
|
||||
'backup/backup_auto_storage mixed' => [[
|
||||
(object)['plugin' => 'backup', 'name' => 'backup_auto_storage', 'value' => 2],
|
||||
(object)['plugin' => 'backup', 'name' => 'backup_auto_destination', 'value' => true],
|
||||
]],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that user data is restored when module is restored.
|
||||
*
|
||||
* @dataProvider recycle_bin_settings_provider
|
||||
* @param array $settings array of plugin, name, value stdClass().
|
||||
*/
|
||||
public function test_coursemodule_restore_with_userdata($settings) {
|
||||
// Force configuration changes from provider.
|
||||
foreach ($settings as $setting) {
|
||||
// Need to create a directory for backup_auto_destination.
|
||||
if ($setting->plugin === 'backup' && $setting->name === 'backup_auto_destination' && $setting->value === true) {
|
||||
$setting->value = make_request_directory();
|
||||
}
|
||||
set_config($setting->name, $setting->value, $setting->plugin);
|
||||
}
|
||||
|
||||
$student = $this->getDataGenerator()->create_and_enrol($this->course, 'student');
|
||||
$this->setUser($student);
|
||||
|
||||
@@ -211,8 +247,20 @@ class tool_recyclebin_course_bin_tests extends advanced_testcase {
|
||||
|
||||
/**
|
||||
* Tests that user data is not restored when module is restored.
|
||||
*
|
||||
* @dataProvider recycle_bin_settings_provider
|
||||
* @param array $settings array of plugin, name, value stdClass().
|
||||
*/
|
||||
public function test_coursemodule_restore_without_userdata() {
|
||||
public function test_coursemodule_restore_without_userdata($settings) {
|
||||
// Force configuration changes from provider.
|
||||
foreach ($settings as $setting) {
|
||||
// Need to create a directory for backup_auto_destination.
|
||||
if ($setting->plugin === 'backup' && $setting->name === 'backup_auto_destination' && $setting->value === true) {
|
||||
$setting->value = make_request_directory();
|
||||
}
|
||||
set_config($setting->name, $setting->value, $setting->plugin);
|
||||
}
|
||||
|
||||
$student = $this->getDataGenerator()->create_and_enrol($this->course, 'student');
|
||||
$this->setUser($student);
|
||||
|
||||
|
||||
Vendored
+1
-1
File diff suppressed because one or more lines are too long
@@ -352,30 +352,21 @@ define(['jquery', 'core/ajax', 'core/templates', 'core/notification', 'core/str'
|
||||
* @param {String} image new image name ("i/show", "i/hide", etc.)
|
||||
* @param {String} stringname new string for the action menu item
|
||||
* @param {String} stringcomponent
|
||||
* @param {String} titlestr string for "title" attribute (if different from stringname)
|
||||
* @param {String} titlecomponent
|
||||
* @param {String} titlestr not used
|
||||
* @param {String} titlecomponent not used
|
||||
* @param {String} newaction new value for data-action attribute of the link
|
||||
* @return {Promise} promise which is resolved when the replacement has completed
|
||||
*/
|
||||
var replaceActionItem = function(actionitem, image, stringname,
|
||||
stringcomponent, titlestr, titlecomponent, newaction) {
|
||||
|
||||
|
||||
var stringRequests = [{key: stringname, component: stringcomponent}];
|
||||
if (titlestr) {
|
||||
stringRequests.push({key: titlestr, component: titlecomponent});
|
||||
}
|
||||
// Do not provide an icon with duplicate, different text to the menu item.
|
||||
|
||||
return str.get_strings(stringRequests).then(function(strings) {
|
||||
actionitem.find('span.menu-action-text').html(strings[0]);
|
||||
actionitem.attr('title', strings[0]);
|
||||
|
||||
var title = '';
|
||||
if (titlestr) {
|
||||
title = strings[1];
|
||||
actionitem.attr('title', title);
|
||||
}
|
||||
return templates.renderPix(image, 'core', title);
|
||||
return templates.renderPix(image, 'core');
|
||||
}).then(function(pixhtml) {
|
||||
actionitem.find('.icon').replaceWith(pixhtml);
|
||||
actionitem.attr('data-action', newaction);
|
||||
|
||||
+14
-15
@@ -117,10 +117,9 @@ abstract class format_section_renderer_base extends plugin_renderer_base {
|
||||
$name = empty($value['name']) ? '' : $value['name'];
|
||||
$attr = empty($value['attr']) ? array() : $value['attr'];
|
||||
$class = empty($value['pixattr']['class']) ? '' : $value['pixattr']['class'];
|
||||
$alt = empty($value['pixattr']['alt']) ? '' : $value['pixattr']['alt'];
|
||||
$al = new action_menu_link_secondary(
|
||||
new moodle_url($url),
|
||||
new pix_icon($icon, $alt, null, array('class' => "smallicon " . $class)),
|
||||
new pix_icon($icon, '', null, array('class' => "smallicon " . $class)),
|
||||
$name,
|
||||
$attr
|
||||
);
|
||||
@@ -328,8 +327,8 @@ abstract class format_section_renderer_base extends plugin_renderer_base {
|
||||
'url' => new moodle_url('/course/editsection.php', array('id' => $section->id, 'sr' => $sectionreturn)),
|
||||
'icon' => 'i/settings',
|
||||
'name' => $streditsection,
|
||||
'pixattr' => array('class' => '', 'alt' => $streditsection),
|
||||
'attr' => array('class' => 'icon edit', 'title' => $streditsection));
|
||||
'pixattr' => array('class' => ''),
|
||||
'attr' => array('class' => 'icon edit'));
|
||||
}
|
||||
|
||||
if ($section->section) {
|
||||
@@ -343,8 +342,8 @@ abstract class format_section_renderer_base extends plugin_renderer_base {
|
||||
'url' => $url,
|
||||
'icon' => 'i/hide',
|
||||
'name' => $strhidefromothers,
|
||||
'pixattr' => array('class' => '', 'alt' => $strhidefromothers),
|
||||
'attr' => array('class' => 'icon editing_showhide', 'title' => $strhidefromothers,
|
||||
'pixattr' => array('class' => ''),
|
||||
'attr' => array('class' => 'icon editing_showhide',
|
||||
'data-sectionreturn' => $sectionreturn, 'data-action' => 'hide'));
|
||||
} else {
|
||||
$strshowfromothers = get_string('showfromothers', 'format_'.$course->format);
|
||||
@@ -353,8 +352,8 @@ abstract class format_section_renderer_base extends plugin_renderer_base {
|
||||
'url' => $url,
|
||||
'icon' => 'i/show',
|
||||
'name' => $strshowfromothers,
|
||||
'pixattr' => array('class' => '', 'alt' => $strshowfromothers),
|
||||
'attr' => array('class' => 'icon editing_showhide', 'title' => $strshowfromothers,
|
||||
'pixattr' => array('class' => ''),
|
||||
'attr' => array('class' => 'icon editing_showhide',
|
||||
'data-sectionreturn' => $sectionreturn, 'data-action' => 'show'));
|
||||
}
|
||||
}
|
||||
@@ -370,8 +369,8 @@ abstract class format_section_renderer_base extends plugin_renderer_base {
|
||||
'url' => $url,
|
||||
'icon' => 'i/up',
|
||||
'name' => $strmoveup,
|
||||
'pixattr' => array('class' => '', 'alt' => $strmoveup),
|
||||
'attr' => array('class' => 'icon moveup', 'title' => $strmoveup));
|
||||
'pixattr' => array('class' => ''),
|
||||
'attr' => array('class' => 'icon moveup'));
|
||||
}
|
||||
|
||||
$url = clone($baseurl);
|
||||
@@ -383,8 +382,8 @@ abstract class format_section_renderer_base extends plugin_renderer_base {
|
||||
'url' => $url,
|
||||
'icon' => 'i/down',
|
||||
'name' => $strmovedown,
|
||||
'pixattr' => array('class' => '', 'alt' => $strmovedown),
|
||||
'attr' => array('class' => 'icon movedown', 'title' => $strmovedown));
|
||||
'pixattr' => array('class' => ''),
|
||||
'attr' => array('class' => 'icon movedown'));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -405,8 +404,8 @@ abstract class format_section_renderer_base extends plugin_renderer_base {
|
||||
'url' => $url,
|
||||
'icon' => 'i/delete',
|
||||
'name' => $strdelete,
|
||||
'pixattr' => array('class' => '', 'alt' => $strdelete),
|
||||
'attr' => array('class' => 'icon editing_delete', 'title' => $strdelete));
|
||||
'pixattr' => array('class' => ''),
|
||||
'attr' => array('class' => 'icon editing_delete'));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1009,7 +1008,7 @@ abstract class format_section_renderer_base extends plugin_renderer_base {
|
||||
if ($sectionreturn !== null) {
|
||||
$url->param('sectionreturn', $sectionreturn);
|
||||
}
|
||||
$icon = $this->output->pix_icon('t/add', $straddsections);
|
||||
$icon = $this->output->pix_icon('t/add', '');
|
||||
$newsections = $maxsections - $lastsection;
|
||||
echo html_writer::link($url, $icon . $straddsections,
|
||||
array('class' => 'add-sections', 'data-add-sections' => $straddsections, 'new-sections' => $newsections));
|
||||
|
||||
@@ -123,21 +123,19 @@ class format_topics_renderer extends format_section_renderer_base {
|
||||
if ($section->section && has_capability('moodle/course:setcurrentsection', $coursecontext)) {
|
||||
if ($course->marker == $section->section) { // Show the "light globe" on/off.
|
||||
$url->param('marker', 0);
|
||||
$markedthistopic = get_string('markedthistopic');
|
||||
$highlightoff = get_string('highlightoff');
|
||||
$controls['highlight'] = array('url' => $url, "icon" => 'i/marked',
|
||||
'name' => $highlightoff,
|
||||
'pixattr' => array('class' => '', 'alt' => $markedthistopic),
|
||||
'attr' => array('class' => 'editing_highlight', 'title' => $markedthistopic,
|
||||
'pixattr' => array('class' => ''),
|
||||
'attr' => array('class' => 'editing_highlight',
|
||||
'data-action' => 'removemarker'));
|
||||
} else {
|
||||
$url->param('marker', $section->section);
|
||||
$markthistopic = get_string('markthistopic');
|
||||
$highlight = get_string('highlight');
|
||||
$controls['highlight'] = array('url' => $url, "icon" => 'i/marker',
|
||||
'name' => $highlight,
|
||||
'pixattr' => array('class' => '', 'alt' => $markthistopic),
|
||||
'attr' => array('class' => 'editing_highlight', 'title' => $markthistopic,
|
||||
'pixattr' => array('class' => ''),
|
||||
'attr' => array('class' => 'editing_highlight',
|
||||
'data-action' => 'setmarker'));
|
||||
}
|
||||
}
|
||||
|
||||
+11
-11
@@ -1891,7 +1891,7 @@ function course_get_cm_edit_actions(cm_info $mod, $indent = -1, $sr = null) {
|
||||
if ($hasmanageactivities) {
|
||||
$actions['update'] = new action_menu_link_secondary(
|
||||
new moodle_url($baseurl, array('update' => $mod->id)),
|
||||
new pix_icon('t/edit', $str->editsettings, 'moodle', array('class' => 'iconsmall', 'title' => '')),
|
||||
new pix_icon('t/edit', '', 'moodle', array('class' => 'iconsmall')),
|
||||
$str->editsettings,
|
||||
array('class' => 'editing_update', 'data-action' => 'update')
|
||||
);
|
||||
@@ -1917,7 +1917,7 @@ function course_get_cm_edit_actions(cm_info $mod, $indent = -1, $sr = null) {
|
||||
}
|
||||
$actions['moveright'] = new action_menu_link_secondary(
|
||||
new moodle_url($baseurl, array('id' => $mod->id, 'indent' => '1')),
|
||||
new pix_icon($rightarrow, $str->moveright, 'moodle', array('class' => 'iconsmall', 'title' => '')),
|
||||
new pix_icon($rightarrow, '', 'moodle', array('class' => 'iconsmall')),
|
||||
$str->moveright,
|
||||
array('class' => 'editing_moveright ' . $enabledclass, 'data-action' => 'moveright',
|
||||
'data-keepopen' => true, 'data-sectionreturn' => $sr)
|
||||
@@ -1930,7 +1930,7 @@ function course_get_cm_edit_actions(cm_info $mod, $indent = -1, $sr = null) {
|
||||
}
|
||||
$actions['moveleft'] = new action_menu_link_secondary(
|
||||
new moodle_url($baseurl, array('id' => $mod->id, 'indent' => '-1')),
|
||||
new pix_icon($leftarrow, $str->moveleft, 'moodle', array('class' => 'iconsmall', 'title' => '')),
|
||||
new pix_icon($leftarrow, '', 'moodle', array('class' => 'iconsmall')),
|
||||
$str->moveleft,
|
||||
array('class' => 'editing_moveleft ' . $enabledclass, 'data-action' => 'moveleft',
|
||||
'data-keepopen' => true, 'data-sectionreturn' => $sr)
|
||||
@@ -1953,7 +1953,7 @@ function course_get_cm_edit_actions(cm_info $mod, $indent = -1, $sr = null) {
|
||||
if ($displayedoncoursepage) {
|
||||
$actions['hide'] = new action_menu_link_secondary(
|
||||
new moodle_url($baseurl, array('hide' => $mod->id)),
|
||||
new pix_icon('t/hide', $str->modhide, 'moodle', array('class' => 'iconsmall', 'title' => '')),
|
||||
new pix_icon('t/hide', '', 'moodle', array('class' => 'iconsmall')),
|
||||
$str->modhide,
|
||||
array('class' => 'editing_hide', 'data-action' => 'hide')
|
||||
);
|
||||
@@ -1961,7 +1961,7 @@ function course_get_cm_edit_actions(cm_info $mod, $indent = -1, $sr = null) {
|
||||
// Offer to "show" only if the section is visible.
|
||||
$actions['show'] = new action_menu_link_secondary(
|
||||
new moodle_url($baseurl, array('show' => $mod->id)),
|
||||
new pix_icon('t/show', $str->modshow, 'moodle', array('class' => 'iconsmall', 'title' => '')),
|
||||
new pix_icon('t/show', '', 'moodle', array('class' => 'iconsmall')),
|
||||
$str->modshow,
|
||||
array('class' => 'editing_show', 'data-action' => 'show')
|
||||
);
|
||||
@@ -1971,7 +1971,7 @@ function course_get_cm_edit_actions(cm_info $mod, $indent = -1, $sr = null) {
|
||||
// When making the "stealth" module unavailable we perform the same action as hiding the visible module.
|
||||
$actions['hide'] = new action_menu_link_secondary(
|
||||
new moodle_url($baseurl, array('hide' => $mod->id)),
|
||||
new pix_icon('t/unblock', $str->makeunavailable, 'moodle', array('class' => 'iconsmall', 'title' => '')),
|
||||
new pix_icon('t/unblock', '', 'moodle', array('class' => 'iconsmall')),
|
||||
$str->makeunavailable,
|
||||
array('class' => 'editing_makeunavailable', 'data-action' => 'hide', 'data-sectionreturn' => $sr)
|
||||
);
|
||||
@@ -1982,7 +1982,7 @@ function course_get_cm_edit_actions(cm_info $mod, $indent = -1, $sr = null) {
|
||||
$action = $sectionvisible ? 'stealth' : 'show';
|
||||
$actions[$action] = new action_menu_link_secondary(
|
||||
new moodle_url($baseurl, array($action => $mod->id)),
|
||||
new pix_icon('t/block', $str->makeavailable, 'moodle', array('class' => 'iconsmall', 'title' => '')),
|
||||
new pix_icon('t/block', '', 'moodle', array('class' => 'iconsmall')),
|
||||
$str->makeavailable,
|
||||
array('class' => 'editing_makeavailable', 'data-action' => $action, 'data-sectionreturn' => $sr)
|
||||
);
|
||||
@@ -1995,7 +1995,7 @@ function course_get_cm_edit_actions(cm_info $mod, $indent = -1, $sr = null) {
|
||||
course_allowed_module($mod->get_course(), $mod->modname)) {
|
||||
$actions['duplicate'] = new action_menu_link_secondary(
|
||||
new moodle_url($baseurl, array('duplicate' => $mod->id)),
|
||||
new pix_icon('t/copy', $str->duplicate, 'moodle', array('class' => 'iconsmall', 'title' => '')),
|
||||
new pix_icon('t/copy', '', 'moodle', array('class' => 'iconsmall')),
|
||||
$str->duplicate,
|
||||
array('class' => 'editing_duplicate', 'data-action' => 'duplicate', 'data-sectionreturn' => $sr)
|
||||
);
|
||||
@@ -2026,7 +2026,7 @@ function course_get_cm_edit_actions(cm_info $mod, $indent = -1, $sr = null) {
|
||||
|
||||
$actions[$actionname] = new action_menu_link_primary(
|
||||
new moodle_url($baseurl, array('id' => $mod->id, 'groupmode' => $nextgroupmode)),
|
||||
new pix_icon($groupimage, $grouptitle, 'moodle', array('class' => 'iconsmall')),
|
||||
new pix_icon($groupimage, '', 'moodle', array('class' => 'iconsmall')),
|
||||
$grouptitle,
|
||||
array('class' => 'editing_'. $actionname, 'data-action' => $nextactionname,
|
||||
'aria-live' => 'assertive', 'data-sectionreturn' => $sr)
|
||||
@@ -2040,7 +2040,7 @@ function course_get_cm_edit_actions(cm_info $mod, $indent = -1, $sr = null) {
|
||||
if (has_capability('moodle/role:assign', $modcontext)){
|
||||
$actions['assign'] = new action_menu_link_secondary(
|
||||
new moodle_url('/admin/roles/assign.php', array('contextid' => $modcontext->id)),
|
||||
new pix_icon('t/assignroles', $str->assign, 'moodle', array('class' => 'iconsmall', 'title' => '')),
|
||||
new pix_icon('t/assignroles', '', 'moodle', array('class' => 'iconsmall')),
|
||||
$str->assign,
|
||||
array('class' => 'editing_assign', 'data-action' => 'assignroles', 'data-sectionreturn' => $sr)
|
||||
);
|
||||
@@ -2050,7 +2050,7 @@ function course_get_cm_edit_actions(cm_info $mod, $indent = -1, $sr = null) {
|
||||
if ($hasmanageactivities) {
|
||||
$actions['delete'] = new action_menu_link_secondary(
|
||||
new moodle_url($baseurl, array('delete' => $mod->id)),
|
||||
new pix_icon('t/delete', $str->delete, 'moodle', array('class' => 'iconsmall', 'title' => '')),
|
||||
new pix_icon('t/delete', '', 'moodle', array('class' => 'iconsmall')),
|
||||
$str->delete,
|
||||
array('class' => 'editing_delete', 'data-action' => 'delete', 'data-sectionreturn' => $sr)
|
||||
);
|
||||
|
||||
@@ -304,7 +304,7 @@ class behat_course extends behat_base {
|
||||
|
||||
// Click on highlight topic link.
|
||||
$this->execute('behat_general::i_click_on_in_the',
|
||||
array(get_string('markthistopic'), "link", $this->escape($xpath), "xpath_element")
|
||||
array(get_string('highlight'), "link", $this->escape($xpath), "xpath_element")
|
||||
);
|
||||
}
|
||||
|
||||
@@ -326,7 +326,7 @@ class behat_course extends behat_base {
|
||||
|
||||
// Click on un-highlight topic link.
|
||||
$this->execute('behat_general::i_click_on_in_the',
|
||||
array(get_string('markedthistopic'), "link", $this->escape($xpath), "xpath_element")
|
||||
array(get_string('highlightoff'), "link", $this->escape($xpath), "xpath_element")
|
||||
);
|
||||
}
|
||||
|
||||
@@ -444,7 +444,7 @@ class behat_course extends behat_base {
|
||||
$xpath = $this->section_exists($sectionnumber);
|
||||
|
||||
// The important checking, we can not check the img.
|
||||
$this->execute('behat_general::should_exist_in_the', ['This topic is highlighted as the current topic', 'icon', $xpath, 'xpath_element']);
|
||||
$this->execute('behat_general::should_exist_in_the', ['Remove highlight', 'link', $xpath, 'xpath_element']);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -775,11 +775,10 @@ class behat_course extends behat_base {
|
||||
// Ensure the destination is valid.
|
||||
$sectionxpath = $this->section_exists($sectionnumber);
|
||||
|
||||
$activitynode = $this->get_activity_element('Move', 'icon', $activityname);
|
||||
|
||||
// JS enabled.
|
||||
if ($this->running_javascript()) {
|
||||
|
||||
$activitynode = $this->get_activity_element('Move', 'icon', $activityname);
|
||||
$destinationxpath = $sectionxpath . "/descendant::ul[contains(concat(' ', normalize-space(@class), ' '), ' yui3-dd-drop ')]";
|
||||
|
||||
$this->execute("behat_general::i_drag_and_i_drop_it_in",
|
||||
@@ -1165,9 +1164,9 @@ class behat_course extends behat_base {
|
||||
|
||||
// Checking the show button alt text and show icon.
|
||||
$showtext = get_string('showfromothers', $courseformat);
|
||||
$linkxpath = $xpath . "/descendant::a[@title=" . behat_context_helper::escape($showtext) . "]";
|
||||
$linkxpath = $xpath . "//a[*[contains(text(), " . behat_context_helper::escape($showtext) . ")]]";
|
||||
|
||||
$exception = new ElementNotFoundException($this->getSession(), 'Show section link ');
|
||||
$exception = new ElementNotFoundException($this->getSession(), 'Show section link');
|
||||
|
||||
// Returing the link so both Non-JS and JS browsers can interact with it.
|
||||
return $this->find('xpath', $linkxpath, $exception);
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
define(["core/icon_system","jquery","core/ajax","core/mustache","core/localstorage","core/url"],function(a,b,c,d,e,f){var g=null,h=null,i=function(){a.apply(this,arguments)};return i.prototype=Object.create(a.prototype),i.prototype.init=function(){if(g)return b.when(this);var a=e.get("core/iconmap-fontawesome");return a&&(a=JSON.parse(a)),a?(g=a,b.when(this)):(null===h&&(h=c.call([{methodname:"core_output_load_fontawesome_icon_map",args:[]}],!0,!1)[0]),h.then(function(a){return g={},b.each(a,function(a,b){g[b.component+"/"+b.pix]=b.to}),e.set("core/iconmap-fontawesome",JSON.stringify(g)),this}.bind(this)))},i.prototype.renderIcon=function(a,b,c,e){var h=g[b+"/"+a],i=!1;if("undefined"==typeof h){var j=f.imageUrl(a,b);i={attributes:[{name:"src",value:j},{name:"alt",value:c},{name:"title",value:c}]}}var k={key:h,title:c,alt:c,unmappedIcon:i},l=d.render(e,k);return l.trim()},i.prototype.getTemplateName=function(){return"core/pix_icon_fontawesome"},i});
|
||||
define(["core/icon_system","jquery","core/ajax","core/mustache","core/localstorage","core/url"],function(a,b,c,d,e,f){var g=null,h=null,i=function(){a.apply(this,arguments)};return i.prototype=Object.create(a.prototype),i.prototype.init=function(){if(g)return b.when(this);var a=e.get("core/iconmap-fontawesome");return a&&(a=JSON.parse(a)),a?(g=a,b.when(this)):(null===h&&(h=c.call([{methodname:"core_output_load_fontawesome_icon_map",args:[]}],!0,!1)[0]),h.then(function(a){return g={},b.each(a,function(a,b){g[b.component+"/"+b.pix]=b.to}),e.set("core/iconmap-fontawesome",JSON.stringify(g)),this}.bind(this)))},i.prototype.renderIcon=function(a,b,c,e){var h=g[b+"/"+a],i=!1;if("undefined"==typeof h){var j=f.imageUrl(a,b);i={attributes:[{name:"src",value:j},{name:"alt",value:c},{name:"title",value:c}]}}var k={key:h,title:c,alt:c,unmappedIcon:i};"undefined"!=typeof c&&""!==c||(k["aria-hidden"]=!0);var l=d.render(e,k);return l.trim()},i.prototype.getTemplateName=function(){return"core/pix_icon_fontawesome"},i});
|
||||
+1
-1
@@ -1 +1 @@
|
||||
define(["core/icon_system","core/url","core/mustache"],function(a,b,c){var d=function(){a.apply(this,arguments)};return d.prototype=Object.create(a.prototype),d.prototype.renderIcon=function(a,d,e,f){var g=b.imageUrl(a,d),h={attributes:[{name:"src",value:g},{name:"alt",value:e},{name:"title",value:e}]},i=c.render(f,h);return i.trim()},d.prototype.getTemplateName=function(){return"core/pix_icon"},d});
|
||||
define(["core/icon_system","core/url","core/mustache"],function(a,b,c){var d=function(){a.apply(this,arguments)};return d.prototype=Object.create(a.prototype),d.prototype.renderIcon=function(a,d,e,f){var g=b.imageUrl(a,d),h={attributes:[{name:"src",value:g},{name:"alt",value:e},{name:"title",value:e}]};"undefined"!=typeof e&&""!=e||h.attributes.push({name:"aria-hidden",value:"true"});var i=c.render(f,h);return i.trim()},d.prototype.getTemplateName=function(){return"core/pix_icon"},d});
|
||||
@@ -105,6 +105,10 @@ define(['core/icon_system', 'jquery', 'core/ajax', 'core/mustache', 'core/locals
|
||||
unmappedIcon: unmappedIcon
|
||||
};
|
||||
|
||||
if (typeof title === "undefined" || title === '') {
|
||||
context['aria-hidden'] = true;
|
||||
}
|
||||
|
||||
var result = Mustache.render(template, context);
|
||||
return result.trim();
|
||||
};
|
||||
|
||||
@@ -52,6 +52,10 @@ define(['core/icon_system', 'core/url', 'core/mustache'],
|
||||
{name: 'title', value: title}
|
||||
]
|
||||
};
|
||||
if (typeof title === "undefined" || title == "") {
|
||||
templatecontext.attributes.push({name: 'aria-hidden', value: 'true'});
|
||||
}
|
||||
|
||||
var result = Mustache.render(template, templatecontext);
|
||||
return result.trim();
|
||||
};
|
||||
|
||||
@@ -89,6 +89,7 @@ class behat_partial_named_selector extends \Behat\Mink\Selector\PartialNamedSele
|
||||
'file' => 'file',
|
||||
'filemanager' => 'filemanager',
|
||||
'group_message' => 'group_message',
|
||||
'group_message_conversation' => 'group_message_conversation',
|
||||
'group_message_header' => 'group_message_header',
|
||||
'group_message_member' => 'group_message_member',
|
||||
'group_message_tab' => 'group_message_tab',
|
||||
@@ -154,7 +155,10 @@ XPATH
|
||||
, 'group_message' => <<<XPATH
|
||||
.//*[@data-conversation-id]//img[contains(@alt, %locator%)]/..
|
||||
XPATH
|
||||
, 'group_message_header' => <<<XPATH
|
||||
, 'group_message_conversation' => <<<XPATH
|
||||
.//*[@data-region='message-drawer' and contains(., %locator%)]//div[@data-region='content-message-container']
|
||||
XPATH
|
||||
, 'group_message_header' => <<<XPATH
|
||||
.//*[@data-region='message-drawer']//div[@data-region='header-container']//*[text()[contains(., %locator%)]]
|
||||
XPATH
|
||||
, 'group_message_member' => <<<XPATH
|
||||
|
||||
@@ -48,6 +48,19 @@ class behat_message extends behat_base {
|
||||
$this->execute("behat_general::i_click_on", [get_string('togglemessagemenu', 'core_message'), 'link']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Open the messaging conversation list.
|
||||
*
|
||||
* @Given /^I open the "(?P<tab_string>(?:[^"]|\\")*)" conversations list/
|
||||
* @param string $tab
|
||||
*/
|
||||
public function i_open_the_conversations_list(string $tab) {
|
||||
$this->execute('behat_general::i_click_on', [
|
||||
$this->escape($tab),
|
||||
'group_message_tab'
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Open the messaging UI.
|
||||
*
|
||||
@@ -153,7 +166,7 @@ class behat_message extends behat_base {
|
||||
array("//textarea[@data-region='send-message-txt']", $this->escape($messagecontent))
|
||||
);
|
||||
|
||||
$this->execute("behat_forms::press_button", get_string('send', 'message'));
|
||||
$this->execute("behat_forms::press_button", get_string('sendmessage', 'message'));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -204,4 +217,19 @@ class behat_message extends behat_base {
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Select a user in a specific messaging UI conversations list.
|
||||
*
|
||||
* @Given /^I select "(?P<conv_name_string>(?:[^"]|\\")*)" conversation in the "(?P<list_name_string>(?:[^"]|\\")*)" conversations list$/
|
||||
* @param string $convname
|
||||
* @param string $listname
|
||||
*/
|
||||
public function i_select_conversation_in_the_conversations_list(string $convname, string $listname) {
|
||||
$xpath = '//*[@data-region="message-drawer"]//div[@data-region="view-overview-'.
|
||||
$this->escape($listname).
|
||||
'"]//*[@data-conversation-id]//img[contains(@alt,"'.
|
||||
$this->escape($convname).'")]';
|
||||
$this->execute('behat_general::i_click_on', array($xpath, 'xpath_element'));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,6 +31,7 @@ Feature: Manage contacts
|
||||
And I open contact menu
|
||||
And I click on "Add to contacts" "link"
|
||||
And I click on "Add" "button"
|
||||
And I should see "Contact request sent"
|
||||
And I log out
|
||||
And I log in as "student4"
|
||||
Then I should see "2" in the "//*[@data-region='count-container']" "xpath_element"
|
||||
@@ -41,6 +42,7 @@ Feature: Manage contacts
|
||||
And I click on "Student 1 Would like to contact you" "link"
|
||||
Then I should see "Accept and add to contacts"
|
||||
And I click on "Accept and add to contacts" "link_or_button"
|
||||
And I should not see "Accept and add to contacts"
|
||||
And I log out
|
||||
And I log in as "student1"
|
||||
And I open messaging
|
||||
@@ -54,6 +56,7 @@ Feature: Manage contacts
|
||||
And I open contact menu
|
||||
And I click on "Add to contacts" "link"
|
||||
And I click on "Add" "button"
|
||||
And I should see "Contact request sent"
|
||||
And I log out
|
||||
And I log in as "student3"
|
||||
Then I should see "1" in the "//*[@data-region='count-container']" "xpath_element"
|
||||
@@ -64,6 +67,7 @@ Feature: Manage contacts
|
||||
And I click on "Student 1 Would like to contact you" "link"
|
||||
Then I should see "Accept and add to contacts"
|
||||
And I click on "Decline" "link_or_button"
|
||||
And I should not see "Accept and add to contacts"
|
||||
And I open contact menu
|
||||
Then I should see "Add to contacts" in the "//div[@data-region='header-container']" "xpath_element"
|
||||
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
@core @core_message @javascript
|
||||
Feature: Message send messages
|
||||
In order to communicate with fellow users
|
||||
As a user
|
||||
I need to be able to send a message
|
||||
|
||||
Background:
|
||||
Given the following "courses" exist:
|
||||
| fullname | shortname | category | groupmode |
|
||||
| Course 1 | C1 | 0 | 1 |
|
||||
And the following "users" exist:
|
||||
| username | firstname | lastname | email |
|
||||
| student1 | Student | 1 | student1@example.com |
|
||||
| student2 | Student | 2 | student2@example.com |
|
||||
And the following "course enrolments" exist:
|
||||
| user | course | role |
|
||||
| student1 | C1 | student |
|
||||
| student2 | C1 | student |
|
||||
And the following "groups" exist:
|
||||
| name | course | idnumber | enablemessaging |
|
||||
| Group 1 | C1 | G1 | 1 |
|
||||
And the following "group members" exist:
|
||||
| user | group |
|
||||
| student1 | G1 |
|
||||
| student2 | G1 |
|
||||
And the following config values are set as admin:
|
||||
| messaging | 1 |
|
||||
|
||||
Scenario: Send a message to a group conversation
|
||||
Given I log in as "student1"
|
||||
And I open messaging
|
||||
And "Group 1" "group_message" should exist
|
||||
And I select "Group 1" conversation in messaging
|
||||
When I send "Hi!" message in the message area
|
||||
Then I should see "Hi!" in the "Group 1" "group_message_conversation"
|
||||
And I should see "##today##j F##" in the "Group 1" "group_message_conversation"
|
||||
And I log out
|
||||
And I log in as "student2"
|
||||
And I open messaging
|
||||
And "Group 1" "group_message" should exist
|
||||
And I select "Group 1" conversation in messaging
|
||||
And I should see "Hi!" in the "Group 1" "group_message_conversation"
|
||||
|
||||
Scenario: Send a message to a starred conversation
|
||||
Given I log in as "student1"
|
||||
When I open messaging
|
||||
Then "Group 1" "group_message" should exist
|
||||
And I select "Group 1" conversation in the "group-messages" conversations list
|
||||
And I open contact menu
|
||||
And I click on "Star" "link" in the "//div[@data-region='header-container']" "xpath_element"
|
||||
And I go back in "view-conversation" message drawer
|
||||
And I open the "Starred" conversations list
|
||||
And I should see "Group 1"
|
||||
And I select "Group 1" conversation in the "favourites" conversations list
|
||||
And I send "Hi!" message in the message area
|
||||
And I should see "Hi!" in the "Group 1" "group_message_conversation"
|
||||
And I should see "##today##j F##" in the "Group 1" "group_message_conversation"
|
||||
And I go back in "view-conversation" message drawer
|
||||
And I open the "Group" conversations list
|
||||
And I should not see "Group 1" in the "Group" "group_message_tab"
|
||||
|
||||
Scenario: Send a message to a private conversation via contact tab
|
||||
Given the following "message contacts" exist:
|
||||
| user | contact |
|
||||
| student1 | student2 |
|
||||
And I log in as "student1"
|
||||
And I open messaging
|
||||
And I click on "Contacts" "link"
|
||||
And I click on "Student 2" "link" in the "//*[@data-section='contacts']" "xpath_element"
|
||||
When I send "Hi!" message in the message area
|
||||
Then I should see "Hi!" in the "Student 2" "group_message_conversation"
|
||||
And I should see "##today##j F##" in the "Student 2" "group_message_conversation"
|
||||
|
||||
Scenario: Try to send a message to a private conversation is not contact but you are allowed to send a message
|
||||
Given I log in as "student1"
|
||||
And I open messaging
|
||||
When I send "Hi!" message to "Student 2" user
|
||||
Then I should see "Hi!" in the "Student 2" "group_message_conversation"
|
||||
And I should see "##today##j F##" in the "Student 2" "group_message_conversation"
|
||||
And I log out
|
||||
And I log in as "student2"
|
||||
And I open messaging
|
||||
And I select "Student 1" conversation in messaging
|
||||
And I should see "Hi!" in the "Student 1" "group_message_conversation"
|
||||
+2
-2
@@ -29,11 +29,11 @@
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
$version = 2018120303.08; // 20181203 = branching date YYYYMMDD - do not modify!
|
||||
$version = 2018120303.09; // 20181203 = branching date YYYYMMDD - do not modify!
|
||||
// RR = release increments - 00 in DEV branches.
|
||||
// .XX = incremental changes.
|
||||
|
||||
$release = '3.6.3+ (Build: 20190406)'; // Human-friendly version name
|
||||
$release = '3.6.3+ (Build: 20190410)'; // Human-friendly version name
|
||||
|
||||
$branch = '36'; // This version's branch.
|
||||
$maturity = MATURITY_STABLE; // This version's maturity level.
|
||||
|
||||
Reference in New Issue
Block a user