mirror of
https://github.com/moodle/moodle.git
synced 2025-04-13 12:32:08 +02:00
Merge branch 'MDL-63457-master' of git://github.com/peterRd/moodle
This commit is contained in:
commit
f3d077d0a7
2
blocks/myoverview/amd/build/main.min.js
vendored
2
blocks/myoverview/amd/build/main.min.js
vendored
@ -1 +1 @@
|
||||
define(["jquery","block_myoverview/view","block_myoverview/view_nav"],function(a,b,c){var d={COURSES_VIEW:'[data-region="courses-view"]',COURSES_VIEW_CONTENT:'[data-region="course-view-content"]'},e=function(e){e=a(e);var f=e.find(d.COURSES_VIEW),g=e.find(d.COURSES_VIEW_CONTENT);c.init(e,f,g),b.init(f,g)};return{init:e}});
|
||||
define(["jquery","block_myoverview/view","block_myoverview/view_nav"],function(a,b,c){var d=function(d){d=a(d),c.init(d),b.init(d)};return{init:d}});
|
1
blocks/myoverview/amd/build/selectors.min.js
vendored
Normal file
1
blocks/myoverview/amd/build/selectors.min.js
vendored
Normal file
@ -0,0 +1 @@
|
||||
define([],function(){return{courseView:{region:'[data-region="courses-view"]',regionContent:'[data-region="course-view-content"]'}}});
|
2
blocks/myoverview/amd/build/view.min.js
vendored
2
blocks/myoverview/amd/build/view.min.js
vendored
File diff suppressed because one or more lines are too long
2
blocks/myoverview/amd/build/view_nav.min.js
vendored
2
blocks/myoverview/amd/build/view_nav.min.js
vendored
@ -1 +1 @@
|
||||
define(["jquery","core/custom_interaction_events","block_myoverview/repository","block_myoverview/view"],function(a,b,c,d){var e={FILTERS:'[data-region="filter"]',FILTER_OPTION:"[data-filter]",DISPLAY_OPTION:"[data-display-option]"},f=function(a,b){var d=null;d="display"==a?"block_myoverview_user_view_preference":"sort"==a?"block_myoverview_user_sort_preference":"block_myoverview_user_grouping_preference",c.updateUserPreferences({preferences:[{type:d,value:b}]})},g=function(c,g,h){var i=c.find(e.FILTERS);b.define(i,[b.events.activate]),i.on(b.events.activate,e.FILTER_OPTION,function(b,c){var e=a(b.target);if(!e.hasClass("active")){var i=e.attr("data-filter"),j="data-"+i,k=e.attr("data-value"),l=e.attr("data-pref");g.attr(j,k),f(i,l),d.init(g,h),c.originalEvent.preventDefault()}}),b.define(i,[b.events.activate]),i.on(b.events.activate,e.DISPLAY_OPTION,function(b,c){var e=a(b.target);if(!e.hasClass("active")){var i=e.attr("data-display-option"),j=e.attr("data-value"),k=e.attr("data-pref");f(i,k),g.attr("data-display",j),d.reset(g,h),c.originalEvent.preventDefault()}})},h=function(b,c,d){b=a(b),g(b,c,d)};return{init:h}});
|
||||
define(["jquery","core/custom_interaction_events","block_myoverview/repository","block_myoverview/view","block_myoverview/selectors"],function(a,b,c,d,e){var f={FILTERS:'[data-region="filter"]',FILTER_OPTION:"[data-filter]",DISPLAY_OPTION:"[data-display-option]"},g=function(a,b){var d=null;d="display"==a?"block_myoverview_user_view_preference":"sort"==a?"block_myoverview_user_sort_preference":"block_myoverview_user_grouping_preference",c.updateUserPreferences({preferences:[{type:d,value:b}]})},h=function(c){var h=c.find(f.FILTERS);b.define(h,[b.events.activate]),h.on(b.events.activate,f.FILTER_OPTION,function(b,f){var h=a(b.target);if(!h.hasClass("active")){var i=h.attr("data-filter"),j=h.attr("data-pref");c.find(e.courseView.region).attr("data-"+i,h.attr("data-value")),g(i,j),d.init(c),f.originalEvent.preventDefault()}}),b.define(h,[b.events.activate]),h.on(b.events.activate,f.DISPLAY_OPTION,function(b,f){var h=a(b.target);if(!h.hasClass("active")){var i=h.attr("data-display-option"),j=h.attr("data-pref");c.find(e.courseView.region).attr("data-display",h.attr("data-value")),g(i,j),d.reset(c),f.originalEvent.preventDefault()}})},i=function(b){b=a(b),h(b)};return{init:i}});
|
@ -32,12 +32,6 @@ function(
|
||||
View,
|
||||
ViewNav
|
||||
) {
|
||||
|
||||
var SELECTORS = {
|
||||
COURSES_VIEW: '[data-region="courses-view"]',
|
||||
COURSES_VIEW_CONTENT: '[data-region="course-view-content"]'
|
||||
};
|
||||
|
||||
/**
|
||||
* Initialise all of the modules for the overview block.
|
||||
*
|
||||
@ -45,12 +39,10 @@ function(
|
||||
*/
|
||||
var init = function(root) {
|
||||
root = $(root);
|
||||
var coursesViewRoot = root.find(SELECTORS.COURSES_VIEW);
|
||||
var coursesViewContent = root.find(SELECTORS.COURSES_VIEW_CONTENT);
|
||||
// Initialise the course navigation elements.
|
||||
ViewNav.init(root, coursesViewRoot, coursesViewContent);
|
||||
ViewNav.init(root);
|
||||
// Initialise the courses view modules.
|
||||
View.init(coursesViewRoot, coursesViewContent);
|
||||
View.init(root);
|
||||
};
|
||||
|
||||
return {
|
||||
|
31
blocks/myoverview/amd/src/selectors.js
Normal file
31
blocks/myoverview/amd/src/selectors.js
Normal file
@ -0,0 +1,31 @@
|
||||
// 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/>.
|
||||
|
||||
/**
|
||||
* Javascript to initialise the selectors for the myoverview block.
|
||||
*
|
||||
* @package block_myoverview
|
||||
* @copyright 2018 Peter Dias <peter@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
define([], function() {
|
||||
return {
|
||||
courseView: {
|
||||
region: '[data-region="courses-view"]',
|
||||
regionContent: '[data-region="course-view-content"]'
|
||||
}
|
||||
};
|
||||
});
|
@ -30,7 +30,8 @@ define(
|
||||
'core/custom_interaction_events',
|
||||
'core/notification',
|
||||
'core/templates',
|
||||
'core_course/events'
|
||||
'core_course/events',
|
||||
'block_myoverview/selectors'
|
||||
],
|
||||
function(
|
||||
$,
|
||||
@ -40,10 +41,14 @@ function(
|
||||
CustomEvents,
|
||||
Notification,
|
||||
Templates,
|
||||
CourseEvents
|
||||
CourseEvents,
|
||||
Selectors
|
||||
) {
|
||||
|
||||
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"]',
|
||||
@ -64,6 +69,10 @@ function(
|
||||
|
||||
var loadedPages = [];
|
||||
|
||||
var courseOffset = 0;
|
||||
|
||||
var lastPage = 0;
|
||||
|
||||
/**
|
||||
* Get filter values from DOM.
|
||||
*
|
||||
@ -71,11 +80,12 @@ function(
|
||||
* @return {filters} Set filters.
|
||||
*/
|
||||
var getFilterValues = function(root) {
|
||||
var filters = {};
|
||||
filters.display = root.attr('data-display');
|
||||
filters.grouping = root.attr('data-grouping');
|
||||
filters.sort = root.attr('data-sort');
|
||||
return filters;
|
||||
var courseRegion = root.find(Selectors.courseView.region);
|
||||
return {
|
||||
display: courseRegion.attr('data-display'),
|
||||
grouping: courseRegion.attr('data-grouping'),
|
||||
sort: courseRegion.attr('data-sort')
|
||||
};
|
||||
};
|
||||
|
||||
// We want the paged content controls below the paged content area.
|
||||
@ -90,13 +100,12 @@ function(
|
||||
*
|
||||
* @param {object} filters The filters for this view.
|
||||
* @param {int} limit The number of courses to show.
|
||||
* @param {int} pageNumber The pagenumber to view.
|
||||
* @return {promise} Resolved with an array of courses.
|
||||
*/
|
||||
var getMyCourses = function(filters, limit, pageNumber) {
|
||||
var getMyCourses = function(filters, limit) {
|
||||
|
||||
return Repository.getEnrolledCoursesByTimeline({
|
||||
offset: pageNumber * limit,
|
||||
offset: courseOffset,
|
||||
limit: limit,
|
||||
classification: filters.grouping,
|
||||
sort: filters.sort
|
||||
@ -131,7 +140,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');
|
||||
};
|
||||
|
||||
@ -235,6 +244,61 @@ function(
|
||||
}).catch(Notification.exception);
|
||||
};
|
||||
|
||||
/**
|
||||
* Reset the loadedPages dataset to take into account the hidden element
|
||||
*
|
||||
* @param {Object} root The course overview container
|
||||
* @param {Object} target The course that you want to hide
|
||||
*/
|
||||
var hideElement = function(root, target) {
|
||||
var id = getCourseId(target);
|
||||
|
||||
var pagingBar = root.find('[data-region="paging-bar"]');
|
||||
var jumpto = parseInt(pagingBar.attr('data-active-page-number'));
|
||||
|
||||
// Get a reduced dataset for the current page.
|
||||
var courseList = loadedPages[jumpto];
|
||||
var reducedCourse = courseList.courses.reduce(function(accumulator, current) {
|
||||
if (id != current.id) {
|
||||
accumulator.push(current);
|
||||
}
|
||||
return accumulator;
|
||||
}, []);
|
||||
|
||||
// Get the next page's data if loaded and pop the first element from it
|
||||
if (loadedPages[jumpto + 1] != undefined) {
|
||||
var newElement = loadedPages[jumpto + 1].courses.slice(0, 1);
|
||||
loadedPages[jumpto + 1].courses = loadedPages[jumpto + 1].courses.slice(1);
|
||||
|
||||
reducedCourse = $.merge(reducedCourse, newElement);
|
||||
}
|
||||
|
||||
// Check if the next page is the last page and if it still has data associated to it
|
||||
if (lastPage == jumpto + 1 && loadedPages[jumpto + 1].courses.length == 0) {
|
||||
var pagedContentContainer = root.find('[data-region="paged-content-container"]');
|
||||
PagedContentFactory.resetLastPageNumber($(pagedContentContainer).attr('id'), jumpto);
|
||||
}
|
||||
|
||||
loadedPages[jumpto].courses = reducedCourse;
|
||||
|
||||
// Reduce the course offset
|
||||
courseOffset--;
|
||||
|
||||
// Render the paged content for the current
|
||||
var pagedContentPage = getPagedContentContainer(root, jumpto);
|
||||
renderCourses(root, loadedPages[jumpto]).then(function(html, js) {
|
||||
return Templates.replaceNodeContents(pagedContentPage, html, js);
|
||||
}).catch(Notification.exception);
|
||||
|
||||
// Delete subsequent pages in order to trigger the callback
|
||||
loadedPages.forEach(function(courseList, index) {
|
||||
if (index > jumpto) {
|
||||
var page = getPagedContentContainer(root, index);
|
||||
page.remove();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Set the courses favourite status and push to repository
|
||||
*
|
||||
@ -292,7 +356,7 @@ function(
|
||||
courses: coursesData.courses
|
||||
});
|
||||
} else {
|
||||
var nocoursesimg = root.attr('data-nocoursesimg');
|
||||
var nocoursesimg = root.find(Selectors.courseView.region).attr('data-nocoursesimg');
|
||||
return Templates.render(TEMPLATES.NOCOURSES, {
|
||||
nocoursesimg: nocoursesimg
|
||||
});
|
||||
@ -300,20 +364,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) {
|
||||
var filters = getFilterValues(root);
|
||||
|
||||
var pagedContentPromise = PagedContentFactory.createWithLimit(
|
||||
@ -323,18 +379,65 @@ function(
|
||||
|
||||
pagesData.forEach(function(pageData) {
|
||||
var currentPage = pageData.pageNumber;
|
||||
var pageNumber = pageData.pageNumber - 1;
|
||||
var limit = pageData.limit;
|
||||
|
||||
if (lastPage == currentPage) {
|
||||
// If we are on the last page and have it's data then load it from cache
|
||||
actions.allItemsLoaded(lastPage);
|
||||
promises.push(renderCourses(root, loadedPages[currentPage]));
|
||||
return;
|
||||
}
|
||||
|
||||
// Get 2 pages worth of data as we will need it for the hidden functionality.
|
||||
if (loadedPages[currentPage + 1] == undefined) {
|
||||
if (loadedPages[currentPage] == undefined) {
|
||||
limit *= 2;
|
||||
}
|
||||
}
|
||||
|
||||
var pagePromise = getMyCourses(
|
||||
filters,
|
||||
pageData.limit,
|
||||
pageNumber
|
||||
limit
|
||||
).then(function(coursesData) {
|
||||
if (coursesData.courses.length < pageData.limit) {
|
||||
actions.allItemsLoaded(pageData.pageNumber);
|
||||
var courses = coursesData.courses;
|
||||
var nextPageStart = 0;
|
||||
var pageCourses = [];
|
||||
|
||||
// If current page's data is loaded make sure we max it to page limit
|
||||
if (loadedPages[currentPage] != undefined) {
|
||||
pageCourses = loadedPages[currentPage].courses;
|
||||
var currentPageLength = pageCourses.length;
|
||||
if (currentPageLength < pageData.limit) {
|
||||
nextPageStart = pageData.limit - currentPageLength;
|
||||
pageCourses = $.merge(loadedPages[currentPage].courses, courses.slice(0, nextPageStart));
|
||||
}
|
||||
} else {
|
||||
nextPageStart = pageData.limit;
|
||||
pageCourses = courses.slice(0, pageData.limit);
|
||||
}
|
||||
loadedPages[currentPage] = coursesData;
|
||||
return renderCourses(root, coursesData);
|
||||
|
||||
// Finished setting up the current page
|
||||
loadedPages[currentPage] = {
|
||||
courses: pageCourses
|
||||
};
|
||||
|
||||
// Set up the next page
|
||||
var remainingCourses = courses.slice(nextPageStart, courses.length);
|
||||
loadedPages[currentPage + 1] = {
|
||||
courses: remainingCourses
|
||||
};
|
||||
|
||||
// Set the last page to either the current or next page
|
||||
if (loadedPages[currentPage].courses.length < pageData.limit) {
|
||||
lastPage = currentPage;
|
||||
actions.allItemsLoaded(currentPage);
|
||||
} else if (loadedPages[currentPage + 1] != undefined
|
||||
&& loadedPages[currentPage + 1].courses.length < pageData.limit) {
|
||||
lastPage = currentPage + 1;
|
||||
}
|
||||
|
||||
courseOffset = coursesData.nextoffset;
|
||||
return renderCourses(root, loadedPages[currentPage]);
|
||||
})
|
||||
.catch(Notification.exception);
|
||||
|
||||
@ -347,7 +450,7 @@ function(
|
||||
);
|
||||
|
||||
pagedContentPromise.then(function(html, js) {
|
||||
return Templates.replaceNodeContents(content, html, js);
|
||||
return Templates.replaceNodeContents(root.find(Selectors.courseView.region), html, js);
|
||||
}).catch(Notification.exception);
|
||||
};
|
||||
|
||||
@ -363,14 +466,14 @@ function(
|
||||
|
||||
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();
|
||||
});
|
||||
@ -378,20 +481,75 @@ 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);
|
||||
|
||||
hideElement(root, target);
|
||||
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);
|
||||
|
||||
hideElement(root, target);
|
||||
data.originalEvent.preventDefault();
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Intialise the courses list and cards views on page load.
|
||||
*
|
||||
* @param {object} root The root element for the courses view.
|
||||
*/
|
||||
var init = function(root) {
|
||||
root = $(root);
|
||||
loadedPages = [];
|
||||
lastPage = 0;
|
||||
courseOffset = 0;
|
||||
|
||||
if (!root.attr('data-init')) {
|
||||
registerEventListeners(root);
|
||||
root.attr('data-init', true);
|
||||
}
|
||||
|
||||
initializePagedContent(root);
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
* Reset the courses views to their original
|
||||
* state on first page load.
|
||||
* state on first page load.courseOffset
|
||||
*
|
||||
* This is called when configuration has changed for the event lists
|
||||
* to cause them to reload their data.
|
||||
*
|
||||
* @param {Object} root The root element for the timeline view.
|
||||
* @param {Object} content The content element for the timeline view.
|
||||
*/
|
||||
var reset = function(root, content) {
|
||||
|
||||
var reset = function(root) {
|
||||
if (loadedPages.length > 0) {
|
||||
loadedPages.forEach(function(courseList, index) {
|
||||
var pagedContentPage = getPagedContentContainer(root, index);
|
||||
@ -400,7 +558,7 @@ function(
|
||||
}).catch(Notification.exception);
|
||||
});
|
||||
} else {
|
||||
init(root, content);
|
||||
init(root);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -26,13 +26,15 @@ define(
|
||||
'jquery',
|
||||
'core/custom_interaction_events',
|
||||
'block_myoverview/repository',
|
||||
'block_myoverview/view'
|
||||
'block_myoverview/view',
|
||||
'block_myoverview/selectors'
|
||||
],
|
||||
function(
|
||||
$,
|
||||
CustomEvents,
|
||||
Repository,
|
||||
View
|
||||
View,
|
||||
Selectors
|
||||
) {
|
||||
|
||||
var SELECTORS = {
|
||||
@ -71,10 +73,8 @@ function(
|
||||
* Event listener for the Display filter (cards, list).
|
||||
*
|
||||
* @param {object} root The root element for the overview block
|
||||
* @param {object} viewRoot The root element for displaying courses.
|
||||
* @param {object} viewContent content The content element for the courses view.
|
||||
*/
|
||||
var registerSelector = function(root, viewRoot, viewContent) {
|
||||
var registerSelector = function(root) {
|
||||
|
||||
var Selector = root.find(SELECTORS.FILTERS);
|
||||
|
||||
@ -91,16 +91,13 @@ function(
|
||||
}
|
||||
|
||||
var filter = option.attr('data-filter');
|
||||
var attributename = 'data-' + filter;
|
||||
var value = option.attr('data-value');
|
||||
var pref = option.attr('data-pref');
|
||||
|
||||
viewRoot.attr(attributename, value);
|
||||
|
||||
root.find(Selectors.courseView.region).attr('data-' + filter, option.attr('data-value'));
|
||||
updatePreferences(filter, pref);
|
||||
|
||||
// Reset the views.
|
||||
View.init(viewRoot, viewContent);
|
||||
View.init(root);
|
||||
|
||||
data.originalEvent.preventDefault();
|
||||
}
|
||||
@ -118,12 +115,11 @@ function(
|
||||
}
|
||||
|
||||
var filter = option.attr('data-display-option');
|
||||
var value = option.attr('data-value');
|
||||
var pref = option.attr('data-pref');
|
||||
|
||||
root.find(Selectors.courseView.region).attr('data-display', option.attr('data-value'));
|
||||
updatePreferences(filter, pref);
|
||||
viewRoot.attr('data-display', value);
|
||||
View.reset(viewRoot, viewContent);
|
||||
View.reset(root);
|
||||
data.originalEvent.preventDefault();
|
||||
}
|
||||
);
|
||||
@ -134,12 +130,10 @@ function(
|
||||
* the navigation elements.
|
||||
*
|
||||
* @param {object} root The root element for the myoverview block
|
||||
* @param {object} viewRoot The root element for the myoverview block
|
||||
* @param {object} viewContent The content element for the myoverview block
|
||||
*/
|
||||
var init = function(root, viewRoot, viewContent) {
|
||||
var init = function(root) {
|
||||
root = $(root);
|
||||
registerSelector(root, viewRoot, viewContent);
|
||||
registerSelector(root);
|
||||
};
|
||||
|
||||
return {
|
||||
|
@ -26,6 +26,7 @@ namespace block_myoverview\privacy;
|
||||
|
||||
use core_privacy\local\request\user_preference_provider;
|
||||
use core_privacy\local\metadata\collection;
|
||||
use \core_privacy\local\request\writer;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
@ -58,14 +59,14 @@ class provider implements \core_privacy\local\metadata\provider, user_preference
|
||||
public static function export_user_preferences(int $userid) {
|
||||
$preference = get_user_preferences('block_myoverview_user_sort_preference', null, $userid);
|
||||
if (isset($preference)) {
|
||||
\core_privacy\local\request\writer::export_user_preference('block_myoverview',
|
||||
writer::export_user_preference('block_myoverview',
|
||||
'block_myoverview_user_sort_preference', get_string($preference, 'block_myoverview'),
|
||||
get_string('privacy:metadata:overviewsortpreference', 'block_myoverview'));
|
||||
}
|
||||
|
||||
$preference = get_user_preferences('block_myoverview_user_view_preference', null, $userid);
|
||||
if (isset($preference)) {
|
||||
\core_privacy\local\request\writer::export_user_preference('block_myoverview',
|
||||
writer::export_user_preference('block_myoverview',
|
||||
'block_myoverview_user_view_preference',
|
||||
get_string($preference, 'block_myoverview'),
|
||||
get_string('privacy:metadata:overviewviewpreference', 'block_myoverview'));
|
||||
@ -73,10 +74,25 @@ class provider implements \core_privacy\local\metadata\provider, user_preference
|
||||
|
||||
$preference = get_user_preferences('block_myoverview_user_grouping_preference', null, $userid);
|
||||
if (isset($preference)) {
|
||||
\core_privacy\local\request\writer::export_user_preference('block_myoverview',
|
||||
writer::export_user_preference('block_myoverview',
|
||||
'block_myoverview_user_grouping_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,
|
||||
])
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -45,6 +45,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['favourites'] = 'Starred';
|
||||
$string['future'] = 'Future';
|
||||
@ -62,6 +63,14 @@ $string['privacy:metadata:overviewgroupingpreference'] = 'The Course overview bl
|
||||
$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';
|
||||
@ -79,4 +88,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';
|
||||
|
@ -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>
|
||||
|
@ -36,7 +36,9 @@
|
||||
|
||||
<div class="card-deck dashboard-card-deck" role="list">
|
||||
{{#courses}}
|
||||
<div class="card dashboard-card" role="listitem">
|
||||
<div class="card dashboard-card" role="listitem"
|
||||
data-region="course-content"
|
||||
data-course-id="{{{id}}}">
|
||||
<a href="{{viewurl}}" tabindex="-1">
|
||||
<div class="card-img dashboard-card-img" style='background-image: url("{{{courseimage}}}");'>
|
||||
<span class="sr-only">{{#str}}aria:courseimage, core_course{{/str}}</span>
|
||||
|
@ -36,7 +36,9 @@
|
||||
|
||||
<ul class="list-group">
|
||||
{{#courses}}
|
||||
<li class="list-group-item course-listitem">
|
||||
<li class="list-group-item course-listitem"
|
||||
data-region="course-content"
|
||||
data-course-id="{{{id}}}">
|
||||
<div class="row-fluid">
|
||||
<div class="{{#hasprogress}}col-6 span6{{/hasprogress}}{{^hasprogress}}col-11 span11{{/hasprogress}} p-l-0">
|
||||
<div class="d-flex align-items-center">
|
||||
|
@ -36,7 +36,9 @@
|
||||
}}
|
||||
<div role="list">
|
||||
{{#courses}}
|
||||
<div class="course-summaryitem m-b-1 p-2" role="listitem">
|
||||
<div class="course-summaryitem m-b-1 p-2" role="listitem"
|
||||
data-region="course-content"
|
||||
data-course-id="{{{id}}}">
|
||||
<div class="row-fluid d-flex">
|
||||
<a href="{{viewurl}}" class="col-sm-4 col-xl-3 span4 position-relative" tabindex="-1">
|
||||
<div class="position-absolute">
|
||||
|
@ -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 dashboard-card' and contains(.,'Course 2')]" "xpath_element"
|
||||
And I click on "Hide from view" "link" in the "//div[@class='card dashboard-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 dashboard-card' and contains(.,'Course 1')]" "xpath_element"
|
||||
And I click on "Hide from view" "link" in the "//div[@class='card dashboard-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 dashboard-card' and contains(.,'Course 5')]" "xpath_element"
|
||||
And I click on "Hide from view" "link" in the "//div[@class='card dashboard-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
|
||||
|
@ -28,11 +28,11 @@ Feature: The my overview block allows users to favourite their courses
|
||||
When I click on ".coursemenubtn" "css_element" in the "//div[@class='card dashboard-card' and contains(.,'Course 2')]" "xpath_element"
|
||||
And I click on "Star this course" "link" in the "//div[@class='card dashboard-card' and contains(.,'Course 2')]" "xpath_element"
|
||||
And I reload the page
|
||||
Then "//div[@role='listitem' and contains(.,'Course 2')]//span[@data-region='is-favourite' and @aria-hidden='false']" "xpath_element" should exist
|
||||
And "//div[@role='listitem' and contains(.,'Course 2')]//span[@data-region='is-favourite' and @aria-hidden='true']" "xpath_element" should not exist
|
||||
And "//div[@role='listitem' and contains(.,'Course 2')]//span[@data-region='not-favourite' and @aria-hidden='true']" "xpath_element" should exist
|
||||
And "//div[@role='listitem' and contains(.,'Course 1')]//span[@data-region='is-favourite' and @aria-hidden='true']" "xpath_element" should exist
|
||||
And "//div[@role='listitem' and contains(.,'Course 3')]//span[@data-region='is-favourite' and @aria-hidden='true']" "xpath_element" should exist
|
||||
Then "//div[@class='card dashboard-card' and contains(.,'Course 2')]//span[@data-region='is-favourite' and @aria-hidden='false']" "xpath_element" should exist
|
||||
And "//div[@class='card dashboard-card' and contains(.,'Course 2')]//span[@data-region='is-favourite' and @aria-hidden='true']" "xpath_element" should not exist
|
||||
And "//div[@class='card dashboard-card' and contains(.,'Course 2')]//span[@data-region='not-favourite' and @aria-hidden='true']" "xpath_element" should exist
|
||||
And "//div[@class='card dashboard-card' and contains(.,'Course 1')]//span[@data-region='is-favourite' and @aria-hidden='true']" "xpath_element" should exist
|
||||
And "//div[@class='card dashboard-card' and contains(.,'Course 3')]//span[@data-region='is-favourite' and @aria-hidden='true']" "xpath_element" should exist
|
||||
And I log out
|
||||
|
||||
Scenario: Star a course and switch display to list
|
||||
|
@ -0,0 +1,83 @@
|
||||
@block @block_myoverview @javascript
|
||||
Feature: The my overview block allows users to hide 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 dashboard-card' and contains(.,'Course 2')]" "xpath_element"
|
||||
And I click on "Hide from view" "link" in the "//div[@class='card dashboard-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 dashboard-card' and contains(.,'Course 2')]" "xpath_element"
|
||||
And I click on "Star this course" "link" in the "//div[@class='card dashboard-card' and contains(.,'Course 2')]" "xpath_element"
|
||||
And I click on ".coursemenubtn" "css_element" in the "//div[@class='card dashboard-card' and contains(.,'Course 2')]" "xpath_element"
|
||||
And I click on "Hide from view" "link" in the "//div[@class='card dashboard-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 dashboard-card' and contains(.,'Course 2')]" "xpath_element"
|
||||
And I click on "Hide from view" "link" in the "//div[@class='card dashboard-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 dashboard-card' and contains(.,'Course 2')]" "xpath_element"
|
||||
And I click on "Show this course" "link" in the "//div[@class='card dashboard-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 dashboard-card' and contains(.,'Course 2')]" "xpath_element"
|
||||
And I click on "Star this course" "link" in the "//div[@class='card dashboard-card' and contains(.,'Course 2')]" "xpath_element"
|
||||
And I click on ".coursemenubtn" "css_element" in the "//div[@class='card dashboard-card' and contains(.,'Course 2')]" "xpath_element"
|
||||
And I click on "Hide from view" "link" in the "//div[@class='card dashboard-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 dashboard-card' and contains(.,'Course 2')]" "xpath_element"
|
||||
And I click on "Show this course" "link" in the "//div[@class='card dashboard-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
|
@ -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
|
||||
);
|
||||
}
|
||||
}
|
@ -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
|
||||
)
|
||||
);
|
||||
}
|
||||
|
@ -3698,6 +3698,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');
|
||||
}
|
||||
@ -3706,7 +3708,17 @@ class core_course_external extends external_api {
|
||||
|
||||
$requiredproperties = course_summary_exporter::define_properties();
|
||||
$fields = join(',', array_keys($requiredproperties));
|
||||
$courses = course_get_enrolled_courses_for_logged_in_user(0, $offset, $sort, $fields);
|
||||
$hiddencourses = get_hidden_courses_on_timeline();
|
||||
$courses = [];
|
||||
|
||||
// If the timeline requires the hidden courses then restrict the result to only $hiddencourses else exclude.
|
||||
if ($classification == COURSE_TIMELINE_HIDDEN) {
|
||||
$courses = course_get_enrolled_courses_for_logged_in_user(0, $offset, $sort, $fields,
|
||||
COURSE_DB_QUERY_LIMIT, $hiddencourses);
|
||||
} else {
|
||||
$courses = course_get_enrolled_courses_for_logged_in_user(0, $offset, $sort, $fields,
|
||||
COURSE_DB_QUERY_LIMIT, [], $hiddencourses);
|
||||
}
|
||||
|
||||
$favouritecourseids = [];
|
||||
$ufservice = \core_favourites\service_factory::get_service_for_user_context(\context_user::instance($USER->id));
|
||||
@ -3725,7 +3737,6 @@ class core_course_external extends external_api {
|
||||
$favouritecourseids,
|
||||
$limit
|
||||
);
|
||||
|
||||
} else {
|
||||
list($filteredcourses, $processedcount) = course_filter_courses_by_timeline_classification(
|
||||
$courses,
|
||||
|
@ -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) {
|
||||
@ -4190,6 +4191,8 @@ function course_classify_courses_for_timeline(array $courses) {
|
||||
* @param string|null $sort SQL string for sorting
|
||||
* @param string|null $fields SQL string for fields to be returned
|
||||
* @param int $dbquerylimit The number of records to load per DB request
|
||||
* @param array $includecourses courses ids to be restricted
|
||||
* @param array $hiddencourses courses ids to be excluded
|
||||
* @return Generator
|
||||
*/
|
||||
function course_get_enrolled_courses_for_logged_in_user(
|
||||
@ -4197,14 +4200,16 @@ function course_get_enrolled_courses_for_logged_in_user(
|
||||
int $offset = 0,
|
||||
string $sort = null,
|
||||
string $fields = null,
|
||||
int $dbquerylimit = COURSE_DB_QUERY_LIMIT
|
||||
int $dbquerylimit = COURSE_DB_QUERY_LIMIT,
|
||||
array $includecourses = [],
|
||||
array $hiddencourses = []
|
||||
) : Generator {
|
||||
|
||||
$haslimit = !empty($limit);
|
||||
$recordsloaded = 0;
|
||||
$querylimit = (!$haslimit || $limit > $dbquerylimit) ? $dbquerylimit : $limit;
|
||||
|
||||
while ($courses = enrol_get_my_courses($fields, $sort, $querylimit, [], false, $offset)) {
|
||||
while ($courses = enrol_get_my_courses($fields, $sort, $querylimit, $includecourses, false, $offset, $hiddencourses)) {
|
||||
yield from $courses;
|
||||
|
||||
$recordsloaded += $querylimit;
|
||||
@ -4242,7 +4247,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 +4260,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++;
|
||||
}
|
||||
@ -4494,3 +4503,29 @@ function can_download_from_backup_filearea($filearea, \context $context, stdClas
|
||||
}
|
||||
return $candownload;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of hidden courses
|
||||
*
|
||||
* @param int|object|null $user User override to get the filter from. Defaults to current user
|
||||
* @return array $ids List of hidden courses
|
||||
* @throws coding_exception
|
||||
*/
|
||||
function get_hidden_courses_on_timeline($user = null) {
|
||||
global $USER;
|
||||
|
||||
if (empty($user)) {
|
||||
$user = $USER->id;
|
||||
}
|
||||
|
||||
$preferences = get_user_preferences(null, null, $user);
|
||||
$ids = [];
|
||||
foreach ($preferences as $key => $value) {
|
||||
if (preg_match('/block_myoverview_hidden_course_(\d)+/', $key)) {
|
||||
$id = preg_split('/block_myoverview_hidden_course_/', $key);
|
||||
$ids[] = $id[1];
|
||||
}
|
||||
}
|
||||
|
||||
return $ids;
|
||||
}
|
||||
|
@ -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.
|
||||
|
@ -604,6 +604,60 @@ class core_enrollib_testcase extends advanced_testcase {
|
||||
$this->assertEquals($course2->id, $courses[$course2->id]->id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the enrol_get_my_courses function when using the $includehidden parameter, which
|
||||
* should remove any courses hidden from the user's timeline
|
||||
*
|
||||
* @throws coding_exception
|
||||
* @throws dml_exception
|
||||
*/
|
||||
public function test_enrol_get_my_courses_include_hidden() {
|
||||
global $DB, $CFG;
|
||||
|
||||
$this->resetAfterTest(true);
|
||||
|
||||
// Create test user and 4 courses, two of which have guest access enabled.
|
||||
$user = $this->getDataGenerator()->create_user();
|
||||
$course1 = $this->getDataGenerator()->create_course(
|
||||
(object)array('shortname' => 'X',
|
||||
'enrol_guest_status_0' => ENROL_INSTANCE_DISABLED,
|
||||
'enrol_guest_password_0' => ''));
|
||||
$course2 = $this->getDataGenerator()->create_course(
|
||||
(object)array('shortname' => 'Z',
|
||||
'enrol_guest_status_0' => ENROL_INSTANCE_ENABLED,
|
||||
'enrol_guest_password_0' => ''));
|
||||
$course3 = $this->getDataGenerator()->create_course(
|
||||
(object)array('shortname' => 'Y',
|
||||
'enrol_guest_status_0' => ENROL_INSTANCE_ENABLED,
|
||||
'enrol_guest_password_0' => 'frog'));
|
||||
$course4 = $this->getDataGenerator()->create_course(
|
||||
(object)array('shortname' => 'W',
|
||||
'enrol_guest_status_0' => ENROL_INSTANCE_DISABLED,
|
||||
'enrol_guest_password_0' => ''));
|
||||
|
||||
// User is enrolled in first course.
|
||||
$this->getDataGenerator()->enrol_user($user->id, $course1->id);
|
||||
$this->getDataGenerator()->enrol_user($user->id, $course2->id);
|
||||
$this->getDataGenerator()->enrol_user($user->id, $course3->id);
|
||||
$this->getDataGenerator()->enrol_user($user->id, $course4->id);
|
||||
|
||||
// Check enrol_get_my_courses basic use (without include hidden provided).
|
||||
$this->setUser($user);
|
||||
$courses = enrol_get_my_courses();
|
||||
$this->assertEquals([$course4->id, $course3->id, $course2->id, $course1->id], array_keys($courses));
|
||||
|
||||
// Hide a course.
|
||||
set_user_preference('block_myoverview_hidden_course_' . $course3->id, true);
|
||||
|
||||
// Hidden course shouldn't be returned.
|
||||
$courses = enrol_get_my_courses(null, null, 0, [], false, 0, [$course3->id]);
|
||||
$this->assertEquals([$course4->id, $course2->id, $course1->id], array_keys($courses));
|
||||
|
||||
// Offset should take into account hidden course.
|
||||
$courses = enrol_get_my_courses(null, null, 0, [], false, 2, [$course3->id]);
|
||||
$this->assertEquals([$course1->id], array_keys($courses));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the enrol_get_my_courses function when using the $allaccessible parameter, which
|
||||
* includes a wider range of courses (enrolled courses + other accessible ones).
|
||||
|
2
lib/amd/build/paged_content_factory.min.js
vendored
2
lib/amd/build/paged_content_factory.min.js
vendored
@ -1 +1 @@
|
||||
define(["jquery","core/templates","core/notification","core/paged_content"],function(a,b,c,d){var e={PAGED_CONTENT:"core/paged_content"},f={ITEMS_PER_PAGE_SINGLE:25,ITEMS_PER_PAGE_ARRAY:[25,50,100,0],MAX_PAGES:3},g=function(){return{pagingbar:!1,pagingdropdown:!1,skipjs:!0,ignorecontrolwhileloading:!0,controlplacementbottom:!1}},h=function(){return{showitemsperpageselector:!1,itemsperpage:35,previous:!0,next:!0,activepagenumber:1,hidecontrolonsinglepage:!0,pages:[]}},i=function(a,b){var c=1;if(a>0){var d=a%b;d?(a-=d,c=a/b+1):c=a/b}return c},j=function(b,c){null===c&&(c=f.ITEMS_PER_PAGE_SINGLE),a.isArray(c)&&(c=c[0]);var d=h();d.itemsperpage=c;for(var e=i(b,c),g=1;g<=e;g++){var j={number:g,page:""+g};1===g&&(j.active=!0),d.pages.push(j)}return d},k=function(b){if(a.isArray(b)){var c=b.map(function(a){return"number"==typeof a?{value:a,active:!1}:a}),d=c.filter(function(a){return a.active});return d.length||(c[0].active=!0),c}return b},l=function(b){null===b&&(b=f.ITEMS_PER_PAGE_ARRAY);var c=h();return c.itemsperpage=k(b),c.showitemsperpageselector=a.isArray(b),c},m=function(a,b){return a?j(a,b):l(b)},n=function(b,c){if(null===b&&(b=f.ITEMS_PER_PAGE_SINGLE),a.isArray(b))return{options:b};var d={options:[]},e=0,g=0,h=f.MAX_PAGES;c.hasOwnProperty("maxPages")&&(h=c.maxPages);for(var i=1;i<=h;i++){var j=0;i<=2?(j=b,g=b):(g=2*g,j=g),e+=j;var k={itemcount:j,content:e};1===i&&(k.active=!0),d.options.push(k)}return d},o=function(a,b,c){var d=g();return c.hasOwnProperty("ignoreControlWhileLoading")&&(d.ignorecontrolwhileloading=c.ignoreControlWhileLoading),c.hasOwnProperty("controlPlacementBottom")&&(d.controlplacementbottom=c.controlPlacementBottom),c.hasOwnProperty("hideControlOnSinglePage")&&(d.hidecontrolonsinglepage=c.hideControlOnSinglePage),c.hasOwnProperty("ariaLabels")&&(d.arialabels=c.ariaLabels),c.hasOwnProperty("dropdown")&&c.dropdown?d.pagingdropdown=n(b,c):d.pagingbar=m(a,b),d},p=function(a,b){return r(null,null,a,b)},q=function(a,b,c){return r(null,a,b,c)},r=function(f,g,h,i){i=i||{};var j=a.Deferred(),k=o(f,g,i);return b.render(e.PAGED_CONTENT,k).then(function(b,c){b=a(b);var e=b;d.init(e,h),j.resolve(b,c)}).fail(function(a){j.reject(a)}).fail(c.exception),j.promise()},s=function(a,b,c,d){"undefined"==typeof d&&(d={});var e=a.length;return r(e,b,function(b){var d=[];return b.forEach(function(b){var c=b.offset,f=b.limit?c+b.limit:e,g=a.slice(c,f);d.push(g)}),c(d)},d)};return{create:p,createWithLimit:q,createWithTotalAndLimit:r,createFromStaticList:s,createFromAjax:r}});
|
||||
define(["jquery","core/templates","core/notification","core/paged_content","core/paged_content_events","core/pubsub"],function(a,b,c,d,e,f){var g={PAGED_CONTENT:"core/paged_content"},h={ITEMS_PER_PAGE_SINGLE:25,ITEMS_PER_PAGE_ARRAY:[25,50,100,0],MAX_PAGES:3},i=function(){return{pagingbar:!1,pagingdropdown:!1,skipjs:!0,ignorecontrolwhileloading:!0,controlplacementbottom:!1}},j=function(){return{showitemsperpageselector:!1,itemsperpage:35,previous:!0,next:!0,activepagenumber:1,hidecontrolonsinglepage:!0,pages:[]}},k=function(a,b){var c=1;if(a>0){var d=a%b;d?(a-=d,c=a/b+1):c=a/b}return c},l=function(b,c){null===c&&(c=h.ITEMS_PER_PAGE_SINGLE),a.isArray(c)&&(c=c[0]);var d=j();d.itemsperpage=c;for(var e=k(b,c),f=1;f<=e;f++){var g={number:f,page:""+f};1===f&&(g.active=!0),d.pages.push(g)}return d},m=function(b){if(a.isArray(b)){var c=b.map(function(a){return"number"==typeof a?{value:a,active:!1}:a}),d=c.filter(function(a){return a.active});return d.length||(c[0].active=!0),c}return b},n=function(b){null===b&&(b=h.ITEMS_PER_PAGE_ARRAY);var c=j();return c.itemsperpage=m(b),c.showitemsperpageselector=a.isArray(b),c},o=function(a,b){return a?l(a,b):n(b)},p=function(b,c){if(null===b&&(b=h.ITEMS_PER_PAGE_SINGLE),a.isArray(b))return{options:b};var d={options:[]},e=0,f=0,g=h.MAX_PAGES;c.hasOwnProperty("maxPages")&&(g=c.maxPages);for(var i=1;i<=g;i++){var j=0;i<=2?(j=b,f=b):(f=2*f,j=f),e+=j;var k={itemcount:j,content:e};1===i&&(k.active=!0),d.options.push(k)}return d},q=function(a,b,c){var d=i();return c.hasOwnProperty("ignoreControlWhileLoading")&&(d.ignorecontrolwhileloading=c.ignoreControlWhileLoading),c.hasOwnProperty("controlPlacementBottom")&&(d.controlplacementbottom=c.controlPlacementBottom),c.hasOwnProperty("hideControlOnSinglePage")&&(d.hidecontrolonsinglepage=c.hideControlOnSinglePage),c.hasOwnProperty("ariaLabels")&&(d.arialabels=c.ariaLabels),c.hasOwnProperty("dropdown")&&c.dropdown?d.pagingdropdown=p(b,c):d.pagingbar=o(a,b),d},r=function(a,b){return t(null,null,a,b)},s=function(a,b,c){return t(null,a,b,c)},t=function(e,f,h,i){i=i||{};var j=a.Deferred(),k=q(e,f,i);return b.render(g.PAGED_CONTENT,k).then(function(b,c){b=a(b);var e=b;d.init(e,h),j.resolve(b,c)}).fail(function(a){j.reject(a)}).fail(c.exception),j.promise()},u=function(a,b,c,d){"undefined"==typeof d&&(d={});var e=a.length;return t(e,b,function(b){var d=[];return b.forEach(function(b){var c=b.offset,f=b.limit?c+b.limit:e,g=a.slice(c,f);d.push(g)}),c(d)},d)},v=function(a,b){f.publish(a+e.ALL_ITEMS_LOADED,b)};return{create:r,createWithLimit:s,createWithTotalAndLimit:t,createFromStaticList:u,createFromAjax:t,resetLastPageNumber:v}});
|
2
lib/amd/build/paged_content_pages.min.js
vendored
2
lib/amd/build/paged_content_pages.min.js
vendored
@ -1 +1 @@
|
||||
define(["jquery","core/templates","core/notification","core/pubsub","core/paged_content_events"],function(a,b,c,d,e){var f={ROOT:'[data-region="page-container"]',PAGE_REGION:'[data-region="paged-content-page"]',ACTIVE_PAGE_REGION:'[data-region="paged-content-page"].active'},g={PAGING_CONTENT_ITEM:"core/paged_content_page",LOADING:"core/overlay_loading"},h=300,i=function(a,b){return a.find('[data-page="'+b+'"]')},j=function(d){var e=a.Deferred();return d.attr("aria-busy",!0),b.render(g.LOADING,{visible:!0}).then(function(b){var c=a(b),f=setTimeout(function(){d.css("position","relative"),c.appendTo(d)},h);e.always(function(){clearTimeout(f),c.remove(),d.css("position",""),d.removeAttr("aria-busy")})}).fail(c.exception),e},k=function(d,e,f){var h=a.Deferred();return e.then(function(a,e){e=e||"",b.render(g.PAGING_CONTENT_ITEM,{page:f,content:a}).then(function(a){b.appendNodeContents(d,a,e);var c=i(d,f);h.resolve(c)}).fail(function(a){h.reject(a)}).fail(c.exception)}).fail(function(a){h.reject(a)}).fail(c.exception),h.promise()},l=function(b,g,h,l){var m=[],n=[],o=a.Deferred();if(g.forEach(function(a){var c=a.pageNumber,d=i(b,c);d.length?m.push(d):n.push(a)}),n.length&&"function"==typeof l){var p=l(n,{allItemsLoaded:function(a){d.publish(h+e.ALL_ITEMS_LOADED,a)}}),q=p.map(function(a,c){return k(b,a,n[c].pageNumber)});a.when.apply(a,q).then(function(){var a=Array.prototype.slice.call(arguments);o.resolve(a)}).fail(function(a){o.reject(a)}).fail(c.exception)}else o.resolve([]);var r=j(b);o.then(function(a){var c=m.concat(a);b.find(f.PAGE_REGION).addClass("hidden"),c.forEach(function(a){a.removeClass("hidden")})}).then(function(){d.publish(h+e.PAGES_SHOWN,g)}).fail(c.exception).always(function(){r.resolve()})},m=function(b,c,f){b=a(b),d.subscribe(c+e.SHOW_PAGES,function(a){l(b,a,c,f)}),d.subscribe(c+e.SET_ITEMS_PER_PAGE_LIMIT,function(){b.empty()})};return{init:m,rootSelector:f.ROOT}});
|
||||
define(["jquery","core/templates","core/notification","core/pubsub","core/paged_content_events"],function(a,b,c,d,e){var f={ROOT:'[data-region="page-container"]',PAGE_REGION:'[data-region="paged-content-page"]',ACTIVE_PAGE_REGION:'[data-region="paged-content-page"].active'},g={PAGING_CONTENT_ITEM:"core/paged_content_page",LOADING:"core/overlay_loading"},h=300,i=function(a,b){return a.find('[data-page="'+b+'"]')},j=function(d){var e=a.Deferred();return d.attr("aria-busy",!0),b.render(g.LOADING,{visible:!0}).then(function(b){var c=a(b),f=setTimeout(function(){d.css("position","relative"),c.appendTo(d)},h);e.always(function(){clearTimeout(f),c.remove(),d.css("position",""),d.removeAttr("aria-busy")})}).fail(c.exception),e},k=function(d,e,f){var h=a.Deferred();return e.then(function(a,e){e=e||"",b.render(g.PAGING_CONTENT_ITEM,{page:f,content:a}).then(function(a){b.appendNodeContents(d,a,e);var c=i(d,f);h.resolve(c)}).fail(function(a){h.reject(a)}).fail(c.exception)}).fail(function(a){h.reject(a)}).fail(c.exception),h.promise()},l=function(b,g,h,l){var m=[],n=[],o=a.Deferred(),p=!0;if(g.forEach(function(a){var c=a.pageNumber,d=i(b,c);d.length?m.push(d):n.push(a)}),n.length&&"function"==typeof l){var q=l(n,{allItemsLoaded:function(a){d.publish(h+e.ALL_ITEMS_LOADED,a)}}),r=q.map(function(a,c){return k(b,a,n[c].pageNumber)});a.when.apply(a,r).then(function(){var a=Array.prototype.slice.call(arguments);o.resolve(a)}).fail(function(a){o.reject(a)}).fail(c.exception)}else o.resolve([]);var s=j(b);o.then(function(a){var c=m.concat(a);b.find(f.PAGE_REGION).addClass("hidden"),c.forEach(function(a){p&&a.removeClass("hidden")})}).then(function(){d.publish(h+e.PAGES_SHOWN,g)}).fail(c.exception).always(function(){s.resolve()})},m=function(b,c,f){b=a(b),d.subscribe(c+e.SHOW_PAGES,function(a){l(b,a,c,f)}),d.subscribe(c+e.SET_ITEMS_PER_PAGE_LIMIT,function(){b.empty()})};return{init:m,rootSelector:f.ROOT}});
|
@ -25,13 +25,17 @@ define(
|
||||
'jquery',
|
||||
'core/templates',
|
||||
'core/notification',
|
||||
'core/paged_content'
|
||||
'core/paged_content',
|
||||
'core/paged_content_events',
|
||||
'core/pubsub'
|
||||
],
|
||||
function(
|
||||
$,
|
||||
Templates,
|
||||
Notification,
|
||||
PagedContent
|
||||
PagedContent,
|
||||
PagedContentEvents,
|
||||
PubSub
|
||||
) {
|
||||
var TEMPLATES = {
|
||||
PAGED_CONTENT: 'core/paged_content'
|
||||
@ -479,12 +483,24 @@ function(
|
||||
}, config);
|
||||
};
|
||||
|
||||
/**
|
||||
* Reset the last page number for the generated paged-content
|
||||
* This is used when we need a way to update the last page number outside of the getters callback
|
||||
*
|
||||
* @param {String} id ID of the paged content container
|
||||
* @param {Int} lastPageNumber The last page number
|
||||
*/
|
||||
var resetLastPageNumber = function(id, lastPageNumber) {
|
||||
PubSub.publish(id + PagedContentEvents.ALL_ITEMS_LOADED, lastPageNumber);
|
||||
};
|
||||
|
||||
return {
|
||||
create: create,
|
||||
createWithLimit: createWithLimit,
|
||||
createWithTotalAndLimit: createWithTotalAndLimit,
|
||||
createFromStaticList: createFromStaticList,
|
||||
// Backwards compatibility just in case anyone was using this.
|
||||
createFromAjax: createWithTotalAndLimit
|
||||
createFromAjax: createWithTotalAndLimit,
|
||||
resetLastPageNumber: resetLastPageNumber
|
||||
};
|
||||
});
|
||||
|
@ -185,7 +185,7 @@ define(
|
||||
var existingPages = [];
|
||||
var newPageData = [];
|
||||
var newPagesPromise = $.Deferred();
|
||||
|
||||
var shownewpage = true;
|
||||
// Check which of the pages being requests have previously been rendered
|
||||
// so that we only ask for new pages to be rendered by the callback.
|
||||
pagesData.forEach(function(pageData) {
|
||||
@ -239,9 +239,11 @@ define(
|
||||
var pagesToShow = existingPages.concat(newPages);
|
||||
// Hide all existing pages.
|
||||
root.find(SELECTORS.PAGE_REGION).addClass('hidden');
|
||||
// Show each of the pages that were requested.
|
||||
// Show each of the pages that were requested.;
|
||||
pagesToShow.forEach(function(page) {
|
||||
page.removeClass('hidden');
|
||||
if (shownewpage) {
|
||||
page.removeClass('hidden');
|
||||
}
|
||||
});
|
||||
|
||||
return;
|
||||
|
@ -560,9 +560,11 @@ function enrol_add_course_navigation(navigation_node $coursenode, $course) {
|
||||
* @param array $courseids the list of course ids to filter by
|
||||
* @param bool $allaccessible Include courses user is not enrolled in, but can access
|
||||
* @param int $offset Offset the result set by this number
|
||||
* @param array $excludecourses IDs of hidden courses to exclude from search
|
||||
* @return array
|
||||
*/
|
||||
function enrol_get_my_courses($fields = null, $sort = null, $limit = 0, $courseids = [], $allaccessible = false, $offset = 0) {
|
||||
function enrol_get_my_courses($fields = null, $sort = null, $limit = 0, $courseids = [], $allaccessible = false,
|
||||
$offset = 0, $excludecourses = []) {
|
||||
global $DB, $USER, $CFG;
|
||||
|
||||
if ($sort === null) {
|
||||
@ -654,6 +656,12 @@ function enrol_get_my_courses($fields = null, $sort = null, $limit = 0, $coursei
|
||||
$params = array_merge($params, $courseidsparams);
|
||||
}
|
||||
|
||||
if (!empty($excludecourses)) {
|
||||
list($courseidssql, $courseidsparams) = $DB->get_in_or_equal($excludecourses, SQL_PARAMS_NAMED, 'param', false);
|
||||
$wheres = sprintf("%s AND c.id %s", $wheres, $courseidssql);
|
||||
$params = array_merge($params, $courseidsparams);
|
||||
}
|
||||
|
||||
$courseidsql = "";
|
||||
// Logged-in, non-guest users get their enrolled courses.
|
||||
if (!isguestuser() && isloggedin()) {
|
||||
|
@ -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>
|
Loading…
x
Reference in New Issue
Block a user