MDL-74977 core_courseformat: add expanded section update actions

This also fix the Random "Course content preferences" Behat failure
This commit is contained in:
Huong Nguyen 2024-03-21 17:49:40 +07:00
parent b621a7e4b3
commit 6d40f11806
No known key found for this signature in database
GPG Key ID: 40D88AB693A3E72A
13 changed files with 214 additions and 48 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -179,15 +179,12 @@ export default class Component extends BaseComponent {
const toggler = section.querySelector(this.selectors.COLLAPSE);
const isCollapsed = toggler?.classList.contains(this.classes.COLLAPSED) ?? false;
if (isChevron || isCollapsed) {
// Update the state.
const sectionId = section.getAttribute('data-id');
this.reactive.dispatch(
'sectionContentCollapsed',
[sectionId],
!isCollapsed
);
}
const sectionId = section.getAttribute('data-id');
this.reactive.dispatch(
'sectionContentCollapsed',
[sectionId],
!isCollapsed,
);
}
}

View File

@ -572,12 +572,16 @@ export default class {
* @param {boolean} collapsed the new collapsed value
*/
async sectionIndexCollapsed(stateManager, sectionIds, collapsed) {
const collapsedIds = this._updateStateSectionPreference(stateManager, 'indexcollapsed', sectionIds, collapsed);
if (!collapsedIds) {
const affectedSections = this._updateStateSectionPreference(stateManager, 'indexcollapsed', sectionIds, collapsed);
if (!affectedSections) {
return;
}
const course = stateManager.get('course');
await this._callEditWebservice('section_index_collapsed', course.id, collapsedIds);
let actionName = 'section_index_collapsed';
if (!collapsed) {
actionName = 'section_index_expanded';
}
await this._callEditWebservice(actionName, course.id, affectedSections);
}
/**
@ -599,12 +603,16 @@ export default class {
* @param {boolean} collapsed the new collapsed value
*/
async sectionContentCollapsed(stateManager, sectionIds, collapsed) {
const collapsedIds = this._updateStateSectionPreference(stateManager, 'contentcollapsed', sectionIds, collapsed);
if (!collapsedIds) {
const affectedSections = this._updateStateSectionPreference(stateManager, 'contentcollapsed', sectionIds, collapsed);
if (!affectedSections) {
return;
}
const course = stateManager.get('course');
await this._callEditWebservice('section_content_collapsed', course.id, collapsedIds);
let actionName = 'section_content_collapsed';
if (!collapsed) {
actionName = 'section_content_expanded';
}
await this._callEditWebservice(actionName, course.id, affectedSections);
}
/**
@ -618,32 +626,22 @@ export default class {
*/
_updateStateSectionPreference(stateManager, preferenceName, sectionIds, preferenceValue) {
stateManager.setReadOnly(false);
const affectedSections = new Set();
const affectedSections = [];
// Check if we need to update preferences.
sectionIds.forEach(sectionId => {
const section = stateManager.get('section', sectionId);
if (section === undefined) {
stateManager.setReadOnly(true);
return null;
}
const newValue = preferenceValue ?? section[preferenceName];
if (section[preferenceName] != newValue) {
section[preferenceName] = newValue;
affectedSections.add(section.id);
affectedSections.push(section.id);
}
});
stateManager.setReadOnly(true);
if (affectedSections.size == 0) {
return null;
}
// Get all collapsed section ids.
const collapsedSectionIds = [];
const state = stateManager.state;
state.section.forEach(section => {
if (section[preferenceName]) {
collapsedSectionIds.push(section.id);
}
});
return collapsedSectionIds;
return affectedSections;
}
/**

View File

@ -131,9 +131,9 @@ export default class Component extends BaseComponent {
const toggler = section.querySelector(this.selectors.COLLAPSE);
const isCollapsed = toggler?.classList.contains(this.classes.COLLAPSED) ?? false;
if (isChevron || isCollapsed) {
// Update the state.
const sectionId = section.getAttribute('data-id');
// Update the state.
const sectionId = section.getAttribute('data-id');
if (!sectionlink || isCollapsed) {
this.reactive.dispatch(
'sectionIndexCollapsed',
[sectionId],

View File

@ -726,8 +726,9 @@ abstract class base {
global $USER;
$course = $this->get_course();
try {
$sectionpreferences = (array) json_decode(
get_user_preferences("coursesectionspreferences_{$course->id}", '', $USER->id)
$sectionpreferences = json_decode(
get_user_preferences("coursesectionspreferences_{$course->id}", '', $USER->id),
true,
);
if (empty($sectionpreferences)) {
$sectionpreferences = [];
@ -746,10 +747,65 @@ abstract class base {
*
*/
public function set_sections_preference(string $preferencename, array $sectionids) {
global $USER;
$course = $this->get_course();
$sectionpreferences = $this->get_sections_preferences_by_preference();
$sectionpreferences[$preferencename] = $sectionids;
$this->persist_to_user_preference($sectionpreferences);
}
/**
* Add section preference ids.
*
* @param string $preferencename preference name
* @param array $sectionids affected section ids
*/
public function add_section_preference_ids(
string $preferencename,
array $sectionids,
): void {
$sectionpreferences = $this->get_sections_preferences_by_preference();
if (!isset($sectionpreferences[$preferencename])) {
$sectionpreferences[$preferencename] = [];
}
foreach ($sectionids as $sectionid) {
if (!in_array($sectionid, $sectionpreferences[$preferencename])) {
$sectionpreferences[$preferencename][] = $sectionid;
}
}
$this->persist_to_user_preference($sectionpreferences);
}
/**
* Remove section preference ids.
*
* @param string $preferencename preference name
* @param array $sectionids affected section ids
*/
public function remove_section_preference_ids(
string $preferencename,
array $sectionids,
): void {
$sectionpreferences = $this->get_sections_preferences_by_preference();
if (!isset($sectionpreferences[$preferencename])) {
$sectionpreferences[$preferencename] = [];
}
foreach ($sectionids as $sectionid) {
if (($key = array_search($sectionid, $sectionpreferences[$preferencename])) !== false) {
unset($sectionpreferences[$preferencename][$key]);
}
}
$this->persist_to_user_preference($sectionpreferences);
}
/**
* Persist the section preferences to the user preferences.
*
* @param array $sectionpreferences the section preferences
*/
private function persist_to_user_preference(
array $sectionpreferences,
): void {
global $USER;
$course = $this->get_course();
set_user_preference('coursesectionspreferences_' . $course->id, json_encode($sectionpreferences), $USER->id);
// Invalidate section preferences cache.
$coursesectionscache = cache::make('core', 'coursesectionspreferences');

View File

@ -832,7 +832,7 @@ class stateactions {
}
/**
* Update the course content section collapsed value.
* Update the course content section state to collapse.
*
* @param stateupdates $updates the affected course elements track
* @param stdClass $course the course object
@ -845,17 +845,40 @@ class stateactions {
stdClass $course,
array $ids = [],
?int $targetsectionid = null,
?int $targetcmid = null
?int $targetcmid = null,
): void {
if (!empty($ids)) {
$this->validate_sections($course, $ids, __FUNCTION__);
}
$format = course_get_format($course->id);
$format->set_sections_preference('contentcollapsed', $ids);
$format->add_section_preference_ids('contentcollapsed', $ids);
}
/**
* Update the course index section collapsed value.
* Update the course content section state to expand.
*
* @param stateupdates $updates the affected course elements track
* @param stdClass $course the course object
* @param int[] $ids the collapsed section ids
* @param int|null $targetsectionid not used
* @param int|null $targetcmid not used
*/
public function section_content_expanded(
stateupdates $updates,
stdClass $course,
array $ids = [],
?int $targetsectionid = null,
?int $targetcmid = null,
): void {
if (!empty($ids)) {
$this->validate_sections($course, $ids, __FUNCTION__);
}
$format = course_get_format($course->id);
$format->remove_section_preference_ids('contentcollapsed', $ids);
}
/**
* Update the course index section state to collapse.
*
* @param stateupdates $updates the affected course elements track
* @param stdClass $course the course object
@ -868,13 +891,36 @@ class stateactions {
stdClass $course,
array $ids = [],
?int $targetsectionid = null,
?int $targetcmid = null
?int $targetcmid = null,
): void {
if (!empty($ids)) {
$this->validate_sections($course, $ids, __FUNCTION__);
}
$format = course_get_format($course->id);
$format->set_sections_preference('indexcollapsed', $ids);
$format->add_section_preference_ids('indexcollapsed', $ids);
}
/**
* Update the course index section state to expand.
*
* @param stateupdates $updates the affected course elements track
* @param stdClass $course the course object
* @param int[] $ids the collapsed section ids
* @param int|null $targetsectionid not used
* @param int|null $targetcmid not used
*/
public function section_index_expanded(
stateupdates $updates,
stdClass $course,
array $ids = [],
?int $targetsectionid = null,
?int $targetcmid = null,
): void {
if (!empty($ids)) {
$this->validate_sections($course, $ids, __FUNCTION__);
}
$format = course_get_format($course->id);
$format->remove_section_preference_ids('indexcollapsed', $ids);
}
/**

View File

@ -366,6 +366,73 @@ class base_test extends advanced_testcase {
);
}
/**
* Test add_section_preference_ids() method.
*
* @covers \core_courseformat\base::persist_to_user_preference
*/
public function test_add_section_preference_ids(): void {
$this->resetAfterTest();
// Create initial data.
$generator = $this->getDataGenerator();
$course = $generator->create_course();
$user = $generator->create_and_enrol($course);
// Get the course format.
$format = course_get_format($course);
// Login as the user.
$this->setUser($user);
// Add section preference ids.
$format->add_section_preference_ids('pref1', [1, 2]);
$format->add_section_preference_ids('pref1', [3]);
$format->add_section_preference_ids('pref2', [1]);
// Get section preferences.
$sectionpreferences = $format->get_sections_preferences_by_preference();
$this->assertCount(3, $sectionpreferences['pref1']);
$this->assertContains(1, $sectionpreferences['pref1']);
$this->assertContains(2, $sectionpreferences['pref1']);
$this->assertContains(3, $sectionpreferences['pref1']);
$this->assertCount(1, $sectionpreferences['pref2']);
$this->assertContains(1, $sectionpreferences['pref1']);
}
/**
* Test remove_section_preference_ids() method.
*
* @covers \core_courseformat\base::persist_to_user_preference
*/
public function test_remove_section_preference_ids(): void {
$this->resetAfterTest();
// Create initial data.
$generator = $this->getDataGenerator();
$course = $generator->create_course();
$user = $generator->create_and_enrol($course);
// Get the course format.
$format = course_get_format($course);
// Login as the user.
$this->setUser($user);
// Set initial preferences.
$format->set_sections_preference('pref1', [1, 2, 3]);
$format->set_sections_preference('pref2', [1]);
// Remove section with id = 3 out of the pref1.
$format->remove_section_preference_ids('pref1', [3]);
// Get section preferences.
$sectionpreferences = $format->get_sections_preferences_by_preference();
$this->assertCount(2, $sectionpreferences['pref1']);
$this->assertCount(1, $sectionpreferences['pref2']);
// Remove section with id = 2 out of the pref1.
$format->remove_section_preference_ids('pref1', [2]);
// Remove section with id = 1 out of the pref2.
$format->remove_section_preference_ids('pref2', [1]);
// Get section preferences.
$sectionpreferences = $format->get_sections_preferences_by_preference();
$this->assertCount(1, $sectionpreferences['pref1']);
$this->assertEmpty($sectionpreferences['pref2']);
}
/**
* Test that retrieving last section number for a course
*

View File

@ -59,6 +59,8 @@ plugins can now include the string 'plugin_description' to provide a description
* A new item, initsections, has been added to the testing_data_generator::create_course() function, to let the generator rename
the sections to "Section X".
* The core_courseformat\base::get_format_string last parameter has been removed because it was erroneous.
* New core_courseformat\base::add_section_preference_ids() to add the section ids to course format preferences.
* New core_courseformat\base::remove_section_preference_ids() to remove the section ids out of course format preferences.
=== 4.3 ===
* New core_courseformat\output\activitybadge class that can be extended by any module to display content near the activity name.