MDL-63457 block_myoverview: Hide courses from individual overview blocks

* add new preferences for hidden courses
* Modified course filter to use the user preference
* Behat test
* Regex update
* Toggle hide/show
* Update enrolled_course getter to pass hidden value
* provider
* unit tests
This commit is contained in:
Peter
2018-10-29 08:32:05 +08:00
parent 448bd578d8
commit e6f03948d7
16 changed files with 523 additions and 26 deletions
+1 -1
View File
@@ -1 +1 @@
define(["jquery","block_myoverview/repository","core/paged_content_factory","core/custom_interaction_events","core/notification","core/templates"],function(a,b,c,d,e,f){var g={ACTION_ADD_FAVOURITE:'[data-action="add-favourite"]',ACTION_REMOVE_FAVOURITE:'[data-action="remove-favourite"]',FAVOURITE_ICON:'[data-region="favourite-icon"]',ICON_IS_FAVOURITE:'[data-region="is-favourite"]',ICON_NOT_FAVOURITE:'[data-region="not-favourite"]',PAGED_CONTENT_CONTAINER:'[data-region="page-container"]'},h={COURSES_CARDS:"block_myoverview/view-cards",COURSES_LIST:"block_myoverview/view-list",COURSES_SUMMARY:"block_myoverview/view-summary",NOCOURSES:"block_myoverview/no-courses"},i=[12,24,48],j=[],k=function(a){var b={};return b.display=a.attr("data-display"),b.grouping=a.attr("data-grouping"),b.sort=a.attr("data-sort"),b},l={ignoreControlWhileLoading:!0,controlPlacementBottom:!0},m=function(a,c,d){return b.getEnrolledCoursesByTimeline({offset:d*c,limit:c,classification:a.grouping,sort:a.sort})},n=function(a,b){return a.find(g.FAVOURITE_ICON+'[data-course-id="'+b+'"]')},o=function(a,b){return a.find('[data-region="paged-content-page"][data-page="'+b+'"]')},p=function(a){return a.attr("data-course-id")},q=function(a,b){var c=n(a,b),d=c.find(g.ICON_IS_FAVOURITE);d.addClass("hidden"),d.attr("aria-hidden",!0);var e=c.find(g.ICON_NOT_FAVOURITE);e.removeClass("hidden"),e.attr("aria-hidden",!1)},r=function(a,b){var c=n(a,b),d=c.find(g.ICON_IS_FAVOURITE);d.removeClass("hidden"),d.attr("aria-hidden",!1);var e=c.find(g.ICON_NOT_FAVOURITE);e.addClass("hidden"),e.attr("aria-hidden",!0)},s=function(a,b){return a.find('[data-action="add-favourite"][data-course-id="'+b+'"]')},t=function(a,b){return a.find('[data-action="remove-favourite"][data-course-id="'+b+'"]')},u=function(a,b){var c=t(a,b),d=s(a,b);w(b,!0).then(function(f){f?(c.removeClass("hidden"),d.addClass("hidden"),r(a,b)):e.alert("Starring course failed","Could not change favourite state")})["catch"](e.exception)},v=function(a,b){var c=t(a,b),d=s(a,b);w(b,!1).then(function(f){f?(c.addClass("hidden"),d.removeClass("hidden"),q(a,b)):e.alert("Starring course failed","Could not change favourite state")})["catch"](e.exception)},w=function(a,c){return b.setFavouriteCourses({courses:[{id:a,favourite:c}]}).then(function(b){return 0==b.warnings.length&&(j.forEach(function(b){b.courses.forEach(function(d,e){d.id==a&&(b.courses[e].isfavourite=c)})}),!0)})["catch"](e.exception)},x=function(a,b){var c=k(a),d="";if(d="cards"==c.display?h.COURSES_CARDS:"list"==c.display?h.COURSES_LIST:h.COURSES_SUMMARY,b.courses.length)return f.render(d,{courses:b.courses});var e=a.attr("data-nocoursesimg");return f.render(h.NOCOURSES,{nocoursesimg:e})},y=function(b,d){b=a(b),b.attr("data-init")||(z(b),b.attr("data-init",!0));var g=k(b),h=c.createWithLimit(i,function(a,c){var d=[];return a.forEach(function(a){var f=a.pageNumber,h=a.pageNumber-1,i=m(g,a.limit,h).then(function(d){return d.courses.length<a.limit&&c.allItemsLoaded(a.pageNumber),j[f]=d,x(b,d)})["catch"](e.exception);d.push(i)}),d},l);h.then(function(a,b){return f.replaceNodeContents(d,a,b)})["catch"](e.exception)},z=function(b){d.define(b,[d.events.activate]),b.on(d.events.activate,g.ACTION_ADD_FAVOURITE,function(c,d){var e=a(c.target).closest(g.ACTION_ADD_FAVOURITE),f=p(e);u(b,f),d.originalEvent.preventDefault()}),b.on(d.events.activate,g.ACTION_REMOVE_FAVOURITE,function(c,d){var e=a(c.target).closest(g.ACTION_REMOVE_FAVOURITE),f=p(e);v(b,f),d.originalEvent.preventDefault()}),b.on(d.events.activate,g.FAVOURITE_ICON,function(a,b){b.originalEvent.preventDefault()})},A=function(a,b){j.length>0?j.forEach(function(b,c){var d=o(a,c);x(a,b).then(function(a,b){return f.replaceNodeContents(d,a,b)})["catch"](e.exception)}):y(a,b)};return{init:y,reset:A}});
define(["jquery","block_myoverview/repository","core/paged_content_factory","core/custom_interaction_events","core/notification","core/ajax","core/templates"],function(a,b,c,d,e,f,g){var h={COURSE_REGION:'[data-region="course-view-content"]',ACTION_HIDE_COURSE:'[data-action="hide-course"]',ACTION_SHOW_COURSE:'[data-action="show-course"]',ACTION_ADD_FAVOURITE:'[data-action="add-favourite"]',ACTION_REMOVE_FAVOURITE:'[data-action="remove-favourite"]',FAVOURITE_ICON:'[data-region="favourite-icon"]',ICON_IS_FAVOURITE:'[data-region="is-favourite"]',ICON_NOT_FAVOURITE:'[data-region="not-favourite"]',PAGED_CONTENT_CONTAINER:'[data-region="page-container"]'},i={COURSES_CARDS:"block_myoverview/view-cards",COURSES_LIST:"block_myoverview/view-list",COURSES_SUMMARY:"block_myoverview/view-summary",NOCOURSES:"block_myoverview/no-courses"},j=[12,24,48],k=[],l=function(a){var b={};return b.display=a.attr("data-display"),b.grouping=a.attr("data-grouping"),b.sort=a.attr("data-sort"),b},m={ignoreControlWhileLoading:!0,controlPlacementBottom:!0},n=function(a,c,d){return b.getEnrolledCoursesByTimeline({offset:d*c,limit:c,classification:a.grouping,sort:a.sort})},o=function(a,b){return a.find(h.FAVOURITE_ICON+'[data-course-id="'+b+'"]')},p=function(a,b){return a.find('[data-region="paged-content-page"][data-page="'+b+'"]')},q=function(a){return a.attr("data-course-id")},r=function(a,b){var c=o(a,b),d=c.find(h.ICON_IS_FAVOURITE);d.addClass("hidden"),d.attr("aria-hidden",!0);var e=c.find(h.ICON_NOT_FAVOURITE);e.removeClass("hidden"),e.attr("aria-hidden",!1)},s=function(a,b){var c=o(a,b),d=c.find(h.ICON_IS_FAVOURITE);d.removeClass("hidden"),d.attr("aria-hidden",!1);var e=c.find(h.ICON_NOT_FAVOURITE);e.addClass("hidden"),e.attr("aria-hidden",!0)},t=function(a,b){return a.find('[data-action="add-favourite"][data-course-id="'+b+'"]')},u=function(a,b){return a.find('[data-action="remove-favourite"][data-course-id="'+b+'"]')},v=function(a,b){var c=u(a,b),d=t(a,b);x(b,!0).then(function(f){f?(c.removeClass("hidden"),d.addClass("hidden"),s(a,b)):e.alert("Starring course failed","Could not change favourite state")})["catch"](e.exception)},w=function(a,b){var c=u(a,b),d=t(a,b);x(b,!1).then(function(f){f?(c.addClass("hidden"),d.removeClass("hidden"),r(a,b)):e.alert("Starring course failed","Could not change favourite state")})["catch"](e.exception)},x=function(a,c){return b.setFavouriteCourses({courses:[{id:a,favourite:c}]}).then(function(b){return 0==b.warnings.length&&(k.forEach(function(b){b.courses.forEach(function(d,e){d.id==a&&(b.courses[e].isfavourite=c)})}),!0)})["catch"](e.exception)},y=function(a,b){var c=l(a),d="";if(d="cards"==c.display?i.COURSES_CARDS:"list"==c.display?i.COURSES_LIST:i.COURSES_SUMMARY,b.courses.length)return g.render(d,{courses:b.courses});var e=a.attr("data-nocoursesimg");return g.render(i.NOCOURSES,{nocoursesimg:e})},z=function(a,b){var d=l(a),f=c.createWithLimit(j,function(b,c){var f=[];return b.forEach(function(b){var g=b.pageNumber,h=b.pageNumber-1,i=n(d,b.limit,h).then(function(d){return d.courses.length<b.limit&&c.allItemsLoaded(b.pageNumber),k[g]=d,y(a,d)})["catch"](e.exception);f.push(i)}),f},m);f.then(function(a,c){return g.replaceNodeContents(b,a,c)})["catch"](e.exception)},A=function(c,e){d.define(c,[d.events.activate]),c.on(d.events.activate,h.ACTION_ADD_FAVOURITE,function(b,d){var e=a(b.target).closest(h.ACTION_ADD_FAVOURITE),f=q(e);v(c,f),d.originalEvent.preventDefault()}),c.on(d.events.activate,h.ACTION_REMOVE_FAVOURITE,function(b,d){var e=a(b.target).closest(h.ACTION_REMOVE_FAVOURITE),f=q(e);w(c,f),d.originalEvent.preventDefault()}),c.on(d.events.activate,h.FAVOURITE_ICON,function(a,b){b.originalEvent.preventDefault()}),c.on(d.events.activate,h.ACTION_HIDE_COURSE,function(d,f){var g=a(d.target).closest(h.ACTION_HIDE_COURSE),i=q(g),j={preferences:[{type:"block_myoverview_hidden_course_"+i,value:!0}]};b.updateUserPreferences(j),z(c,e),f.originalEvent.preventDefault()}),c.on(d.events.activate,h.ACTION_SHOW_COURSE,function(d,f){var g=a(d.target).closest(h.ACTION_SHOW_COURSE),i=q(g),j={preferences:[{type:"block_myoverview_hidden_course_"+i,value:null}]};b.updateUserPreferences(j),z(c,e),f.originalEvent.preventDefault()})},B=function(b,c){b=a(b),b.attr("data-init")||(A(b,c),b.attr("data-init",!0)),z(b,c)},C=function(a,b){k.length>0?k.forEach(function(b,c){var d=p(a,c);y(a,b).then(function(a,b){return g.replaceNodeContents(d,a,b)})["catch"](e.exception)}):B(a,b)};return{init:B,reset:C}});
+70 -15
View File
@@ -28,6 +28,7 @@ define(
'core/paged_content_factory',
'core/custom_interaction_events',
'core/notification',
'core/ajax',
'core/templates',
],
function(
@@ -36,10 +37,14 @@ function(
PagedContentFactory,
CustomEvents,
Notification,
Ajax,
Templates
) {
var SELECTORS = {
COURSE_REGION: '[data-region="course-view-content"]',
ACTION_HIDE_COURSE: '[data-action="hide-course"]',
ACTION_SHOW_COURSE: '[data-action="show-course"]',
ACTION_ADD_FAVOURITE: '[data-action="add-favourite"]',
ACTION_REMOVE_FAVOURITE: '[data-action="remove-favourite"]',
FAVOURITE_ICON: '[data-region="favourite-icon"]',
@@ -127,7 +132,7 @@ function(
* @param {Object} root The favourite icon container element.
* @return {Number} Course id.
*/
var getFavouriteCourseId = function(root) {
var getCourseId = function(root) {
return root.attr('data-course-id');
};
@@ -294,20 +299,12 @@ function(
};
/**
* Intialise the courses list and cards views on page load.
* Intialise the paged list and cards views on page load.
*
* @param {object} root The root element for the courses view.
* @param {object} content The content element for the courses view.
*/
var init = function(root, content) {
root = $(root);
if (!root.attr('data-init')) {
registerEventListeners(root);
root.attr('data-init', true);
}
var initializePagedContent = function(root, content) {
var filters = getFilterValues(root);
var pagedContentPromise = PagedContentFactory.createWithLimit(
@@ -330,7 +327,7 @@ function(
loadedPages[currentPage] = coursesData;
return renderCourses(root, coursesData);
})
.catch(Notification.exception);
.catch(Notification.exception);
promises.push(pagePromise);
});
@@ -350,21 +347,21 @@ function(
*
* @param {Object} root The myoverview block container element.
*/
var registerEventListeners = function(root) {
var registerEventListeners = function(root, content) {
CustomEvents.define(root, [
CustomEvents.events.activate
]);
root.on(CustomEvents.events.activate, SELECTORS.ACTION_ADD_FAVOURITE, function(e, data) {
var favourite = $(e.target).closest(SELECTORS.ACTION_ADD_FAVOURITE);
var courseId = getFavouriteCourseId(favourite);
var courseId = getCourseId(favourite);
addToFavourites(root, courseId);
data.originalEvent.preventDefault();
});
root.on(CustomEvents.events.activate, SELECTORS.ACTION_REMOVE_FAVOURITE, function(e, data) {
var favourite = $(e.target).closest(SELECTORS.ACTION_REMOVE_FAVOURITE);
var courseId = getFavouriteCourseId(favourite);
var courseId = getCourseId(favourite);
removeFromFavourites(root, courseId);
data.originalEvent.preventDefault();
});
@@ -372,9 +369,67 @@ function(
root.on(CustomEvents.events.activate, SELECTORS.FAVOURITE_ICON, function(e, data) {
data.originalEvent.preventDefault();
});
root.on(CustomEvents.events.activate, SELECTORS.ACTION_HIDE_COURSE, function(e, data) {
var target = $(e.target).closest(SELECTORS.ACTION_HIDE_COURSE);
var id = getCourseId(target);
var request = {
preferences: [
{
type: 'block_myoverview_hidden_course_' + id,
value: true
}
]
};
Repository.updateUserPreferences(request);
// Reload the paged content based on the hidden course
initializePagedContent(root, content);
data.originalEvent.preventDefault();
});
root.on(CustomEvents.events.activate, SELECTORS.ACTION_SHOW_COURSE, function(e, data) {
var target = $(e.target).closest(SELECTORS.ACTION_SHOW_COURSE);
var id = getCourseId(target);
var request = {
preferences: [
{
type: 'block_myoverview_hidden_course_' + id,
value: null
}
]
};
Repository.updateUserPreferences(request);
// Reload the paged content based on the hidden course
initializePagedContent(root, content);
data.originalEvent.preventDefault();
});
};
/**
* Intialise the courses list and cards views on page load.
*
* @param {object} root The root element for the courses view.
* @param {object} content The content element for the courses view.
*/
var init = function(root, content) {
root = $(root);
if (!root.attr('data-init')) {
registerEventListeners(root, content);
root.attr('data-init', true);
}
initializePagedContent(root, content);
};
/**
* Reset the courses views to their original
* state on first page load.
*
@@ -78,5 +78,20 @@ class provider implements \core_privacy\local\metadata\provider, user_preference
get_string($preference, 'block_myoverview'),
get_string('privacy:metadata:overviewgroupingpreference', 'block_myoverview'));
}
$preferences = get_user_preferences(null, null, $userid);
foreach ($preferences as $name => $value) {
if ((substr($name, 0, 30) == 'block_myoverview_hidden_course')) {
writer::export_user_preference(
'block_myoverview',
$name,
$value,
get_string('privacy:request:preference:set', 'block_myoverview', (object) [
'name' => $name,
'value' => $value,
])
);
}
}
}
}
+10 -1
View File
@@ -48,6 +48,7 @@ $string['aria:summary'] = 'Switch to summary view';
$string['aria:sortingdropdown'] = 'Sorting dropdown';
$string['card'] = 'Card';
$string['cards'] = 'Cards';
$string['courseprogress'] = 'Course progress:';
$string['complete'] = 'Complete';
$string['favourite'] = 'Starred course';
$string['favourites'] = 'Starred';
@@ -67,6 +68,14 @@ $string['privacy:metadata:overviewgroupingpreference'] = 'The myoverview block g
$string['removefromfavourites'] = 'Unstar this course';
$string['summary'] = 'Summary';
$string['title'] = 'Title';
$string['aria:hidecourse'] = 'Hide {$a} from view';
$string['aria:showcourse'] = 'Show {$a} in view';
$string['aria:hiddencourses'] = 'Show hidden courses';
$string['hidden'] = 'Hidden courses';
$string['hidecourse'] = 'Hide from view';
$string['hiddencourses'] = 'Hidden';
$string['show'] = 'Show this course';
$string['privacy:request:preference:set'] = 'The value of the setting \'{$a->name}\' was \'{$a->value}\'';
// Deprecated since Moodle 3.6.
$string['defaulttab'] = 'Default tab';
@@ -84,4 +93,4 @@ $string['sortbydates'] = 'Sort by dates';
$string['timeline'] = 'Timeline';
$string['viewcoursename'] = 'View course {$a}';
$string['privacy:metadata:overviewlasttab'] = 'This stores the last tab selected by the user on the overview block.';
$string['viewcourse'] = 'View course';
+13 -2
View File
@@ -32,6 +32,7 @@ define('BLOCK_MYOVERVIEW_GROUPING_INPROGRESS', 'inprogress');
define('BLOCK_MYOVERVIEW_GROUPING_FUTURE', 'future');
define('BLOCK_MYOVERVIEW_GROUPING_PAST', 'past');
define('BLOCK_MYOVERVIEW_GROUPING_FAVOURITES', 'favourites');
define('BLOCK_MYOVERVIEW_GROUPING_HIDDEN', 'hidden');
/**
* Constants for the user preferences sorting options
@@ -62,7 +63,8 @@ function block_myoverview_user_preferences() {
BLOCK_MYOVERVIEW_GROUPING_INPROGRESS,
BLOCK_MYOVERVIEW_GROUPING_FUTURE,
BLOCK_MYOVERVIEW_GROUPING_PAST,
BLOCK_MYOVERVIEW_GROUPING_FAVOURITES
BLOCK_MYOVERVIEW_GROUPING_FAVOURITES,
BLOCK_MYOVERVIEW_GROUPING_HIDDEN
)
);
$preferences['block_myoverview_user_sort_preference'] = array(
@@ -84,5 +86,14 @@ function block_myoverview_user_preferences() {
BLOCK_MYOVERVIEW_VIEW_SUMMARY
)
);
$preferences['/^block_myoverview_hidden_course_(\d)+$/'] = array(
'isregex' => true,
'choices' => array(0, 1),
'type' => PARAM_INT,
'null' => NULL_NOT_ALLOWED,
'default' => 'none'
);
return $preferences;
}
}
@@ -57,5 +57,27 @@
{{#str}} aria:removefromfavourites, block_myoverview {{/str}} {{{fullname}}}
</div>
</a>
<a class="dropdown-item {{^hidden}}hidden{{/hidden}}" href="#"
data-action="show-course"
data-course-id="{{id}}"
aria-controls="favorite-icon-{{ id }}"
>
{{#pix}} i/show, core, {{#str}} hidden, block_myoverview {{/str}} {{/pix}}
{{#str}} show, block_myoverview {{/str}}
<div class="sr-only">
{{#str}} aria:showcourse, block_myoverview, {{fullname}} {{/str}}
</div>
</a>
<a class="dropdown-item {{#hidden}}hidden{{/hidden}}" href="#"
data-action="hide-course"
data-course-id="{{id}}"
aria-controls="favorite-icon-{{ id }}"
>
{{#pix}} i/hide, core, {{#str}} hidden, block_myoverview {{/str}} {{/pix}}
{{#str}} hidecourse, block_myoverview {{/str}}
<div class="sr-only">
{{#str}} aria:hidecourse, block_myoverview, {{fullname}} {{/str}}
</div>
</a>
</div>
</div>
@@ -32,6 +32,7 @@
data-display="{{view}}"
data-grouping="{{grouping}}"
data-sort="{{sort}}"
data-prev-display="{{view}}"
data-nocoursesimg="{{nocoursesimg}}">
<div data-region="course-view-content">
<div data-region="courses-loading-placeholder">
@@ -36,6 +36,7 @@
{{#future}}{{#str}} future, block_myoverview {{/str}}{{/future}}
{{#past}}{{#str}} past, block_myoverview {{/str}}{{/past}}
{{#favourites}}{{#str}} favourites, block_myoverview {{/str}}{{/favourites}}
{{#hidden}}{{#str}} hiddencourses, block_myoverview {{/str}}{{/hidden}}
</span>
</button>
<ul class="dropdown-menu" data-show-active-item data-active-item-text aria-labelledby="groupingdropdown">
@@ -64,5 +65,10 @@
{{#str}} favourites, block_myoverview {{/str}}
</a>
</li>
<li>
<a class="dropdown-item {{#hidden}}active{{/hidden}}" href="#" data-filter="grouping" data-value="hidden" data-pref="hidden" aria-label="{{#str}} aria:hiddencourses, block_myoverview {{/str}}" aria-controls="courses-view-{{uniqid}}">
{{#str}} hiddencourses, block_myoverview {{/str}}
</a>
</li>
</ul>
</div>
@@ -157,4 +157,46 @@ Feature: The my overview block allows users to easily access their courses
And I click on "Last accessed" "link" in the "Course overview" "block"
And I reload the page
Then I should see "Last accessed" in the "Course overview" "block"
And "[data-sort='ul.timeaccess desc']" "css_element" in the "Course overview" "block" should be visible
And "[data-sort='ul.timeaccess desc']" "css_element" in the "Course overview" "block" should be visible
Scenario: View inprogress courses with hide persistent functionality
Given I log in as "student1"
And I click on "All" "button" in the "Course overview" "block"
When I click on "In progress" "link" in the "Course overview" "block"
And I click on ".coursemenubtn" "css_element" in the "//div[@class='card course-card' and contains(.,'Course 2')]" "xpath_element"
And I click on "Hide from view" "link" in the "//div[@class='card course-card' and contains(.,'Course 2')]" "xpath_element"
And I reload the page
Then I should see "Course 3" in the "Course overview" "block"
Then I should see "Course 4" in the "Course overview" "block"
And I should not see "Course 2" in the "Course overview" "block"
And I should not see "Course 1" in the "Course overview" "block"
And I should not see "Course 5" in the "Course overview" "block"
And I log out
Scenario: View past courses with hide persistent functionality
Given I log in as "student1"
And I click on "All" "button" in the "Course overview" "block"
When I click on "Past" "link" in the "Course overview" "block"
And I click on ".coursemenubtn" "css_element" in the "//div[@class='card course-card' and contains(.,'Course 1')]" "xpath_element"
And I click on "Hide from view" "link" in the "//div[@class='card course-card' and contains(.,'Course 1')]" "xpath_element"
And I reload the page
Then I should not see "Course 1" in the "Course overview" "block"
And I should not see "Course 2" in the "Course overview" "block"
And I should not see "Course 3" in the "Course overview" "block"
And I should not see "Course 4" in the "Course overview" "block"
And I should not see "Course 5" in the "Course overview" "block"
And I log out
Scenario: View future courses with hide persistent functionality
Given I log in as "student1"
And I click on "All" "button" in the "Course overview" "block"
When I click on "Future" "link" in the "Course overview" "block"
And I click on ".coursemenubtn" "css_element" in the "//div[@class='card course-card' and contains(.,'Course 5')]" "xpath_element"
And I click on "Hide from view" "link" in the "//div[@class='card course-card' and contains(.,'Course 5')]" "xpath_element"
And I reload the page
Then I should not see "Course 5" in the "Course overview" "block"
And I should not see "Course 1" in the "Course overview" "block"
And I should not see "Course 2" in the "Course overview" "block"
And I should not see "Course 3" in the "Course overview" "block"
And I should not see "Course 4" in the "Course overview" "block"
And I log out
@@ -0,0 +1,83 @@
@block @block_myoverview @javascript
Feature: The my overview block allows users to favourite their courses
In order to enable the my overview block in a course
As a student
I can add the my overview block to my dashboard
Background:
Given the following "users" exist:
| username | firstname | lastname | email | idnumber |
| student1 | Student | X | student1@example.com | S1 |
And the following "courses" exist:
| fullname | shortname | category |
| Course 1 | C1 | 0 |
| Course 2 | C2 | 0 |
| Course 3 | C3 | 0 |
| Course 4 | C4 | 0 |
| Course 5 | C5 | 0 |
And the following "course enrolments" exist:
| user | course | role |
| student1 | C1 | student |
| student1 | C2 | student |
| student1 | C3 | student |
| student1 | C4 | student |
| student1 | C5 | student |
Scenario: Test hide toggle functionality
Given I log in as "student1"
When I click on ".coursemenubtn" "css_element" in the "//div[@class='card course-card' and contains(.,'Course 2')]" "xpath_element"
And I click on "Hide from view" "link" in the "//div[@class='card course-card' and contains(.,'Course 2')]" "xpath_element"
And I reload the page
Then I should not see "Course 2" in the "Course overview" "block"
And I log out
Scenario: Test hide toggle functionality w/ favorites
Given I log in as "student1"
And I click on ".coursemenubtn" "css_element" in the "//div[@class='card course-card' and contains(.,'Course 2')]" "xpath_element"
And I click on "Star this course" "link" in the "//div[@class='card course-card' and contains(.,'Course 2')]" "xpath_element"
And I click on ".coursemenubtn" "css_element" in the "//div[@class='card course-card' and contains(.,'Course 2')]" "xpath_element"
And I click on "Hide from view" "link" in the "//div[@class='card course-card' and contains(.,'Course 2')]" "xpath_element"
When I reload the page
Then I should not see "Course 2" in the "Course overview" "block"
And I click on "All" "button" in the "Course overview" "block"
And I click on "Starred" "link" in the "Course overview" "block"
Then I should not see "Course 2" in the "Course overview" "block"
And I click on "Starred" "button" in the "Course overview" "block"
And I click on "Hidden" "link" in the "Course overview" "block"
Then I should see "Course 2" in the "Course overview" "block"
And I log out
Scenario: Test show toggle functionality
Given I log in as "student1"
And I click on ".coursemenubtn" "css_element" in the "//div[@class='card course-card' and contains(.,'Course 2')]" "xpath_element"
And I click on "Hide from view" "link" in the "//div[@class='card course-card' and contains(.,'Course 2')]" "xpath_element"
When I click on "All" "button" in the "Course overview" "block"
And I click on "Hidden" "link" in the "Course overview" "block"
When I click on ".coursemenubtn" "css_element" in the "//div[@class='card course-card' and contains(.,'Course 2')]" "xpath_element"
And I click on "Show this course" "link" in the "//div[@class='card course-card' and contains(.,'Course 2')]" "xpath_element"
And I reload the page
And I click on "Hidden" "button" in the "Course overview" "block"
When I click on "All" "link" in the "Course overview" "block"
Then I should see "Course 2" in the "Course overview" "block"
And I log out
Scenario: Test show toggle functionality w/ favorites
Given I log in as "student1"
And I click on ".coursemenubtn" "css_element" in the "//div[@class='card course-card' and contains(.,'Course 2')]" "xpath_element"
And I click on "Star this course" "link" in the "//div[@class='card course-card' and contains(.,'Course 2')]" "xpath_element"
And I click on ".coursemenubtn" "css_element" in the "//div[@class='card course-card' and contains(.,'Course 2')]" "xpath_element"
And I click on "Hide from view" "link" in the "//div[@class='card course-card' and contains(.,'Course 2')]" "xpath_element"
And I click on "All" "button" in the "Course overview" "block"
And I click on "Hidden" "link" in the "Course overview" "block"
And I should see "Course 2" in the "Course overview" "block"
And I click on ".coursemenubtn" "css_element" in the "//div[@class='card course-card' and contains(.,'Course 2')]" "xpath_element"
And I click on "Show this course" "link" in the "//div[@class='card course-card' and contains(.,'Course 2')]" "xpath_element"
When I reload the page
Then I should not see "Course 2" in the "Course overview" "block"
And I click on "Hidden" "button" in the "Course overview" "block"
And I click on "All" "link" in the "Course overview" "block"
Then I should see "Course 2" in the "Course overview" "block"
And I click on "All" "button" in the "Course overview" "block"
And I click on "Starred" "link" in the "Course overview" "block"
Then I should see "Course 2" in the "Course overview" "block"
And I log out
+19
View File
@@ -78,4 +78,23 @@ class block_myoverview_privacy_testcase extends \core_privacy\tests\provider_tes
array('block_myoverview_user_view_preference', 'summary')
);
}
public function test_export_user_preferences_with_hidden_courses() {
$this->resetAfterTest();
$user = $this->getDataGenerator()->create_user();
$name = "block_myoverview_hidden_course_1";
set_user_preference($name, 1, $user);
provider::export_user_preferences($user->id);
$writer = writer::with_context(\context_system::instance());
$blockpreferences = $writer->get_user_preferences('block_myoverview');
$this->assertEquals(
get_string("privacy:request:preference:set", 'block_myoverview', (object) [
'name' => $name,
'value' => 1,
]),
$blockpreferences->{$name}->description
);
}
}
+5 -1
View File
@@ -71,7 +71,8 @@ class course_summary_exporter extends \core\external\exporter {
'courseimage' => $courseimage,
'progress' => $progress,
'hasprogress' => $hasprogress,
'isfavourite' => $this->related['isfavourite']
'isfavourite' => $this->related['isfavourite'],
'hidden' => boolval(get_user_preferences('block_myoverview_hidden_course_' . $this->data->id, 0))
);
}
@@ -137,6 +138,9 @@ class course_summary_exporter extends \core\external\exporter {
),
'isfavourite' => array(
'type' => PARAM_BOOL
),
'hidden' => array(
'type' => PARAM_BOOL
)
);
}
+2 -1
View File
@@ -3665,6 +3665,8 @@ class core_course_external extends external_api {
break;
case COURSE_FAVOURITES:
break;
case COURSE_TIMELINE_HIDDEN:
break;
default:
throw new invalid_parameter_exception('Invalid classification');
}
@@ -3692,7 +3694,6 @@ class core_course_external extends external_api {
$favouritecourseids,
$limit
);
} else {
list($filteredcourses, $processedcount) = course_filter_courses_by_timeline_classification(
$courses,
+9 -3
View File
@@ -60,6 +60,7 @@ define('COURSE_TIMELINE_PAST', 'past');
define('COURSE_TIMELINE_INPROGRESS', 'inprogress');
define('COURSE_TIMELINE_FUTURE', 'future');
define('COURSE_FAVOURITES', 'favourites');
define('COURSE_TIMELINE_HIDDEN', 'hidden');
define('COURSE_DB_QUERY_LIMIT', 1000);
function make_log_url($module, $url) {
@@ -4242,7 +4243,8 @@ function course_filter_courses_by_timeline_classification(
) : array {
if (!in_array($classification,
[COURSE_TIMELINE_ALL, COURSE_TIMELINE_PAST, COURSE_TIMELINE_INPROGRESS, COURSE_TIMELINE_FUTURE])) {
[COURSE_TIMELINE_ALL, COURSE_TIMELINE_PAST, COURSE_TIMELINE_INPROGRESS,
COURSE_TIMELINE_FUTURE, COURSE_TIMELINE_HIDDEN])) {
$message = 'Classification must be one of COURSE_TIMELINE_ALL, COURSE_TIMELINE_PAST, '
. 'COURSE_TIMELINE_INPROGRESS or COURSE_TIMELINE_FUTURE';
throw new moodle_exception($message);
@@ -4254,8 +4256,11 @@ function course_filter_courses_by_timeline_classification(
foreach ($courses as $course) {
$numberofcoursesprocessed++;
$pref = get_user_preferences('block_myoverview_hidden_course_' . $course->id, 0);
if ($classification == COURSE_TIMELINE_ALL || $classification == course_classify_for_timeline($course)) {
// Added as of MDL-63457 toggle viewability for each user.
if (($classification == COURSE_TIMELINE_HIDDEN && $pref) ||
(($classification == COURSE_TIMELINE_ALL || $classification == course_classify_for_timeline($course)) && !$pref)) {
$filteredcourses[] = $course;
$filtermatches++;
}
@@ -4299,8 +4304,9 @@ function course_filter_courses_by_favourites(
foreach ($courses as $course) {
$numberofcoursesprocessed++;
$pref = get_user_preferences('block_myoverview_hidden_course_' . $course->id, 0);
if (in_array($course->id, $favouritecourseids)) {
if (in_array($course->id, $favouritecourseids) && !$pref) {
$filteredcourses[] = $course;
$filtermatches++;
}
+212
View File
@@ -4718,6 +4718,218 @@ class core_course_courselib_testcase extends advanced_testcase {
$this->assertEquals($expectedprocessedcount, $processedcount);
}
/**
* Test cases for the course_filter_courses_by_timeline_classification w/ hidden courses tests.
*/
public function get_course_filter_courses_by_timeline_classification_hidden_courses_test_cases() {
$now = time();
$day = 86400;
$coursedata = [
[
'shortname' => 'apast',
'startdate' => $now - ($day * 2),
'enddate' => $now - $day
],
[
'shortname' => 'bpast',
'startdate' => $now - ($day * 2),
'enddate' => $now - $day
],
[
'shortname' => 'cpast',
'startdate' => $now - ($day * 2),
'enddate' => $now - $day
],
[
'shortname' => 'dpast',
'startdate' => $now - ($day * 2),
'enddate' => $now - $day
],
[
'shortname' => 'epast',
'startdate' => $now - ($day * 2),
'enddate' => $now - $day
],
[
'shortname' => 'ainprogress',
'startdate' => $now - $day,
'enddate' => $now + $day
],
[
'shortname' => 'binprogress',
'startdate' => $now - $day,
'enddate' => $now + $day
],
[
'shortname' => 'cinprogress',
'startdate' => $now - $day,
'enddate' => $now + $day
],
[
'shortname' => 'dinprogress',
'startdate' => $now - $day,
'enddate' => $now + $day
],
[
'shortname' => 'einprogress',
'startdate' => $now - $day,
'enddate' => $now + $day
],
[
'shortname' => 'afuture',
'startdate' => $now + $day
],
[
'shortname' => 'bfuture',
'startdate' => $now + $day
],
[
'shortname' => 'cfuture',
'startdate' => $now + $day
],
[
'shortname' => 'dfuture',
'startdate' => $now + $day
],
[
'shortname' => 'efuture',
'startdate' => $now + $day
]
];
// Raw enrolled courses result set should be returned in this order:
// afuture, ainprogress, apast, bfuture, binprogress, bpast, cfuture, cinprogress, cpast,
// dfuture, dinprogress, dpast, efuture, einprogress, epast
//
// By classification the offset values for each record should be:
// COURSE_TIMELINE_FUTURE
// 0 (afuture), 3 (bfuture), 6 (cfuture), 9 (dfuture), 12 (efuture)
// COURSE_TIMELINE_INPROGRESS
// 1 (ainprogress), 4 (binprogress), 7 (cinprogress), 10 (dinprogress), 13 (einprogress)
// COURSE_TIMELINE_PAST
// 2 (apast), 5 (bpast), 8 (cpast), 11 (dpast), 14 (epast).
return [
'empty set' => [
'coursedata' => [],
'classification' => COURSE_TIMELINE_FUTURE,
'limit' => 2,
'offset' => 0,
'expectedcourses' => [],
'expectedprocessedcount' => 0,
'hiddencourse' => ''
],
// COURSE_TIMELINE_FUTURE.
'future not limit no offset' => [
'coursedata' => $coursedata,
'classification' => COURSE_TIMELINE_FUTURE,
'limit' => 0,
'offset' => 0,
'expectedcourses' => ['afuture', 'cfuture', 'dfuture', 'efuture'],
'expectedprocessedcount' => 15,
'hiddencourse' => 'bfuture'
],
'future no offset' => [
'coursedata' => $coursedata,
'classification' => COURSE_TIMELINE_FUTURE,
'limit' => 2,
'offset' => 0,
'expectedcourses' => ['afuture', 'cfuture'],
'expectedprocessedcount' => 7,
'hiddencourse' => 'bfuture'
],
'future offset' => [
'coursedata' => $coursedata,
'classification' => COURSE_TIMELINE_FUTURE,
'limit' => 2,
'offset' => 2,
'expectedcourses' => ['bfuture', 'dfuture'],
'expectedprocessedcount' => 8,
'hiddencourse' => 'cfuture'
],
'future exact limit' => [
'coursedata' => $coursedata,
'classification' => COURSE_TIMELINE_FUTURE,
'limit' => 5,
'offset' => 0,
'expectedcourses' => ['afuture', 'cfuture', 'dfuture', 'efuture'],
'expectedprocessedcount' => 15,
'hiddencourse' => 'bfuture'
],
'future limit less results' => [
'coursedata' => $coursedata,
'classification' => COURSE_TIMELINE_FUTURE,
'limit' => 10,
'offset' => 0,
'expectedcourses' => ['afuture', 'cfuture', 'dfuture', 'efuture'],
'expectedprocessedcount' => 15,
'hiddencourse' => 'bfuture'
],
'future limit less results with offset' => [
'coursedata' => $coursedata,
'classification' => COURSE_TIMELINE_FUTURE,
'limit' => 10,
'offset' => 5,
'expectedcourses' => ['cfuture', 'efuture'],
'expectedprocessedcount' => 10,
'hiddencourse' => 'dfuture'
],
];
}
/**
* Test the course_filter_courses_by_timeline_classification function hidden courses.
*
* @dataProvider get_course_filter_courses_by_timeline_classification_hidden_courses_test_cases()
* @param array $coursedata Course test data to create.
* @param string $classification Timeline classification.
* @param int $limit Maximum number of results to return.
* @param int $offset Results to skip at the start of the result set.
* @param string[] $expectedcourses Expected courses in results.
* @param int $expectedprocessedcount Expected number of course records to be processed.
* @param int $hiddencourse The course to hide as part of this process
*/
public function test_course_filter_courses_by_timeline_classification_with_hidden_courses(
$coursedata,
$classification,
$limit,
$offset,
$expectedcourses,
$expectedprocessedcount,
$hiddencourse
) {
$this->resetAfterTest();
$generator = $this->getDataGenerator();
$student = $generator->create_user();
$this->setUser($student);
$courses = array_map(function($coursedata) use ($generator, $hiddencourse) {
$course = $generator->create_course($coursedata);
if ($course->shortname == $hiddencourse) {
set_user_preference('block_myoverview_hidden_course_' . $course->id, true);
}
return $course;
}, $coursedata);
foreach ($courses as $course) {
$generator->enrol_user($student->id, $course->id, 'student');
}
$coursesgenerator = course_get_enrolled_courses_for_logged_in_user(0, $offset, 'shortname ASC', 'shortname');
list($result, $processedcount) = course_filter_courses_by_timeline_classification(
$coursesgenerator,
$classification,
$limit
);
$actual = array_map(function($course) {
return $course->shortname;
}, $result);
$this->assertEquals($expectedcourses, $actual);
$this->assertEquals($expectedprocessedcount, $processedcount);
}
/**
* Testing core_course_core_calendar_get_valid_event_timestart_range when the course has no end date.
@@ -21,7 +21,8 @@
Example context (json):
{
"isfavourite": true
"isfavourite": true,
"hidden": true
}
}}
<div class="ml-auto dropdown">
@@ -46,6 +47,16 @@
{{#str}} removefromfavourites, block_myoverview {{/str}}
</a>
</li>
<li class="{{^hidden}}hidden{{/hidden}}" data-action="hide-course" data-course-id="{{id}}">
<a class="dropdown-item p-a-1" href="#">
{{#str}} aria:hidecourse, block_myoverview, {{fullname}} {{/str}}
</a>
</li>
<li class="{{#hidden}}hidden{{/hidden}}" data-action="show-course" data-course-id="{{id}}">
<a class="dropdown-item p-a-1" href="#">
{{#str}} aria:showcourse, block_myoverview, {{fullname}} {{/str}}
</a>
</li>
</ul>
</div>
</div>