first commit

This commit is contained in:
CHIEFSOFT\ameye
2024-09-30 18:11:26 -04:00
commit e592ca6823
27270 changed files with 5002257 additions and 0 deletions
+9
View File
@@ -0,0 +1,9 @@
define("block_myoverview/main",["exports","block_myoverview/view","block_myoverview/view_nav"],(function(_exports,View,ViewNav){function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}function _interopRequireWildcard(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}return newObj.default=obj,cache&&cache.set(obj,newObj),newObj}
/**
* Javascript to initialise the myoverview block.
*
* @copyright 2018 Bas Brands <bas@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,View=_interopRequireWildcard(View),ViewNav=_interopRequireWildcard(ViewNav);_exports.init=root=>{ViewNav.init(root),View.init(root)}}));
//# sourceMappingURL=main.min.js.map
@@ -0,0 +1 @@
{"version":3,"file":"main.min.js","sources":["../src/main.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * Javascript to initialise the myoverview block.\n *\n * @copyright 2018 Bas Brands <bas@moodle.com>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport * as View from 'block_myoverview/view';\nimport * as ViewNav from 'block_myoverview/view_nav';\n\n/**\n * Initialise all of the modules for the overview block.\n *\n * @param {object} root The root element for the overview block.\n */\nexport const init = (root) => {\n // Initialise the course navigation elements.\n ViewNav.init(root);\n // Initialise the courses view modules.\n View.init(root);\n};\n"],"names":["root","ViewNav","init","View"],"mappings":";;;;;;4KA8BqBA,OAEjBC,QAAQC,KAAKF,MAEbG,KAAKD,KAAKF"}
+10
View File
@@ -0,0 +1,10 @@
define("block_myoverview/repository",["exports","core/ajax"],(function(_exports,_ajax){var obj;
/**
* A javascript module to retrieve enrolled coruses from the server.
*
* @module block_myoverview/repository
* @copyright 2018 Bas Brands <base@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.setFavouriteCourses=_exports.getEnrolledCoursesByTimeline=_exports.SUMMARY_REQUIRED_FIELDS=_exports.CARDLIST_REQUIRED_FIELDS=void 0,_ajax=(obj=_ajax)&&obj.__esModule?obj:{default:obj};_exports.getEnrolledCoursesByTimeline=args=>{const request={methodname:"core_course_get_enrolled_courses_by_timeline_classification",args:args};return _ajax.default.call([request])[0]};_exports.setFavouriteCourses=args=>{const request={methodname:"core_course_set_favourite_courses",args:args};return _ajax.default.call([request])[0]};_exports.CARDLIST_REQUIRED_FIELDS=["id","fullname","shortname","showcoursecategory","showshortname","visible","enddate"];_exports.SUMMARY_REQUIRED_FIELDS=["id","fullname","shortname","showcoursecategory","showshortname","visible","enddate","summary","summaryformat"]}));
//# sourceMappingURL=repository.min.js.map
@@ -0,0 +1 @@
{"version":3,"file":"repository.min.js","sources":["../src/repository.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * A javascript module to retrieve enrolled coruses from the server.\n *\n * @module block_myoverview/repository\n * @copyright 2018 Bas Brands <base@moodle.com>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Ajax from 'core/ajax';\n\n/**\n * Retrieve a list of enrolled courses.\n *\n * Valid args are:\n * string classification future, inprogress, past\n * int limit number of records to retreive\n * int Offset offset for pagination\n * int sort sort by lastaccess or name\n *\n * @method getEnrolledCoursesByTimeline\n * @param {object} args The request arguments\n * @return {promise} Resolved with an array of courses\n */\nexport const getEnrolledCoursesByTimeline = args => {\n const request = {\n methodname: 'core_course_get_enrolled_courses_by_timeline_classification',\n args: args\n };\n\n return Ajax.call([request])[0];\n};\n\n/**\n * Set the favourite state on a list of courses.\n *\n * Valid args are:\n * Array courses list of course id numbers.\n *\n * @param {Object} args Arguments send to the webservice.\n * @return {Promise} Resolve with warnings.\n */\nexport const setFavouriteCourses = args => {\n const request = {\n methodname: 'core_course_set_favourite_courses',\n args: args\n };\n\n return Ajax.call([request])[0];\n};\n\n/**\n * These course fields are the only ones needed to be included in the results for the card and list views.\n *\n * @type {string[]}\n */\nexport const CARDLIST_REQUIRED_FIELDS = [\n 'id',\n 'fullname',\n 'shortname',\n 'showcoursecategory',\n 'showshortname',\n 'visible',\n 'enddate',\n];\n\n/**\n * These course fields are the only ones needed to be included in the results for the card and list views.\n *\n * @type {string[]}\n */\nexport const SUMMARY_REQUIRED_FIELDS = [\n 'id',\n 'fullname',\n 'shortname',\n 'showcoursecategory',\n 'showshortname',\n 'visible',\n 'enddate',\n 'summary',\n 'summaryformat',\n];\n"],"names":["args","request","methodname","Ajax","call"],"mappings":";;;;;;;oSAsC4CA,aAClCC,QAAU,CACZC,WAAY,8DACZF,KAAMA,aAGHG,cAAKC,KAAK,CAACH,UAAU,iCAYGD,aACzBC,QAAU,CACZC,WAAY,oCACZF,KAAMA,aAGHG,cAAKC,KAAK,CAACH,UAAU,sCAQQ,CACpC,KACA,WACA,YACA,qBACA,gBACA,UACA,4CAQmC,CACnC,KACA,WACA,YACA,qBACA,gBACA,UACA,UACA,UACA"}
+3
View File
@@ -0,0 +1,3 @@
define("block_myoverview/selectors",["exports"],(function(_exports){Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0;return _exports.default={courseView:{region:'[data-region="courses-view"]',regionContent:'[data-region="course-view-content"]'},FILTERS:'[data-region="filter"]',FILTER_OPTION:"[data-filter]",DISPLAY_OPTION:"[data-display-option]",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"]',region:{selectBlock:'[data-region="myoverview"]',clearIcon:'[data-action="clearsearch"]',searchInput:'[data-action="search"]'}},_exports.default}));
//# sourceMappingURL=selectors.min.js.map
@@ -0,0 +1 @@
{"version":3,"file":"selectors.min.js","sources":["../src/selectors.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * Javascript to initialise the selectors for the myoverview block.\n *\n * @copyright 2018 Peter Dias <peter@moodle.com>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nexport default {\n courseView: {\n region: '[data-region=\"courses-view\"]',\n regionContent: '[data-region=\"course-view-content\"]'\n },\n FILTERS: '[data-region=\"filter\"]',\n FILTER_OPTION: '[data-filter]',\n DISPLAY_OPTION: '[data-display-option]',\n ACTION_HIDE_COURSE: '[data-action=\"hide-course\"]',\n ACTION_SHOW_COURSE: '[data-action=\"show-course\"]',\n ACTION_ADD_FAVOURITE: '[data-action=\"add-favourite\"]',\n ACTION_REMOVE_FAVOURITE: '[data-action=\"remove-favourite\"]',\n FAVOURITE_ICON: '[data-region=\"favourite-icon\"]',\n ICON_IS_FAVOURITE: '[data-region=\"is-favourite\"]',\n ICON_NOT_FAVOURITE: '[data-region=\"not-favourite\"]',\n region: {\n selectBlock: '[data-region=\"myoverview\"]',\n clearIcon: '[data-action=\"clearsearch\"]',\n searchInput: '[data-action=\"search\"]',\n },\n};\n"],"names":["courseView","region","regionContent","FILTERS","FILTER_OPTION","DISPLAY_OPTION","ACTION_HIDE_COURSE","ACTION_SHOW_COURSE","ACTION_ADD_FAVOURITE","ACTION_REMOVE_FAVOURITE","FAVOURITE_ICON","ICON_IS_FAVOURITE","ICON_NOT_FAVOURITE","selectBlock","clearIcon","searchInput"],"mappings":"4KAsBe,CACXA,WAAY,CACRC,OAAQ,+BACRC,cAAe,uCAEnBC,QAAS,yBACTC,cAAe,gBACfC,eAAgB,wBAChBC,mBAAoB,8BACpBC,mBAAoB,8BACpBC,qBAAsB,gCACtBC,wBAAyB,mCACzBC,eAAgB,iCAChBC,kBAAmB,+BACnBC,mBAAoB,gCACpBX,OAAQ,CACJY,YAAa,6BACbC,UAAW,8BACXC,YAAa"}
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+9
View File
@@ -0,0 +1,9 @@
define("block_myoverview/view_nav",["exports","jquery","core/custom_interaction_events","core/notification","core_user/repository","block_myoverview/view","block_myoverview/selectors"],(function(_exports,_jquery,CustomEvents,_notification,_repository,View,_selectors){function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}function _interopRequireWildcard(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}return newObj.default=obj,cache&&cache.set(obj,newObj),newObj}function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}
/**
* Manage the timeline view navigation for the overview block.
*
* @copyright 2018 Bas Brands <bas@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_jquery=_interopRequireDefault(_jquery),CustomEvents=_interopRequireWildcard(CustomEvents),_notification=_interopRequireDefault(_notification),View=_interopRequireWildcard(View),_selectors=_interopRequireDefault(_selectors);const updatePreferences=(filter,value)=>{let type=null;return type="display"===filter?"block_myoverview_user_view_preference":"sort"===filter?"block_myoverview_user_sort_preference":"customfieldvalue"===filter?"block_myoverview_user_grouping_customfieldvalue_preference":"block_myoverview_user_grouping_preference",(0,_repository.setUserPreference)(type,value).catch(_notification.default.exception)};_exports.init=root=>{(root=>{const Selector=root.find(_selectors.default.FILTERS);CustomEvents.define(Selector,[CustomEvents.events.activate]),Selector.on(CustomEvents.events.activate,_selectors.default.FILTER_OPTION,((e,data)=>{const option=(0,_jquery.default)(e.target);if(option.hasClass("active"))return;const filter=option.attr("data-filter"),pref=option.attr("data-pref"),customfieldvalue=option.attr("data-customfieldvalue");root.find(_selectors.default.courseView.region).attr("data-"+filter,option.attr("data-value")),updatePreferences(filter,pref),customfieldvalue&&(root.find(_selectors.default.courseView.region).attr("data-customfieldvalue",customfieldvalue),updatePreferences("customfieldvalue",customfieldvalue));const page=document.querySelector(_selectors.default.region.selectBlock),input=page.querySelector(_selectors.default.region.searchInput);if(""!==input.value){const clearIcon=page.querySelector(_selectors.default.region.clearIcon);input.value="",View.clearSearch(clearIcon,root)}else View.init(root);data.originalEvent.preventDefault()})),Selector.on(CustomEvents.events.activate,_selectors.default.DISPLAY_OPTION,((e,data)=>{const option=(0,_jquery.default)(e.target);if(option.hasClass("active"))return;const filter=option.attr("data-display-option"),pref=option.attr("data-pref");root.find(_selectors.default.courseView.region).attr("data-display",option.attr("data-value")),updatePreferences(filter,pref),View.reset(root),data.originalEvent.preventDefault()}))})(root=(0,_jquery.default)(root))}}));
//# sourceMappingURL=view_nav.min.js.map
File diff suppressed because one or more lines are too long
+36
View File
@@ -0,0 +1,36 @@
// 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 myoverview block.
*
* @copyright 2018 Bas Brands <bas@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
import * as View from 'block_myoverview/view';
import * as ViewNav from 'block_myoverview/view_nav';
/**
* Initialise all of the modules for the overview block.
*
* @param {object} root The root element for the overview block.
*/
export const init = (root) => {
// Initialise the course navigation elements.
ViewNav.init(root);
// Initialise the courses view modules.
View.init(root);
};
+96
View File
@@ -0,0 +1,96 @@
// 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/>.
/**
* A javascript module to retrieve enrolled coruses from the server.
*
* @module block_myoverview/repository
* @copyright 2018 Bas Brands <base@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
import Ajax from 'core/ajax';
/**
* Retrieve a list of enrolled courses.
*
* Valid args are:
* string classification future, inprogress, past
* int limit number of records to retreive
* int Offset offset for pagination
* int sort sort by lastaccess or name
*
* @method getEnrolledCoursesByTimeline
* @param {object} args The request arguments
* @return {promise} Resolved with an array of courses
*/
export const getEnrolledCoursesByTimeline = args => {
const request = {
methodname: 'core_course_get_enrolled_courses_by_timeline_classification',
args: args
};
return Ajax.call([request])[0];
};
/**
* Set the favourite state on a list of courses.
*
* Valid args are:
* Array courses list of course id numbers.
*
* @param {Object} args Arguments send to the webservice.
* @return {Promise} Resolve with warnings.
*/
export const setFavouriteCourses = args => {
const request = {
methodname: 'core_course_set_favourite_courses',
args: args
};
return Ajax.call([request])[0];
};
/**
* These course fields are the only ones needed to be included in the results for the card and list views.
*
* @type {string[]}
*/
export const CARDLIST_REQUIRED_FIELDS = [
'id',
'fullname',
'shortname',
'showcoursecategory',
'showshortname',
'visible',
'enddate',
];
/**
* These course fields are the only ones needed to be included in the results for the card and list views.
*
* @type {string[]}
*/
export const SUMMARY_REQUIRED_FIELDS = [
'id',
'fullname',
'shortname',
'showcoursecategory',
'showshortname',
'visible',
'enddate',
'summary',
'summaryformat',
];
+43
View File
@@ -0,0 +1,43 @@
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Javascript to initialise the selectors for the myoverview block.
*
* @copyright 2018 Peter Dias <peter@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
export default {
courseView: {
region: '[data-region="courses-view"]',
regionContent: '[data-region="course-view-content"]'
},
FILTERS: '[data-region="filter"]',
FILTER_OPTION: '[data-filter]',
DISPLAY_OPTION: '[data-display-option]',
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"]',
region: {
selectBlock: '[data-region="myoverview"]',
clearIcon: '[data-action="clearsearch"]',
searchInput: '[data-action="search"]',
},
};
+890
View File
@@ -0,0 +1,890 @@
// 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/>.
/**
* Manage the courses view for the overview block.
*
* @copyright 2018 Bas Brands <bas@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
import $ from 'jquery';
import * as Repository from 'block_myoverview/repository';
import * as PagedContentFactory from 'core/paged_content_factory';
import * as PubSub from 'core/pubsub';
import * as CustomEvents from 'core/custom_interaction_events';
import * as Notification from 'core/notification';
import * as Templates from 'core/templates';
import * as CourseEvents from 'core_course/events';
import SELECTORS from 'block_myoverview/selectors';
import * as PagedContentEvents from 'core/paged_content_events';
import * as Aria from 'core/aria';
import {debounce} from 'core/utils';
import {setUserPreference} from 'core_user/repository';
const TEMPLATES = {
COURSES_CARDS: 'block_myoverview/view-cards',
COURSES_LIST: 'block_myoverview/view-list',
COURSES_SUMMARY: 'block_myoverview/view-summary',
NOCOURSES: 'core_course/no-courses'
};
const GROUPINGS = {
GROUPING_ALLINCLUDINGHIDDEN: 'allincludinghidden',
GROUPING_ALL: 'all',
GROUPING_INPROGRESS: 'inprogress',
GROUPING_FUTURE: 'future',
GROUPING_PAST: 'past',
GROUPING_FAVOURITES: 'favourites',
GROUPING_HIDDEN: 'hidden'
};
const NUMCOURSES_PERPAGE = [12, 24, 48, 96, 0];
let loadedPages = [];
let courseOffset = 0;
let lastPage = 0;
let lastLimit = 0;
let namespace = null;
/**
* Whether the summary display has been loaded.
*
* If true, this means that courses have been loaded with the summary text.
* Otherwise, switching to the summary display mode will require course data to be fetched with the summary text.
*
* @type {boolean}
*/
let summaryDisplayLoaded = false;
/**
* Get filter values from DOM.
*
* @param {object} root The root element for the courses view.
* @return {filters} Set filters.
*/
const getFilterValues = root => {
const courseRegion = root.find(SELECTORS.courseView.region);
return {
display: courseRegion.attr('data-display'),
grouping: courseRegion.attr('data-grouping'),
sort: courseRegion.attr('data-sort'),
displaycategories: courseRegion.attr('data-displaycategories'),
customfieldname: courseRegion.attr('data-customfieldname'),
customfieldvalue: courseRegion.attr('data-customfieldvalue'),
};
};
// We want the paged content controls below the paged content area.
// and the controls should be ignored while data is loading.
const DEFAULT_PAGED_CONTENT_CONFIG = {
ignoreControlWhileLoading: true,
controlPlacementBottom: true,
persistentLimitKey: 'block_myoverview_user_paging_preference'
};
/**
* Get enrolled courses from backend.
*
* @param {object} filters The filters for this view.
* @param {int} limit The number of courses to show.
* @return {promise} Resolved with an array of courses.
*/
const getMyCourses = (filters, limit) => {
const params = {
offset: courseOffset,
limit: limit,
classification: filters.grouping,
sort: filters.sort,
customfieldname: filters.customfieldname,
customfieldvalue: filters.customfieldvalue,
};
if (filters.display === 'summary') {
params.requiredfields = Repository.SUMMARY_REQUIRED_FIELDS;
summaryDisplayLoaded = true;
} else {
params.requiredfields = Repository.CARDLIST_REQUIRED_FIELDS;
}
return Repository.getEnrolledCoursesByTimeline(params);
};
/**
* Search for enrolled courses from backend.
*
* @param {object} filters The filters for this view.
* @param {int} limit The number of courses to show.
* @param {string} searchValue What does the user want to search within their courses.
* @return {promise} Resolved with an array of courses.
*/
const getSearchMyCourses = (filters, limit, searchValue) => {
const params = {
offset: courseOffset,
limit: limit,
classification: 'search',
sort: filters.sort,
customfieldname: filters.customfieldname,
customfieldvalue: filters.customfieldvalue,
searchvalue: searchValue,
};
if (filters.display === 'summary') {
params.requiredfields = Repository.SUMMARY_REQUIRED_FIELDS;
summaryDisplayLoaded = true;
} else {
params.requiredfields = Repository.CARDLIST_REQUIRED_FIELDS;
summaryDisplayLoaded = false;
}
return Repository.getEnrolledCoursesByTimeline(params);
};
/**
* Get the container element for the favourite icon.
*
* @param {Object} root The course overview container
* @param {Number} courseId Course id number
* @return {Object} The favourite icon container
*/
const getFavouriteIconContainer = (root, courseId) => {
return root.find(SELECTORS.FAVOURITE_ICON + '[data-course-id="' + courseId + '"]');
};
/**
* Get the paged content container element.
*
* @param {Object} root The course overview container
* @param {Number} index Rendered page index.
* @return {Object} The rendered paged container.
*/
const getPagedContentContainer = (root, index) => {
return root.find('[data-region="paged-content-page"][data-page="' + index + '"]');
};
/**
* Get the course id from a favourite element.
*
* @param {Object} root The favourite icon container element.
* @return {Number} Course id.
*/
const getCourseId = root => {
return root.attr('data-course-id');
};
/**
* Hide the favourite icon.
*
* @param {Object} root The favourite icon container element.
* @param {Number} courseId Course id number.
*/
const hideFavouriteIcon = (root, courseId) => {
const iconContainer = getFavouriteIconContainer(root, courseId);
const isFavouriteIcon = iconContainer.find(SELECTORS.ICON_IS_FAVOURITE);
isFavouriteIcon.addClass('hidden');
Aria.hide(isFavouriteIcon);
const notFavourteIcon = iconContainer.find(SELECTORS.ICON_NOT_FAVOURITE);
notFavourteIcon.removeClass('hidden');
Aria.unhide(notFavourteIcon);
};
/**
* Show the favourite icon.
*
* @param {Object} root The course overview container.
* @param {Number} courseId Course id number.
*/
const showFavouriteIcon = (root, courseId) => {
const iconContainer = getFavouriteIconContainer(root, courseId);
const isFavouriteIcon = iconContainer.find(SELECTORS.ICON_IS_FAVOURITE);
isFavouriteIcon.removeClass('hidden');
Aria.unhide(isFavouriteIcon);
const notFavourteIcon = iconContainer.find(SELECTORS.ICON_NOT_FAVOURITE);
notFavourteIcon.addClass('hidden');
Aria.hide(notFavourteIcon);
};
/**
* Get the action menu item
*
* @param {Object} root The course overview container
* @param {Number} courseId Course id.
* @return {Object} The add to favourite menu item.
*/
const getAddFavouriteMenuItem = (root, courseId) => {
return root.find('[data-action="add-favourite"][data-course-id="' + courseId + '"]');
};
/**
* Get the action menu item
*
* @param {Object} root The course overview container
* @param {Number} courseId Course id.
* @return {Object} The remove from favourites menu item.
*/
const getRemoveFavouriteMenuItem = (root, courseId) => {
return root.find('[data-action="remove-favourite"][data-course-id="' + courseId + '"]');
};
/**
* Add course to favourites
*
* @param {Object} root The course overview container
* @param {Number} courseId Course id number
*/
const addToFavourites = (root, courseId) => {
const removeAction = getRemoveFavouriteMenuItem(root, courseId);
const addAction = getAddFavouriteMenuItem(root, courseId);
setCourseFavouriteState(courseId, true).then(success => {
if (success) {
PubSub.publish(CourseEvents.favourited, courseId);
removeAction.removeClass('hidden');
addAction.addClass('hidden');
showFavouriteIcon(root, courseId);
} else {
Notification.alert('Starring course failed', 'Could not change favourite state');
}
return;
}).catch(Notification.exception);
};
/**
* Remove course from favourites
*
* @param {Object} root The course overview container
* @param {Number} courseId Course id number
*/
const removeFromFavourites = (root, courseId) => {
const removeAction = getRemoveFavouriteMenuItem(root, courseId);
const addAction = getAddFavouriteMenuItem(root, courseId);
setCourseFavouriteState(courseId, false).then(success => {
if (success) {
PubSub.publish(CourseEvents.unfavorited, courseId);
removeAction.addClass('hidden');
addAction.removeClass('hidden');
hideFavouriteIcon(root, courseId);
} else {
Notification.alert('Starring course failed', 'Could not change favourite state');
}
return;
}).catch(Notification.exception);
};
/**
* Get the action menu item
*
* @param {Object} root The course overview container
* @param {Number} courseId Course id.
* @return {Object} The hide course menu item.
*/
const getHideCourseMenuItem = (root, courseId) => {
return root.find('[data-action="hide-course"][data-course-id="' + courseId + '"]');
};
/**
* Get the action menu item
*
* @param {Object} root The course overview container
* @param {Number} courseId Course id.
* @return {Object} The show course menu item.
*/
const getShowCourseMenuItem = (root, courseId) => {
return root.find('[data-action="show-course"][data-course-id="' + courseId + '"]');
};
/**
* Hide course
*
* @param {Object} root The course overview container
* @param {Number} courseId Course id number
*/
const hideCourse = (root, courseId) => {
const hideAction = getHideCourseMenuItem(root, courseId);
const showAction = getShowCourseMenuItem(root, courseId);
const filters = getFilterValues(root);
setCourseHiddenState(courseId, true);
// Remove the course from this view as it is now hidden and thus not covered by this view anymore.
// Do only if we are not in "All (including archived)" view mode where really all courses are shown.
if (filters.grouping !== GROUPINGS.GROUPING_ALLINCLUDINGHIDDEN) {
hideElement(root, courseId);
}
hideAction.addClass('hidden');
showAction.removeClass('hidden');
};
/**
* Show course
*
* @param {Object} root The course overview container
* @param {Number} courseId Course id number
*/
const showCourse = (root, courseId) => {
const hideAction = getHideCourseMenuItem(root, courseId);
const showAction = getShowCourseMenuItem(root, courseId);
const filters = getFilterValues(root);
setCourseHiddenState(courseId, null);
// Remove the course from this view as it is now shown again and thus not covered by this view anymore.
// Do only if we are not in "All (including archived)" view mode where really all courses are shown.
if (filters.grouping !== GROUPINGS.GROUPING_ALLINCLUDINGHIDDEN) {
hideElement(root, courseId);
}
hideAction.removeClass('hidden');
showAction.addClass('hidden');
};
/**
* Set the courses hidden status and push to repository
*
* @param {Number} courseId Course id to favourite.
* @param {Boolean} status new hidden status.
* @return {Promise} Repository promise.
*/
const setCourseHiddenState = (courseId, status) => {
// If the given status is not hidden, the preference has to be deleted with a null value.
if (status === false) {
status = null;
}
return setUserPreference(`block_myoverview_hidden_course_${courseId}`, status)
.catch(Notification.exception);
};
/**
* Reset the loadedPages dataset to take into account the hidden element
*
* @param {Object} root The course overview container
* @param {Number} id The course id number
*/
const hideElement = (root, id) => {
const pagingBar = root.find('[data-region="paging-bar"]');
const jumpto = parseInt(pagingBar.attr('data-active-page-number'));
// Get a reduced dataset for the current page.
const courseList = loadedPages[jumpto];
let reducedCourse = courseList.courses.reduce((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 (typeof (loadedPages[jumpto + 1]) !== 'undefined') {
const newElement = loadedPages[jumpto + 1].courses.slice(0, 1);
// Adjust the dataset for the reset of the pages that are loaded.
loadedPages.forEach((courseList, index) => {
if (index > jumpto) {
let popElement = [];
if (typeof (loadedPages[index + 1]) !== 'undefined') {
popElement = loadedPages[index + 1].courses.slice(0, 1);
}
loadedPages[index].courses = [...loadedPages[index].courses.slice(1), ...popElement];
}
});
reducedCourse = [...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) {
const 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.
const pagedContentPage = getPagedContentContainer(root, jumpto);
renderCourses(root, loadedPages[jumpto]).then((html, js) => {
return Templates.replaceNodeContents(pagedContentPage, html, js);
}).catch(Notification.exception);
// Delete subsequent pages in order to trigger the callback.
loadedPages.forEach((courseList, index) => {
if (index > jumpto) {
const page = getPagedContentContainer(root, index);
page.remove();
}
});
};
/**
* Set the courses favourite status and push to repository
*
* @param {Number} courseId Course id to favourite.
* @param {boolean} status new favourite status.
* @return {Promise} Repository promise.
*/
const setCourseFavouriteState = (courseId, status) => {
return Repository.setFavouriteCourses({
courses: [
{
'id': courseId,
'favourite': status
}
]
}).then(result => {
if (result.warnings.length === 0) {
loadedPages.forEach(courseList => {
courseList.courses.forEach((course, index) => {
if (course.id == courseId) {
courseList.courses[index].isfavourite = status;
}
});
});
return true;
} else {
return false;
}
}).catch(Notification.exception);
};
/**
* Given there are no courses to render provide the rendered template.
*
* @param {object} root The root element for the courses view.
* @return {promise} jQuery promise resolved after rendering is complete.
*/
const noCoursesRender = root => {
const nocoursesimg = root.find(SELECTORS.courseView.region).attr('data-nocoursesimg');
const newcourseurl = root.find(SELECTORS.courseView.region).attr('data-newcourseurl');
return Templates.render(TEMPLATES.NOCOURSES, {
nocoursesimg: nocoursesimg,
newcourseurl: newcourseurl
});
};
/**
* Render the dashboard courses.
*
* @param {object} root The root element for the courses view.
* @param {array} coursesData containing array of returned courses.
* @return {promise} jQuery promise resolved after rendering is complete.
*/
const renderCourses = (root, coursesData) => {
const filters = getFilterValues(root);
let currentTemplate = '';
if (filters.display === 'card') {
currentTemplate = TEMPLATES.COURSES_CARDS;
} else if (filters.display === 'list') {
currentTemplate = TEMPLATES.COURSES_LIST;
} else {
currentTemplate = TEMPLATES.COURSES_SUMMARY;
}
if (!coursesData) {
return noCoursesRender(root);
} else {
// Sometimes we get weird objects coming after a failed search, cast to ensure typing functions.
if (Array.isArray(coursesData.courses) === false) {
coursesData.courses = Object.values(coursesData.courses);
}
// Whether the course category should be displayed in the course item.
coursesData.courses = coursesData.courses.map(course => {
course.showcoursecategory = filters.displaycategories === 'on';
return course;
});
if (coursesData.courses.length) {
return Templates.render(currentTemplate, {
courses: coursesData.courses,
});
} else {
return noCoursesRender(root);
}
}
};
/**
* Return the callback to be passed to the subscribe event
*
* @param {object} root The root element for the courses view
* @return {function} Partially applied function that'll execute when passed a limit
*/
const setLimit = root => {
// @param {Number} limit The paged limit that is passed through the event.
return limit => root.find(SELECTORS.courseView.region).attr('data-paging', limit);
};
/**
* Intialise the paged list and cards views on page load.
* Returns an array of paged contents that we would like to handle here
*
* @param {object} root The root element for the courses view
* @param {string} namespace The namespace for all the events attached
*/
const registerPagedEventHandlers = (root, namespace) => {
const event = namespace + PagedContentEvents.SET_ITEMS_PER_PAGE_LIMIT;
PubSub.subscribe(event, setLimit(root));
};
/**
* Figure out how many items are going to be allowed to be rendered in the block.
*
* @param {Number} pagingLimit How many courses to display
* @param {Object} root The course overview container
* @return {Number[]} How many courses will be rendered
*/
const itemsPerPageFunc = (pagingLimit, root) => {
let itemsPerPage = NUMCOURSES_PERPAGE.map(value => {
let active = false;
if (value === pagingLimit) {
active = true;
}
return {
value: value,
active: active
};
});
// Filter out all pagination options which are too large for the amount of courses user is enrolled in.
const totalCourseCount = parseInt(root.find(SELECTORS.courseView.region).attr('data-totalcoursecount'), 10);
return itemsPerPage.filter(pagingOption => {
if (pagingOption.value === 0 && totalCourseCount > 100) {
// To minimise performance issues, do not show the "All" option if the user is enrolled in more than 100 courses.
return false;
}
return pagingOption.value < totalCourseCount;
});
};
/**
* Mutates and controls the loadedPages array and handles the bootstrapping.
*
* @param {Array|Object} coursesData Array of all of the courses to start building the page from
* @param {Number} currentPage What page are we currently on?
* @param {Object} pageData Any current page information
* @param {Object} actions Paged content helper
* @param {null|boolean} activeSearch Are we currently actively searching and building up search results?
*/
const pageBuilder = (coursesData, currentPage, pageData, actions, activeSearch = null) => {
// If the courseData comes in an object then get the value otherwise it is a pure array.
let courses = coursesData.courses ? coursesData.courses : coursesData;
let nextPageStart = 0;
let pageCourses = [];
// If current page's data is loaded make sure we max it to page limit.
if (typeof (loadedPages[currentPage]) !== 'undefined') {
pageCourses = loadedPages[currentPage].courses;
const currentPageLength = pageCourses.length;
if (currentPageLength < pageData.limit) {
nextPageStart = pageData.limit - currentPageLength;
pageCourses = {...loadedPages[currentPage].courses, ...courses.slice(0, nextPageStart)};
}
} else {
// When the page limit is zero, there is only one page of courses, no start for next page.
nextPageStart = pageData.limit || false;
pageCourses = (pageData.limit > 0) ? courses.slice(0, pageData.limit) : courses;
}
// Finished setting up the current page.
loadedPages[currentPage] = {
courses: pageCourses
};
// Set up the next page (if there is more than one page).
const remainingCourses = nextPageStart !== false ? courses.slice(nextPageStart, courses.length) : [];
if (remainingCourses.length) {
loadedPages[currentPage + 1] = {
courses: remainingCourses
};
}
// Set the last page to either the current or next page.
if (loadedPages[currentPage].courses.length < pageData.limit || !remainingCourses.length) {
lastPage = currentPage;
if (activeSearch === null) {
actions.allItemsLoaded(currentPage);
}
} else if (typeof (loadedPages[currentPage + 1]) !== 'undefined'
&& loadedPages[currentPage + 1].courses.length < pageData.limit) {
lastPage = currentPage + 1;
}
courseOffset = coursesData.nextoffset;
};
/**
* In cases when switching between regular rendering and search rendering we need to reset some variables.
*/
const resetGlobals = () => {
courseOffset = 0;
loadedPages = [];
lastPage = 0;
lastLimit = 0;
};
/**
* The default functionality of fetching paginated courses without special handling.
*
* @return {function(Object, Object, Object, Object, Object, Promise, Number): void}
*/
const standardFunctionalityCurry = () => {
resetGlobals();
return (filters, currentPage, pageData, actions, root, promises, limit) => {
const pagePromise = getMyCourses(
filters,
limit
).then(coursesData => {
pageBuilder(coursesData, currentPage, pageData, actions);
return renderCourses(root, loadedPages[currentPage]);
}).catch(Notification.exception);
promises.push(pagePromise);
};
};
/**
* Initialize the searching functionality so we can call it when required.
*
* @return {function(Object, Number, Object, Object, Object, Promise, Number, String): void}
*/
const searchFunctionalityCurry = () => {
resetGlobals();
return (filters, currentPage, pageData, actions, root, promises, limit, inputValue) => {
const searchingPromise = getSearchMyCourses(
filters,
limit,
inputValue
).then(coursesData => {
pageBuilder(coursesData, currentPage, pageData, actions);
return renderCourses(root, loadedPages[currentPage]);
}).catch(Notification.exception);
promises.push(searchingPromise);
};
};
/**
* Initialise the courses list and cards views on page load.
*
* @param {object} root The root element for the courses view.
* @param {function} promiseFunction How do we fetch the courses and what do we do with them?
* @param {null | string} inputValue What to search for
*/
const initializePagedContent = (root, promiseFunction, inputValue = null) => {
const pagingLimit = parseInt(root.find(SELECTORS.courseView.region).attr('data-paging'), 10);
let itemsPerPage = itemsPerPageFunc(pagingLimit, root);
const config = {...{}, ...DEFAULT_PAGED_CONTENT_CONFIG};
config.eventNamespace = namespace;
const pagedContentPromise = PagedContentFactory.createWithLimit(
itemsPerPage,
(pagesData, actions) => {
let promises = [];
pagesData.forEach(pageData => {
const currentPage = pageData.pageNumber;
let limit = (pageData.limit > 0) ? pageData.limit : 0;
// Reset local variables if limits have changed.
if (+lastLimit !== +limit) {
loadedPages = [];
courseOffset = 0;
lastPage = 0;
}
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;
}
lastLimit = limit;
// Get 2 pages worth of data as we will need it for the hidden functionality.
if (typeof (loadedPages[currentPage + 1]) === 'undefined') {
if (typeof (loadedPages[currentPage]) === 'undefined') {
limit *= 2;
}
}
// Get the current applied filters.
const filters = getFilterValues(root);
// Call the curried function that'll handle the course promise and any manipulation of it.
promiseFunction(filters, currentPage, pageData, actions, root, promises, limit, inputValue);
});
return promises;
},
config
);
pagedContentPromise.then((html, js) => {
registerPagedEventHandlers(root, namespace);
return Templates.replaceNodeContents(root.find(SELECTORS.courseView.region), html, js);
}).catch(Notification.exception);
};
/**
* Listen to, and handle events for the myoverview block.
*
* @param {Object} root The myoverview block container element.
* @param {HTMLElement} page The whole HTMLElement for our block.
*/
const registerEventListeners = (root, page) => {
CustomEvents.define(root, [
CustomEvents.events.activate
]);
root.on(CustomEvents.events.activate, SELECTORS.ACTION_ADD_FAVOURITE, (e, data) => {
const favourite = $(e.target).closest(SELECTORS.ACTION_ADD_FAVOURITE);
const courseId = getCourseId(favourite);
addToFavourites(root, courseId);
data.originalEvent.preventDefault();
});
root.on(CustomEvents.events.activate, SELECTORS.ACTION_REMOVE_FAVOURITE, (e, data) => {
const favourite = $(e.target).closest(SELECTORS.ACTION_REMOVE_FAVOURITE);
const courseId = getCourseId(favourite);
removeFromFavourites(root, courseId);
data.originalEvent.preventDefault();
});
root.on(CustomEvents.events.activate, SELECTORS.FAVOURITE_ICON, (e, data) => {
data.originalEvent.preventDefault();
});
root.on(CustomEvents.events.activate, SELECTORS.ACTION_HIDE_COURSE, (e, data) => {
const target = $(e.target).closest(SELECTORS.ACTION_HIDE_COURSE);
const courseId = getCourseId(target);
hideCourse(root, courseId);
data.originalEvent.preventDefault();
});
root.on(CustomEvents.events.activate, SELECTORS.ACTION_SHOW_COURSE, (e, data) => {
const target = $(e.target).closest(SELECTORS.ACTION_SHOW_COURSE);
const courseId = getCourseId(target);
showCourse(root, courseId);
data.originalEvent.preventDefault();
});
// Searching functionality event handlers.
const input = page.querySelector(SELECTORS.region.searchInput);
const clearIcon = page.querySelector(SELECTORS.region.clearIcon);
clearIcon.addEventListener('click', () => {
input.value = '';
input.focus();
clearSearch(clearIcon, root);
});
input.addEventListener('input', debounce(() => {
if (input.value === '') {
clearSearch(clearIcon, root);
} else {
activeSearch(clearIcon);
initializePagedContent(root, searchFunctionalityCurry(), input.value.trim());
}
}, 1000));
};
/**
* Reset the search icon and trigger the init for the block.
*
* @param {HTMLElement} clearIcon Our closing icon to manipulate.
* @param {Object} root The myoverview block container element.
*/
export const clearSearch = (clearIcon, root) => {
clearIcon.classList.add('d-none');
init(root);
};
/**
* Change the searching icon to its' active state.
*
* @param {HTMLElement} clearIcon Our closing icon to manipulate.
*/
const activeSearch = (clearIcon) => {
clearIcon.classList.remove('d-none');
};
/**
* Intialise the courses list and cards views on page load.
*
* @param {object} root The root element for the courses view.
*/
export const init = root => {
root = $(root);
loadedPages = [];
lastPage = 0;
courseOffset = 0;
if (!root.attr('data-init')) {
const page = document.querySelector(SELECTORS.region.selectBlock);
registerEventListeners(root, page);
namespace = "block_myoverview_" + root.attr('id') + "_" + Math.random();
root.attr('data-init', true);
}
initializePagedContent(root, standardFunctionalityCurry());
};
/**
* Reset the courses views to their original
* 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.
*/
export const reset = root => {
if (loadedPages.length > 0) {
const filters = getFilterValues(root);
// If the display mode is changed to 'summary' but the summary display has not been loaded yet,
// we need to re-fetch the courses to include the course summary text.
if (filters.display === 'summary' && !summaryDisplayLoaded) {
const page = document.querySelector(SELECTORS.region.selectBlock);
const input = page.querySelector(SELECTORS.region.searchInput);
if (input.value !== '') {
initializePagedContent(root, searchFunctionalityCurry(), input.value.trim());
} else {
initializePagedContent(root, standardFunctionalityCurry());
}
} else {
loadedPages.forEach((courseList, index) => {
let pagedContentPage = getPagedContentContainer(root, index);
renderCourses(root, courseList).then((html, js) => {
return Templates.replaceNodeContents(pagedContentPage, html, js);
}).catch(Notification.exception);
});
}
} else {
init(root);
}
};
+134
View File
@@ -0,0 +1,134 @@
// 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/>.
/**
* Manage the timeline view navigation for the overview block.
*
* @copyright 2018 Bas Brands <bas@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
import $ from 'jquery';
import * as CustomEvents from 'core/custom_interaction_events';
import Notification from 'core/notification';
import {setUserPreference} from 'core_user/repository';
import * as View from 'block_myoverview/view';
import SELECTORS from 'block_myoverview/selectors';
/**
* Update the user preference for the block.
*
* @param {String} filter The type of filter: display/sort/grouping.
* @param {String} value The current preferred value.
* @return {Promise}
*/
const updatePreferences = (filter, value) => {
let type = null;
if (filter === 'display') {
type = 'block_myoverview_user_view_preference';
} else if (filter === 'sort') {
type = 'block_myoverview_user_sort_preference';
} else if (filter === 'customfieldvalue') {
type = 'block_myoverview_user_grouping_customfieldvalue_preference';
} else {
type = 'block_myoverview_user_grouping_preference';
}
return setUserPreference(type, value)
.catch(Notification.exception);
};
/**
* Event listener for the Display filter (cards, list).
*
* @param {object} root The root element for the overview block
*/
const registerSelector = root => {
const Selector = root.find(SELECTORS.FILTERS);
CustomEvents.define(Selector, [CustomEvents.events.activate]);
Selector.on(
CustomEvents.events.activate,
SELECTORS.FILTER_OPTION,
(e, data) => {
const option = $(e.target);
if (option.hasClass('active')) {
// If it's already active then we don't need to do anything.
return;
}
const filter = option.attr('data-filter');
const pref = option.attr('data-pref');
const customfieldvalue = option.attr('data-customfieldvalue');
root.find(SELECTORS.courseView.region).attr('data-' + filter, option.attr('data-value'));
updatePreferences(filter, pref);
if (customfieldvalue) {
root.find(SELECTORS.courseView.region).attr('data-customfieldvalue', customfieldvalue);
updatePreferences('customfieldvalue', customfieldvalue);
}
// Reset the views.
// Check if the user is currently in a searching state, if so we'll reset it.
const page = document.querySelector(SELECTORS.region.selectBlock);
const input = page.querySelector(SELECTORS.region.searchInput);
if (input.value !== '') {
const clearIcon = page.querySelector(SELECTORS.region.clearIcon);
input.value = '';
// Triggers the init so wont need to call it again.
View.clearSearch(clearIcon, root);
} else {
View.init(root);
}
data.originalEvent.preventDefault();
}
);
Selector.on(
CustomEvents.events.activate,
SELECTORS.DISPLAY_OPTION,
(e, data) => {
const option = $(e.target);
if (option.hasClass('active')) {
return;
}
const filter = option.attr('data-display-option');
const pref = option.attr('data-pref');
root.find(SELECTORS.courseView.region).attr('data-display', option.attr('data-value'));
updatePreferences(filter, pref);
View.reset(root);
data.originalEvent.preventDefault();
}
);
};
/**
* Initialise the timeline view navigation by adding event listeners to
* the navigation elements.
*
* @param {object} root The root element for the myoverview block
*/
export const init = root => {
root = $(root);
registerSelector(root);
};
+143
View File
@@ -0,0 +1,143 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Contains the class for the My overview block.
*
* @package block_myoverview
* @copyright Mark Nelson <markn@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
/**
* My overview block class.
*
* @package block_myoverview
* @copyright Mark Nelson <markn@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class block_myoverview extends block_base {
/**
* Init.
*/
public function init() {
$this->title = get_string('pluginname', 'block_myoverview');
}
/**
* Returns the contents.
*
* @return stdClass contents of block
*/
public function get_content() {
if (isset($this->content)) {
return $this->content;
}
$group = get_user_preferences('block_myoverview_user_grouping_preference');
$sort = get_user_preferences('block_myoverview_user_sort_preference');
$view = get_user_preferences('block_myoverview_user_view_preference');
$paging = get_user_preferences('block_myoverview_user_paging_preference');
$customfieldvalue = get_user_preferences('block_myoverview_user_grouping_customfieldvalue_preference');
$renderable = new \block_myoverview\output\main($group, $sort, $view, $paging, $customfieldvalue);
$renderer = $this->page->get_renderer('block_myoverview');
$this->content = new stdClass();
$this->content->text = $renderer->render($renderable);
$this->content->footer = '';
return $this->content;
}
/**
* Locations where block can be displayed.
*
* @return array
*/
public function applicable_formats() {
return array('my' => true);
}
/**
* Allow the block to have a configuration page.
*
* @return boolean
*/
public function has_config() {
return true;
}
/**
* Return the plugin config settings for external functions.
*
* @return stdClass the configs for both the block instance and plugin
* @since Moodle 3.8
*/
public function get_config_for_external() {
// Return all settings for all users since it is safe (no private keys, etc..).
$configs = get_config('block_myoverview');
// Get the customfield values (if any).
if ($configs->displaygroupingcustomfield) {
$group = get_user_preferences('block_myoverview_user_grouping_preference');
$sort = get_user_preferences('block_myoverview_user_sort_preference');
$view = get_user_preferences('block_myoverview_user_view_preference');
$paging = get_user_preferences('block_myoverview_user_paging_preference');
$customfieldvalue = get_user_preferences('block_myoverview_user_grouping_customfieldvalue_preference');
$renderable = new \block_myoverview\output\main($group, $sort, $view, $paging, $customfieldvalue);
$customfieldsexport = $renderable->get_customfield_values_for_export();
if (!empty($customfieldsexport)) {
$configs->customfieldsexport = json_encode($customfieldsexport);
}
}
return (object) [
'instance' => new stdClass(),
'plugin' => $configs,
];
}
/**
* Disable block editing on the my courses page.
*
* @return boolean
*/
public function instance_can_be_edited() {
if ($this->page->blocks->is_known_region(BLOCK_POS_LEFT) || $this->page->blocks->is_known_region(BLOCK_POS_RIGHT)) {
return true;
} else {
return false;
}
}
/**
* Hide the block header on the my courses page.
*
* @return boolean
*/
public function hide_header() {
if ($this->page->blocks->is_known_region(BLOCK_POS_LEFT) || $this->page->blocks->is_known_region(BLOCK_POS_RIGHT)) {
return false;
} else {
return true;
}
}
}
+615
View File
@@ -0,0 +1,615 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Class containing data for my overview block.
*
* @package block_myoverview
* @copyright 2017 Ryan Wyllie <ryan@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace block_myoverview\output;
defined('MOODLE_INTERNAL') || die();
use core_competency\url;
use renderable;
use renderer_base;
use templatable;
use stdClass;
require_once($CFG->dirroot . '/blocks/myoverview/lib.php');
/**
* Class containing data for my overview block.
*
* @copyright 2018 Bas Brands <bas@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class main implements renderable, templatable {
/**
* Store the grouping preference.
*
* @var string String matching the grouping constants defined in myoverview/lib.php
*/
private $grouping;
/**
* Store the sort preference.
*
* @var string String matching the sort constants defined in myoverview/lib.php
*/
private $sort;
/**
* Store the view preference.
*
* @var string String matching the view/display constants defined in myoverview/lib.php
*/
private $view;
/**
* Store the paging preference.
*
* @var string String matching the paging constants defined in myoverview/lib.php
*/
private $paging;
/**
* Store the display categories config setting.
*
* @var boolean
*/
private $displaycategories;
/**
* Store the configuration values for the myoverview block.
*
* @var array Array of available layouts matching view/display constants defined in myoverview/lib.php
*/
private $layouts;
/**
* Store a course grouping option setting
*
* @var boolean
*/
private $displaygroupingallincludinghidden;
/**
* Store a course grouping option setting.
*
* @var boolean
*/
private $displaygroupingall;
/**
* Store a course grouping option setting.
*
* @var boolean
*/
private $displaygroupinginprogress;
/**
* Store a course grouping option setting.
*
* @var boolean
*/
private $displaygroupingfuture;
/**
* Store a course grouping option setting.
*
* @var boolean
*/
private $displaygroupingpast;
/**
* Store a course grouping option setting.
*
* @var boolean
*/
private $displaygroupingfavourites;
/**
* Store a course grouping option setting.
*
* @var boolean
*/
private $displaygroupinghidden;
/**
* Store a course grouping option setting.
*
* @var bool
*/
private $displaygroupingcustomfield;
/**
* Store the custom field used by customfield grouping.
*
* @var string
*/
private $customfiltergrouping;
/**
* Store the selected custom field value to group by.
*
* @var string
*/
private $customfieldvalue;
/** @var bool true if grouping selector should be shown, otherwise false. */
protected $displaygroupingselector;
/**
* main constructor.
* Initialize the user preferences
*
* @param string $grouping Grouping user preference
* @param string $sort Sort user preference
* @param string $view Display user preference
* @param int $paging
* @param string $customfieldvalue
*
* @throws \dml_exception
*/
public function __construct($grouping, $sort, $view, $paging, $customfieldvalue = null) {
global $CFG;
// Get plugin config.
$config = get_config('block_myoverview');
// Build the course grouping option name to check if the given grouping is enabled afterwards.
$groupingconfigname = 'displaygrouping'.$grouping;
// Check the given grouping and remember it if it is enabled.
if ($grouping && $config->$groupingconfigname == true) {
$this->grouping = $grouping;
// Otherwise fall back to another grouping in a reasonable order.
// This is done to prevent one-time UI glitches in the case when a user has chosen a grouping option previously which
// was then disabled by the admin in the meantime.
} else {
$this->grouping = $this->get_fallback_grouping($config);
}
unset ($groupingconfigname);
// Remember which custom field value we were using, if grouping by custom field.
$this->customfieldvalue = $customfieldvalue;
// Check and remember the given sorting.
if ($sort) {
$this->sort = $sort;
} else if ($CFG->courselistshortnames) {
$this->sort = BLOCK_MYOVERVIEW_SORTING_SHORTNAME;
} else {
$this->sort = BLOCK_MYOVERVIEW_SORTING_TITLE;
}
// In case sorting remembered is shortname and display extended course names not checked,
// we should revert sorting to title.
if (!$CFG->courselistshortnames && $sort == BLOCK_MYOVERVIEW_SORTING_SHORTNAME) {
$this->sort = BLOCK_MYOVERVIEW_SORTING_TITLE;
}
// Check and remember the given view.
$this->view = $view ? $view : BLOCK_MYOVERVIEW_VIEW_CARD;
// Check and remember the given page size, `null` indicates no page size set
// while a `0` indicates a paging size of `All`.
if (!is_null($paging) && $paging == BLOCK_MYOVERVIEW_PAGING_ALL) {
$this->paging = BLOCK_MYOVERVIEW_PAGING_ALL;
} else {
$this->paging = $paging ? $paging : BLOCK_MYOVERVIEW_PAGING_12;
}
// Check and remember if the course categories should be shown or not.
if (!$config->displaycategories) {
$this->displaycategories = BLOCK_MYOVERVIEW_DISPLAY_CATEGORIES_OFF;
} else {
$this->displaycategories = BLOCK_MYOVERVIEW_DISPLAY_CATEGORIES_ON;
}
// Get and remember the available layouts.
$this->set_available_layouts();
$this->view = $view ? $view : reset($this->layouts);
// Check and remember if the particular grouping options should be shown or not.
$this->displaygroupingallincludinghidden = $config->displaygroupingallincludinghidden;
$this->displaygroupingall = $config->displaygroupingall;
$this->displaygroupinginprogress = $config->displaygroupinginprogress;
$this->displaygroupingfuture = $config->displaygroupingfuture;
$this->displaygroupingpast = $config->displaygroupingpast;
$this->displaygroupingfavourites = $config->displaygroupingfavourites;
$this->displaygroupinghidden = $config->displaygroupinghidden;
$this->displaygroupingcustomfield = ($config->displaygroupingcustomfield && $config->customfiltergrouping);
$this->customfiltergrouping = $config->customfiltergrouping;
// Check and remember if the grouping selector should be shown at all or not.
// It will be shown if more than 1 grouping option is enabled.
$displaygroupingselectors = array($this->displaygroupingallincludinghidden,
$this->displaygroupingall,
$this->displaygroupinginprogress,
$this->displaygroupingfuture,
$this->displaygroupingpast,
$this->displaygroupingfavourites,
$this->displaygroupinghidden);
$displaygroupingselectorscount = count(array_filter($displaygroupingselectors));
if ($displaygroupingselectorscount > 1 || $this->displaygroupingcustomfield) {
$this->displaygroupingselector = true;
} else {
$this->displaygroupingselector = false;
}
unset ($displaygroupingselectors, $displaygroupingselectorscount);
}
/**
* Determine the most sensible fallback grouping to use (in cases where the stored selection
* is no longer available).
* @param object $config
* @return string
*/
private function get_fallback_grouping($config) {
if ($config->displaygroupingall == true) {
return BLOCK_MYOVERVIEW_GROUPING_ALL;
}
if ($config->displaygroupingallincludinghidden == true) {
return BLOCK_MYOVERVIEW_GROUPING_ALLINCLUDINGHIDDEN;
}
if ($config->displaygroupinginprogress == true) {
return BLOCK_MYOVERVIEW_GROUPING_INPROGRESS;
}
if ($config->displaygroupingfuture == true) {
return BLOCK_MYOVERVIEW_GROUPING_FUTURE;
}
if ($config->displaygroupingpast == true) {
return BLOCK_MYOVERVIEW_GROUPING_PAST;
}
if ($config->displaygroupingfavourites == true) {
return BLOCK_MYOVERVIEW_GROUPING_FAVOURITES;
}
if ($config->displaygroupinghidden == true) {
return BLOCK_MYOVERVIEW_GROUPING_HIDDEN;
}
if ($config->displaygroupingcustomfield == true) {
return BLOCK_MYOVERVIEW_GROUPING_CUSTOMFIELD;
}
// In this case, no grouping option is enabled and the grouping is not needed at all.
// But it's better not to leave $this->grouping unset for any unexpected case.
return BLOCK_MYOVERVIEW_GROUPING_ALLINCLUDINGHIDDEN;
}
/**
* Set the available layouts based on the config table settings,
* if none are available, defaults to the cards view.
*
* @throws \dml_exception
*
*/
public function set_available_layouts() {
if ($config = get_config('block_myoverview', 'layouts')) {
$this->layouts = explode(',', $config);
} else {
$this->layouts = array(BLOCK_MYOVERVIEW_VIEW_CARD);
}
}
/**
* Get the user preferences as an array to figure out what has been selected.
*
* @return array $preferences Array with the pref as key and value set to true
*/
public function get_preferences_as_booleans() {
$preferences = [];
$preferences[$this->sort] = true;
$preferences[$this->grouping] = true;
// Only use the user view/display preference if it is in available layouts.
if (in_array($this->view, $this->layouts)) {
$preferences[$this->view] = true;
} else {
$preferences[reset($this->layouts)] = true;
}
return $preferences;
}
/**
* Format a layout into an object for export as a Context variable to template.
*
* @param string $layoutname
*
* @return \stdClass $layout an object representation of a layout
* @throws \coding_exception
*/
public function format_layout_for_export($layoutname) {
$layout = new stdClass();
$layout->id = $layoutname;
$layout->name = get_string($layoutname, 'block_myoverview');
$layout->active = $this->view == $layoutname ? true : false;
$layout->arialabel = get_string('aria:' . $layoutname, 'block_myoverview');
return $layout;
}
/**
* Get the available layouts formatted for export.
*
* @return array an array of objects representing available layouts
*/
public function get_formatted_available_layouts_for_export() {
return array_map(array($this, 'format_layout_for_export'), $this->layouts);
}
/**
* Get the list of values to add to the grouping dropdown
* @return object[] containing name, value and active fields
*/
public function get_customfield_values_for_export() {
global $DB, $USER;
if (!$this->displaygroupingcustomfield) {
return [];
}
// Get the relevant customfield ID within the core_course/course component/area.
$fieldid = $DB->get_field_sql("
SELECT f.id
FROM {customfield_field} f
JOIN {customfield_category} c ON c.id = f.categoryid
WHERE f.shortname = :shortname AND c.component = 'core_course' AND c.area = 'course'
", ['shortname' => $this->customfiltergrouping]);
if (!$fieldid) {
return [];
}
$courses = enrol_get_all_users_courses($USER->id, true);
if (!$courses) {
return [];
}
list($csql, $params) = $DB->get_in_or_equal(array_keys($courses), SQL_PARAMS_NAMED);
$select = "instanceid $csql AND fieldid = :fieldid";
$params['fieldid'] = $fieldid;
$distinctablevalue = $DB->sql_compare_text('value');
$values = $DB->get_records_select_menu('customfield_data', $select, $params, '',
"DISTINCT $distinctablevalue, $distinctablevalue AS value2");
\core_collator::asort($values, \core_collator::SORT_NATURAL);
$values = array_filter($values);
if (!$values) {
return [];
}
$field = \core_customfield\field_controller::create($fieldid);
$isvisible = $field->get_configdata_property('visibility') == \core_course\customfield\course_handler::VISIBLETOALL;
// Only visible fields to everybody supporting course grouping will be displayed.
if (!$field->supports_course_grouping() || !$isvisible) {
return []; // The field shouldn't have been selectable in the global settings, but just skip it now.
}
$values = $field->course_grouping_format_values($values);
$customfieldactive = ($this->grouping === BLOCK_MYOVERVIEW_GROUPING_CUSTOMFIELD);
$ret = [];
foreach ($values as $value => $name) {
$ret[] = (object)[
'name' => $name,
'value' => $value,
'active' => ($customfieldactive && ($this->customfieldvalue == $value)),
];
}
return $ret;
}
/**
* Export this data so it can be used as the context for a mustache template.
*
* @param \renderer_base $output
* @return array Context variables for the template
* @throws \coding_exception
*
*/
public function export_for_template(renderer_base $output) {
global $CFG, $USER;
$nocoursesurl = $output->image_url('courses', 'block_myoverview')->out();
$newcourseurl = '';
$coursecat = \core_course_category::user_top();
if ($coursecat && ($category = \core_course_category::get_nearest_editable_subcategory($coursecat, ['create']))) {
$newcourseurl = new \moodle_url('/course/edit.php', ['category' => $category->id]);
}
$customfieldvalues = $this->get_customfield_values_for_export();
$selectedcustomfield = '';
if ($this->grouping == BLOCK_MYOVERVIEW_GROUPING_CUSTOMFIELD) {
foreach ($customfieldvalues as $field) {
if ($field->value == $this->customfieldvalue) {
$selectedcustomfield = $field->name;
break;
}
}
// If the selected custom field value has not been found (possibly because the field has
// been changed in the settings) find a suitable fallback.
if (!$selectedcustomfield) {
$this->grouping = $this->get_fallback_grouping(get_config('block_myoverview'));
if ($this->grouping == BLOCK_MYOVERVIEW_GROUPING_CUSTOMFIELD) {
// If the fallback grouping is still customfield, then select the first field.
$firstfield = reset($customfieldvalues);
if ($firstfield) {
$selectedcustomfield = $firstfield->name;
$this->customfieldvalue = $firstfield->value;
}
}
}
}
$preferences = $this->get_preferences_as_booleans();
$availablelayouts = $this->get_formatted_available_layouts_for_export();
$sort = '';
if ($this->sort == BLOCK_MYOVERVIEW_SORTING_SHORTNAME) {
$sort = 'shortname';
} else {
$sort = $this->sort == BLOCK_MYOVERVIEW_SORTING_TITLE ? 'fullname' : 'ul.timeaccess desc';
}
$defaultvariables = [
'totalcoursecount' => count(enrol_get_all_users_courses($USER->id, true)),
'nocoursesimg' => $nocoursesurl,
'newcourseurl' => $newcourseurl,
'grouping' => $this->grouping,
'sort' => $sort,
// If the user preference display option is not available, default to first available layout.
'view' => in_array($this->view, $this->layouts) ? $this->view : reset($this->layouts),
'paging' => $this->paging,
'layouts' => $availablelayouts,
'displaycategories' => $this->displaycategories,
'displaydropdown' => (count($availablelayouts) > 1) ? true : false,
'displaygroupingallincludinghidden' => $this->displaygroupingallincludinghidden,
'displaygroupingall' => $this->displaygroupingall,
'displaygroupinginprogress' => $this->displaygroupinginprogress,
'displaygroupingfuture' => $this->displaygroupingfuture,
'displaygroupingpast' => $this->displaygroupingpast,
'displaygroupingfavourites' => $this->displaygroupingfavourites,
'displaygroupinghidden' => $this->displaygroupinghidden,
'displaygroupingselector' => $this->displaygroupingselector,
'displaygroupingcustomfield' => $this->displaygroupingcustomfield && $customfieldvalues,
'customfieldname' => $this->customfiltergrouping,
'customfieldvalue' => $this->customfieldvalue,
'customfieldvalues' => $customfieldvalues,
'selectedcustomfield' => $selectedcustomfield,
'showsortbyshortname' => $CFG->courselistshortnames,
];
return array_merge($defaultvariables, $preferences);
}
/**
* Export this data so it can be used as the context for a mustache template.
*
* @param \renderer_base $output
* @return array Context variables for the template
* @throws \coding_exception
*
*/
public function export_for_zero_state_template(renderer_base $output) {
global $CFG, $DB;
$nocoursesimg = $output->image_url('courses', 'block_myoverview');
$coursecat = \core_course_category::user_top();
if ($coursecat) {
$category = \core_course_category::get_nearest_editable_subcategory($coursecat, ['moodle/course:request']);
if ($category && $category->can_request_course()) {
// Add Request a course button.
$button = new \single_button(
new \moodle_url('/course/request.php', ['category' => $category->id]),
get_string('requestcourse'),
'post',
\single_button::BUTTON_PRIMARY
);
return $this->generate_zero_state_data(
$nocoursesimg,
[$button->export_for_template($output)],
['title' => 'zero_request_title', 'intro' => 'zero_request_intro']
);
}
$totalcourses = $DB->count_records_select('course', 'category > 0');
if (!$totalcourses && ($category = \core_course_category::get_nearest_editable_subcategory($coursecat, ['create']))) {
// Add Quickstart guide and Create course buttons.
$quickstarturl = $CFG->coursecreationguide;
if ($quickstarturl) {
$quickstartbutton = new \single_button(
new \moodle_url($quickstarturl, ['lang' => current_language()]),
get_string('viewquickstart', 'block_myoverview'),
'get',
);
$buttons = [$quickstartbutton->export_for_template($output)];
}
$createbutton = new \single_button(
new \moodle_url('/course/edit.php', ['category' => $category->id]),
get_string('createcourse', 'block_myoverview'),
'post',
\single_button::BUTTON_PRIMARY
);
$buttons[] = $createbutton->export_for_template($output);
return $this->generate_zero_state_data(
$nocoursesimg,
$buttons,
['title' => 'zero_nocourses_title', 'intro' => 'zero_nocourses_intro']
);
}
if ($categorytocreate = \core_course_category::get_nearest_editable_subcategory($coursecat, ['create'])) {
$createbutton = new \single_button(
new \moodle_url('/course/edit.php', ['category' => $categorytocreate->id]),
get_string('createcourse', 'block_myoverview'),
'post',
\single_button::BUTTON_PRIMARY
);
$buttons = [$createbutton->export_for_template($output)];
if ($categorytomanage = \core_course_category::get_nearest_editable_subcategory($coursecat, ['manage'])) {
// Add a Manage course button.
$managebutton = new \single_button(
new \moodle_url('/course/management.php', ['category' => $categorytomanage->id]),
get_string('managecourses')
);
$buttons[] = $managebutton->export_for_template($output);
return $this->generate_zero_state_data(
$nocoursesimg,
array_reverse($buttons),
['title' => 'zero_default_title', 'intro' => 'zero_default_intro']
);
}
return $this->generate_zero_state_data(
$nocoursesimg,
$buttons,
['title' => 'zero_default_title', 'intro' => 'zero_default_intro']
);
}
}
return $this->generate_zero_state_data(
$nocoursesimg,
[],
['title' => 'zero_default_title', 'intro' => 'zero_default_intro']
);
}
/**
* Generate the state zero data.
*
* @param \moodle_url $imageurl The URL to the image to show
* @param string[] $buttons Exported {@see \single_button} instances
* @param array $strings Title and intro strings for the zero state if needed.
* @return array Context variables for the template
*/
private function generate_zero_state_data(\moodle_url $imageurl, array $buttons, array $strings) {
global $CFG;
// Documentation data.
$dochref = new \moodle_url($CFG->docroot, ['lang' => current_language()]);
$quickstart = new \moodle_url($CFG->coursecreationguide, ['lang' => current_language()]);
$docparams = [
'quickhref' => $quickstart->out(),
'quicktitle' => get_string('viewquickstart', 'block_myoverview'),
'quicktarget' => '_blank',
'dochref' => $dochref->out(),
'doctitle' => get_string('documentation'),
'doctarget' => $CFG->doctonewwindow ? '_blank' : '_self',
];
return [
'nocoursesimg' => $imageurl->out(),
'title' => ($strings['title']) ? get_string($strings['title'], 'block_myoverview') : '',
'intro' => ($strings['intro']) ? get_string($strings['intro'], 'block_myoverview', $docparams) : '',
'buttons' => $buttons,
];
}
}
@@ -0,0 +1,56 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* myoverview block rendrer
*
* @package block_myoverview
* @copyright 2016 Ryan Wyllie <ryan@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace block_myoverview\output;
defined('MOODLE_INTERNAL') || die;
use plugin_renderer_base;
use renderable;
/**
* myoverview block renderer
*
* @package block_myoverview
* @copyright 2016 Ryan Wyllie <ryan@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class renderer extends plugin_renderer_base {
/**
* Return the main content for the block overview.
*
* @param main $main The main renderable
* @return string HTML string
*/
public function render_main(main $main) {
global $USER;
if (!count(enrol_get_all_users_courses($USER->id, true))) {
return $this->render_from_template(
'block_myoverview/zero-state',
$main->export_for_zero_state_template($this)
);
}
return $this->render_from_template('block_myoverview/main', $main->export_for_template($this));
}
}
@@ -0,0 +1,108 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Privacy Subsystem implementation for block_myoverview.
*
* @package block_myoverview
* @copyright 2018 Zig Tan <zig@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
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();
/**
* Privacy Subsystem for block_myoverview.
*
* @copyright 2018 Zig Tan <zig@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class provider implements \core_privacy\local\metadata\provider, user_preference_provider {
/**
* Returns meta-data information about the myoverview block.
*
* @param \core_privacy\local\metadata\collection $collection A collection of meta-data.
* @return \core_privacy\local\metadata\collection Return the collection of meta-data.
*/
public static function get_metadata(collection $collection): collection {
$collection->add_user_preference('block_myoverview_user_sort_preference', 'privacy:metadata:overviewsortpreference');
$collection->add_user_preference('block_myoverview_user_view_preference', 'privacy:metadata:overviewviewpreference');
$collection->add_user_preference('block_myoverview_user_grouping_preference',
'privacy:metadata:overviewgroupingpreference');
$collection->add_user_preference('block_myoverview_user_paging_preference',
'privacy:metadata:overviewpagingpreference');
return $collection;
}
/**
* Export all user preferences for the myoverview block
*
* @param int $userid The userid of the user whose data is to be exported.
*/
public static function export_user_preferences(int $userid) {
$preference = get_user_preferences('block_myoverview_user_sort_preference', null, $userid);
if (isset($preference)) {
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)) {
writer::export_user_preference('block_myoverview',
'block_myoverview_user_view_preference',
get_string($preference, 'block_myoverview'),
get_string('privacy:metadata:overviewviewpreference', 'block_myoverview'));
}
$preference = get_user_preferences('block_myoverview_user_grouping_preference', null, $userid);
if (isset($preference)) {
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,
])
);
}
}
$preference = get_user_preferences('block_myoverview_user_paging_preference', null, $userid);
if (isset($preference)) {
\core_privacy\local\request\writer::export_user_preference('block_myoverview',
'block_myoverview_user_paging_preference',
$preference,
get_string('privacy:metadata:overviewpagingpreference', 'block_myoverview'));
}
}
}
+38
View File
@@ -0,0 +1,38 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Capabilities for the My overview block.
*
* @package block_myoverview
* @copyright Mark Nelson <markn@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
$capabilities = array(
'block/myoverview:myaddinstance' => array(
'captype' => 'write',
'contextlevel' => CONTEXT_SYSTEM,
'archetypes' => array(
'user' => CAP_ALLOW
),
'clonepermissionsfrom' => 'moodle/my:manageblocks'
)
);
+45
View File
@@ -0,0 +1,45 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* This file keeps track of upgrades to the myoverview block
*
* @since 3.8
* @package block_myoverview
* @copyright 2019 Jake Dallimore <jrhdallimore@gmail.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
/**
* Upgrade code for the MyOverview block.
*
* @param int $oldversion
*/
function xmldb_block_myoverview_upgrade($oldversion) {
// Automatically generated Moodle v4.1.0 release upgrade line.
// Put any upgrade step following this.
// Automatically generated Moodle v4.2.0 release upgrade line.
// Put any upgrade step following this.
// Automatically generated Moodle v4.3.0 release upgrade line.
// Put any upgrade step following this.
// Automatically generated Moodle v4.4.0 release upgrade line.
// Put any upgrade step following this.
return true;
}
@@ -0,0 +1,96 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Lang strings for the My overview block.
*
* @package block_myoverview
* @copyright Mark Nelson <markn@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
$string['allincludinghidden'] = 'All (including removed from view)';
$string['all'] = 'All';
$string['addtofavourites'] = 'Star this course';
$string['aria:addtofavourites'] = 'Star for';
$string['aria:allcoursesincludinghidden'] = 'Show all courses';
$string['aria:allcourses'] = 'Show all courses except courses removed from view';
$string['aria:card'] = 'Switch to card view';
$string['aria:controls'] = 'Course overview controls';
$string['aria:courseactions'] = 'Actions for course';
$string['aria:coursesummary'] = 'Course summary text:';
$string['aria:courseprogress'] = 'Course progress:';
$string['aria:customfield'] = 'Show {$a} courses';
$string['aria:displaydropdown'] = 'Display drop-down menu';
$string['aria:favourites'] = 'Show starred courses only';
$string['aria:future'] = 'Show future courses';
$string['aria:groupingdropdown'] = 'Grouping drop-down menu';
$string['aria:inprogress'] = 'Show courses in progress';
$string['aria:list'] = 'Switch to list view';
$string['aria:past'] = 'Show past courses';
$string['aria:removefromfavourites'] = 'Remove star for';
$string['aria:summary'] = 'Switch to summary view';
$string['aria:sortingdropdown'] = 'Sorting drop-down menu';
$string['availablegroupings'] = 'Available filters';
$string['availablegroupings_desc'] = 'Course filters which are available for selection by users. If none are selected, all courses will be displayed.';
$string['card'] = 'Card';
$string['cards'] = 'Cards';
$string['courseprogress'] = 'Course progress:';
$string['completepercent'] = '{$a}% complete';
$string['createcourse'] = 'Create course';
$string['customfield'] = 'Custom field';
$string['customfiltergrouping'] = 'Field to use';
$string['customfiltergrouping_nofields'] = 'This option requires a course custom field to be set up and visible to everyone.';
$string['displaycategories'] = 'Display categories';
$string['displaycategories_help'] = 'Display the course category on dashboard course items including cards, list items and summary items.';
$string['favourites'] = 'Starred';
$string['future'] = 'Future';
$string['inprogress'] = 'In progress';
$string['lastaccessed'] = 'Last accessed';
$string['layouts'] = 'Available layouts';
$string['layouts_help'] = 'Course overview layouts which are available for selection by users. If none are selected, the card layout will be used.';
$string['list'] = 'List';
$string['myoverview:myaddinstance'] = 'Add a new course overview block to Dashboard';
$string['nocustomvalue'] = 'No {$a}';
$string['past'] = 'Past';
$string['pluginname'] = 'Course overview';
$string['privacy:metadata:overviewsortpreference'] = 'The Course overview block sort preference.';
$string['privacy:metadata:overviewviewpreference'] = 'The Course overview block view preference.';
$string['privacy:metadata:overviewgroupingpreference'] = 'The Course overview block grouping preference.';
$string['privacy:metadata:overviewpagingpreference'] = 'The Course overview block paging preference.';
$string['removefromfavourites'] = 'Unstar this course';
$string['searchcourses'] = "Search courses";
$string['shortname'] = 'Short name';
$string['summary'] = 'Summary';
$string['title'] = 'Course name';
$string['aria:hidecourse'] = 'Remove {$a} from view';
$string['aria:showcourse'] = 'Restore {$a} to view';
$string['aria:hiddencourses'] = 'Show courses removed from view';
$string['hidden'] = 'Courses removed from view';
$string['hidecourse'] = 'Remove from view';
$string['hiddencourses'] = 'Removed from view';
$string['show'] = 'Restore to view';
$string['sortbytitle'] = 'Sort by course name';
$string['sortbylastaccessed'] = 'Sort by last accessed';
$string['sortbyshortname'] = 'Sort by short name';
$string['privacy:request:preference:set'] = 'The value of the setting \'{$a->name}\' was \'{$a->value}\'';
$string['viewquickstart'] = 'View Quickstart guide';
$string['zero_default_title'] = 'You\'re not enrolled in any course';
$string['zero_default_intro'] = 'Once you\'re enrolled in a course, it will appear here.';
$string['zero_request_title'] = 'Request your first course';
$string['zero_request_intro'] = 'Need help getting started? Check out the <a href="{$a->dochref}" title="{$a->doctitle}" target="{$a->doctarget}">Moodle documentation</a> or take your first steps with our <a href="{$a->quickhref}" title="{$a->quicktitle}" target="{$a->quicktarget}">Quickstart guide</a>.';
$string['zero_nocourses_title'] = 'Create your first course';
$string['zero_nocourses_intro'] = 'Need help getting started? Check out the <a href="{$a->dochref}" title="{$a->doctitle}" target="{$a->doctarget}">Moodle documentation</a> or take your first steps with our Quickstart guide.';
+165
View File
@@ -0,0 +1,165 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Library functions for overview.
*
* @package block_myoverview
* @copyright 2018 Peter Dias
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
/**
* Constants for the user preferences grouping options
*/
define('BLOCK_MYOVERVIEW_GROUPING_ALLINCLUDINGHIDDEN', 'allincludinghidden');
define('BLOCK_MYOVERVIEW_GROUPING_ALL', 'all');
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');
define('BLOCK_MYOVERVIEW_GROUPING_CUSTOMFIELD', 'customfield');
/**
* Allows selection of all courses without a value for the custom field.
*/
define('BLOCK_MYOVERVIEW_CUSTOMFIELD_EMPTY', -1);
/**
* Constants for the user preferences sorting options
* timeline
*/
define('BLOCK_MYOVERVIEW_SORTING_TITLE', 'title');
define('BLOCK_MYOVERVIEW_SORTING_LASTACCESSED', 'lastaccessed');
define('BLOCK_MYOVERVIEW_SORTING_SHORTNAME', 'shortname');
/**
* Constants for the user preferences view options
*/
define('BLOCK_MYOVERVIEW_VIEW_CARD', 'card');
define('BLOCK_MYOVERVIEW_VIEW_LIST', 'list');
define('BLOCK_MYOVERVIEW_VIEW_SUMMARY', 'summary');
/**
* Constants for the user paging preferences
*/
define('BLOCK_MYOVERVIEW_PAGING_12', 12);
define('BLOCK_MYOVERVIEW_PAGING_24', 24);
define('BLOCK_MYOVERVIEW_PAGING_48', 48);
define('BLOCK_MYOVERVIEW_PAGING_96', 96);
define('BLOCK_MYOVERVIEW_PAGING_ALL', 0);
/**
* Constants for the admin category display setting
*/
define('BLOCK_MYOVERVIEW_DISPLAY_CATEGORIES_ON', 'on');
define('BLOCK_MYOVERVIEW_DISPLAY_CATEGORIES_OFF', 'off');
/**
* Get the current user preferences that are available
*
* @uses core_user::is_current_user
*
* @return array[] Array representing current options along with defaults
*/
function block_myoverview_user_preferences(): array {
$preferences['block_myoverview_user_grouping_preference'] = array(
'null' => NULL_NOT_ALLOWED,
'default' => BLOCK_MYOVERVIEW_GROUPING_ALL,
'type' => PARAM_ALPHA,
'choices' => array(
BLOCK_MYOVERVIEW_GROUPING_ALLINCLUDINGHIDDEN,
BLOCK_MYOVERVIEW_GROUPING_ALL,
BLOCK_MYOVERVIEW_GROUPING_INPROGRESS,
BLOCK_MYOVERVIEW_GROUPING_FUTURE,
BLOCK_MYOVERVIEW_GROUPING_PAST,
BLOCK_MYOVERVIEW_GROUPING_FAVOURITES,
BLOCK_MYOVERVIEW_GROUPING_HIDDEN,
BLOCK_MYOVERVIEW_GROUPING_CUSTOMFIELD,
),
'permissioncallback' => [core_user::class, 'is_current_user'],
);
$preferences['block_myoverview_user_grouping_customfieldvalue_preference'] = [
'null' => NULL_ALLOWED,
'default' => null,
'type' => PARAM_RAW,
'permissioncallback' => [core_user::class, 'is_current_user'],
];
$preferences['block_myoverview_user_sort_preference'] = array(
'null' => NULL_NOT_ALLOWED,
'default' => BLOCK_MYOVERVIEW_SORTING_LASTACCESSED,
'type' => PARAM_ALPHA,
'choices' => array(
BLOCK_MYOVERVIEW_SORTING_TITLE,
BLOCK_MYOVERVIEW_SORTING_LASTACCESSED,
BLOCK_MYOVERVIEW_SORTING_SHORTNAME
),
'permissioncallback' => [core_user::class, 'is_current_user'],
);
$preferences['block_myoverview_user_view_preference'] = array(
'null' => NULL_NOT_ALLOWED,
'default' => BLOCK_MYOVERVIEW_VIEW_CARD,
'type' => PARAM_ALPHA,
'choices' => array(
BLOCK_MYOVERVIEW_VIEW_CARD,
BLOCK_MYOVERVIEW_VIEW_LIST,
BLOCK_MYOVERVIEW_VIEW_SUMMARY
),
'permissioncallback' => [core_user::class, 'is_current_user'],
);
$preferences['/^block_myoverview_hidden_course_(\d)+$/'] = array(
'isregex' => true,
'choices' => array(0, 1),
'type' => PARAM_INT,
'null' => NULL_NOT_ALLOWED,
'default' => 0,
'permissioncallback' => [core_user::class, 'is_current_user'],
);
$preferences['block_myoverview_user_paging_preference'] = array(
'null' => NULL_NOT_ALLOWED,
'default' => BLOCK_MYOVERVIEW_PAGING_12,
'type' => PARAM_INT,
'choices' => array(
BLOCK_MYOVERVIEW_PAGING_12,
BLOCK_MYOVERVIEW_PAGING_24,
BLOCK_MYOVERVIEW_PAGING_48,
BLOCK_MYOVERVIEW_PAGING_96,
BLOCK_MYOVERVIEW_PAGING_ALL
),
'permissioncallback' => [core_user::class, 'is_current_user'],
);
return $preferences;
}
/**
* Pre-delete course hook to cleanup any records with references to the deleted course.
*
* @param stdClass $course The deleted course
*/
function block_myoverview_pre_course_delete(\stdClass $course) {
// Removing any favourited courses which have been created for users, for this course.
$service = \core_favourites\service_factory::get_service_for_component('core_course');
$service->delete_favourites_by_type_and_item('courses', $course->id);
}
+52
View File
@@ -0,0 +1,52 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="157 -1305 148 125" preserveAspectRatio="xMinYMid meet">
<defs>
<style>
.cls-1 {
clip-path: url(#clip-Courses);
}
.cls-2 {
fill: #eee;
}
.cls-3 {
fill: #c4c8cc;
}
.cls-4 {
fill: #fff;
}
</style>
<clipPath id="clip-Courses">
<rect x="157" y="-1305" width="148" height="125"/>
</clipPath>
</defs>
<g id="Courses" class="cls-1">
<g id="Group_44" data-name="Group 44" transform="translate(-268 -1781)">
<ellipse id="Ellipse_41" data-name="Ellipse 41" class="cls-2" cx="74" cy="14.785" rx="74" ry="14.785" transform="translate(425 571.43)"/>
<rect id="Rectangle_87" data-name="Rectangle 87" class="cls-3" width="95.097" height="110.215" transform="translate(451.909 476)"/>
<g id="Group_43" data-name="Group 43" transform="translate(464.04 494)">
<rect id="Rectangle_88" data-name="Rectangle 88" class="cls-4" width="31.043" height="34" transform="translate(0)"/>
<rect id="Rectangle_89" data-name="Rectangle 89" class="cls-4" width="31.043" height="34" transform="translate(0 42)"/>
<rect id="Rectangle_90" data-name="Rectangle 90" class="cls-4" width="31.067" height="34" transform="translate(39.005)"/>
<rect id="Rectangle_91" data-name="Rectangle 91" class="cls-4" width="31.067" height="34" transform="translate(39.005 42)"/>
<rect id="Rectangle_92" data-name="Rectangle 92" class="cls-3" width="23.023" height="3.18" transform="translate(3.081 16.549)"/>
<rect id="Rectangle_93" data-name="Rectangle 93" class="cls-3" width="23.023" height="3.18" transform="translate(3.081 58.549)"/>
<rect id="Rectangle_94" data-name="Rectangle 94" class="cls-3" width="23.023" height="3.18" transform="translate(43.122 16.549)"/>
<rect id="Rectangle_95" data-name="Rectangle 95" class="cls-3" width="23.023" height="3.18" transform="translate(43.122 58.549)"/>
<rect id="Rectangle_96" data-name="Rectangle 96" class="cls-3" width="14.014" height="3.18" transform="translate(3.081 21.825)"/>
<rect id="Rectangle_97" data-name="Rectangle 97" class="cls-3" width="18.845" height="3.18" transform="translate(3.081 26.825)"/>
<rect id="Rectangle_98" data-name="Rectangle 98" class="cls-3" width="14.014" height="3.18" transform="translate(3.081 63.825)"/>
<rect id="Rectangle_99" data-name="Rectangle 99" class="cls-3" width="18.845" height="3.18" transform="translate(3.081 68.825)"/>
<rect id="Rectangle_100" data-name="Rectangle 100" class="cls-3" width="14.014" height="3.18" transform="translate(43.122 21.825)"/>
<rect id="Rectangle_101" data-name="Rectangle 101" class="cls-3" width="18.845" height="3.18" transform="translate(43.122 26.825)"/>
<rect id="Rectangle_102" data-name="Rectangle 102" class="cls-3" width="14.014" height="3.18" transform="translate(43.122 63.825)"/>
<rect id="Rectangle_103" data-name="Rectangle 103" class="cls-3" width="18.845" height="3.18" transform="translate(43.122 68.825)"/>
<ellipse id="Ellipse_42" data-name="Ellipse 42" class="cls-3" cx="5.658" cy="5.652" rx="5.658" ry="5.652" transform="translate(3.003 3.55)"/>
<ellipse id="Ellipse_43" data-name="Ellipse 43" class="cls-3" cx="5.658" cy="5.652" rx="5.658" ry="5.652" transform="translate(3.003 45.55)"/>
<ellipse id="Ellipse_44" data-name="Ellipse 44" class="cls-3" cx="5.658" cy="5.652" rx="5.658" ry="5.652" transform="translate(43.044 3.55)"/>
<ellipse id="Ellipse_45" data-name="Ellipse 45" class="cls-3" cx="5.658" cy="5.652" rx="5.658" ry="5.652" transform="translate(43.044 45.55)"/>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.7 KiB

+123
View File
@@ -0,0 +1,123 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Settings for the myoverview block
*
* @package block_myoverview
* @copyright 2019 Tom Dickman <tomdickman@catalyst-au.net>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die;
if ($ADMIN->fulltree) {
require_once($CFG->dirroot . '/blocks/myoverview/lib.php');
// Presentation options heading.
$settings->add(new admin_setting_heading('block_myoverview/appearance',
get_string('appearance', 'admin'),
''));
// Display Course Categories on Dashboard course items (cards, lists, summary items).
$settings->add(new admin_setting_configcheckbox(
'block_myoverview/displaycategories',
get_string('displaycategories', 'block_myoverview'),
get_string('displaycategories_help', 'block_myoverview'),
1));
// Enable / Disable available layouts.
$choices = array(BLOCK_MYOVERVIEW_VIEW_CARD => get_string('card', 'block_myoverview'),
BLOCK_MYOVERVIEW_VIEW_LIST => get_string('list', 'block_myoverview'),
BLOCK_MYOVERVIEW_VIEW_SUMMARY => get_string('summary', 'block_myoverview'));
$settings->add(new admin_setting_configmulticheckbox(
'block_myoverview/layouts',
get_string('layouts', 'block_myoverview'),
get_string('layouts_help', 'block_myoverview'),
$choices,
$choices));
unset ($choices);
// Enable / Disable course filter items.
$settings->add(new admin_setting_heading('block_myoverview/availablegroupings',
get_string('availablegroupings', 'block_myoverview'),
get_string('availablegroupings_desc', 'block_myoverview')));
$settings->add(new admin_setting_configcheckbox(
'block_myoverview/displaygroupingallincludinghidden',
get_string('allincludinghidden', 'block_myoverview'),
'',
0));
$settings->add(new admin_setting_configcheckbox(
'block_myoverview/displaygroupingall',
get_string('all', 'block_myoverview'),
'',
1));
$settings->add(new admin_setting_configcheckbox(
'block_myoverview/displaygroupinginprogress',
get_string('inprogress', 'block_myoverview'),
'',
1));
$settings->add(new admin_setting_configcheckbox(
'block_myoverview/displaygroupingpast',
get_string('past', 'block_myoverview'),
'',
1));
$settings->add(new admin_setting_configcheckbox(
'block_myoverview/displaygroupingfuture',
get_string('future', 'block_myoverview'),
'',
1));
$settings->add(new admin_setting_configcheckbox(
'block_myoverview/displaygroupingcustomfield',
get_string('customfield', 'block_myoverview'),
'',
0));
$choices = \core_customfield\api::get_fields_supporting_course_grouping();
if ($choices) {
$choices = ['' => get_string('choosedots')] + $choices;
$settings->add(new admin_setting_configselect(
'block_myoverview/customfiltergrouping',
get_string('customfiltergrouping', 'block_myoverview'),
'',
'',
$choices));
} else {
$settings->add(new admin_setting_configempty(
'block_myoverview/customfiltergrouping',
get_string('customfiltergrouping', 'block_myoverview'),
get_string('customfiltergrouping_nofields', 'block_myoverview')));
}
$settings->hide_if('block_myoverview/customfiltergrouping', 'block_myoverview/displaygroupingcustomfield');
$settings->add(new admin_setting_configcheckbox(
'block_myoverview/displaygroupingfavourites',
get_string('favourites', 'block_myoverview'),
'',
1));
$settings->add(new admin_setting_configcheckbox(
'block_myoverview/displaygroupinghidden',
get_string('hiddencourses', 'block_myoverview'),
'',
1));
}
+11
View File
@@ -0,0 +1,11 @@
/* Hide the first dropdown-divider if no filter option element is listed before it.
This can happen for some subset configurations of the block_myoverview course filter. */
.block_myoverview button#groupingdropdown + .dropdown-menu li:first-of-type.dropdown-divider:first-of-type {
display: none;
}
.block_myoverview .whitebutton .btn-secondary {
background: white;
border-color: var(--primary);
color: var(--primary);
}
@@ -0,0 +1,78 @@
{{!
This file is part of Moodle - http://moodle.org/
Moodle is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Moodle is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Moodle. If not, see <http://www.gnu.org/licenses/>.
}}
{{!
@template block_myoverview/course-action-menu
This template renders action menu for each course.
Example context (json):
{
"isfavourite": true
}
}}
<div class="ml-auto dropdown">
<button class="btn btn-link btn-icon icon-size-3 coursemenubtn"
type="button"
data-toggle="dropdown"
aria-haspopup="true"
aria-expanded="false">
<i class="icon fa fa-ellipsis-v fa-fw m-0" title="{{#str}} aria:courseactions, block_myoverview {{/str}} {{{fullname}}}" aria-hidden="true"></i>
<span class="sr-only">{{#str}} aria:courseactions, block_myoverview {{/str}} {{{fullname}}}</span>
</button>
<div class="dropdown-menu dropdown-menu-right">
<a class="dropdown-item {{#isfavourite}}hidden{{/isfavourite}}" href="#"
data-action="add-favourite"
data-course-id="{{id}}"
aria-controls="favorite-icon-{{ id }}-{{ uniqid }}"
>
{{#str}} addtofavourites, block_myoverview {{/str}}
<div class="sr-only">
{{#str}} aria:addtofavourites, block_myoverview {{/str}} {{{fullname}}}
</div>
</a>
<a class="dropdown-item {{^isfavourite}}hidden{{/isfavourite}}" href="#"
data-action="remove-favourite"
data-course-id="{{id}}"
aria-controls="favorite-icon-{{ id }}-{{ uniqid }}"
>
{{#str}} removefromfavourites, block_myoverview {{/str}}
<div class="sr-only">
{{#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 }}-{{ uniqid }}"
>
{{#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 }}-{{ uniqid }}"
>
{{#str}} hidecourse, block_myoverview {{/str}}
<div class="sr-only">
{{#str}} aria:hidecourse, block_myoverview, {{fullname}} {{/str}}
</div>
</a>
</div>
</div>
@@ -0,0 +1,47 @@
{{!
This file is part of Moodle - http://moodle.org/
Moodle is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Moodle is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Moodle. If not, see <http://www.gnu.org/licenses/>.
}}
{{!
@template block_myoverview/courses-view
This template renders the courses view for the myoverview block.
Example context (json):
{
"nocoursesimg": "https://moodlesite/theme/image.php/boost/block_myoverview/1535727318/courses",
"newcourseurl": "https://moodlesite/course/edit.php",
"grouping": "all",
"sort": "fullname",
"view": "card"
}
}}
<div id="courses-view-{{uniqid}}"
data-region="courses-view"
data-display="{{view}}"
data-grouping="{{grouping}}"
data-customfieldname="{{customfieldname}}"
data-customfieldvalue="{{customfieldvalue}}"
data-sort="{{sort}}"
data-prev-display="{{view}}"
data-paging="{{paging}}"
data-nocoursesimg="{{nocoursesimg}}"
data-totalcoursecount="{{totalcoursecount}}"
data-displaycategories="{{displaycategories}}"
data-newcourseurl="{{newcourseurl}}">
<div data-region="course-view-content">
{{> block_myoverview/placeholders }}
</div>
</div>
+60
View File
@@ -0,0 +1,60 @@
{{!
This file is part of Moodle - http://moodle.org/
Moodle is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Moodle is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Moodle. If not, see <http://www.gnu.org/licenses/>.
}}
{{!
@template block_myoverview/main
This template renders the main content area for the myoverview block.
Example context (json):
{}
}}
<div id="block-myoverview-{{uniqid}}" class="block-myoverview block-cards" data-region="myoverview" role="navigation">
<hr class="mt-0"/>
<div role="search" data-region="filter" class="d-flex align-items-center my-2" aria-label="{{#str}} aria:controls, block_myoverview {{/str}}">
<div class="row no-gutters">
{{> block_myoverview/nav-grouping-selector }}
{{> block_myoverview/nav-search-widget }}
{{> block_myoverview/nav-sort-selector }}
{{#displaydropdown}}
{{> block_myoverview/nav-display-selector }}
{{/displaydropdown}}
</div>
</div>
<div class="container-fluid p-0">
{{> block_myoverview/courses-view }}
</div>
</div>
{{#js}}
require(
[
'jquery',
'block_myoverview/main',
],
function(
$,
Main
) {
var root = $('#block-myoverview-{{uniqid}}');
Main.init(root);
});
{{/js}}
@@ -0,0 +1,47 @@
{{!
This file is part of Moodle - http://moodle.org/
Moodle is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Moodle is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Moodle. If not, see <http://www.gnu.org/licenses/>.
}}
{{!
@template block_myoverview/nav-display-selector
This template renders display dropdown.
Example context (json):
{
"cards": true,
"list": false,
"summary": false
}
}}
<div class="dropdown mb-1">
<button id="displaydropdown" type="button" class="btn btn-outline-secondary dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"
aria-label="{{#str}} aria:displaydropdown, block_myoverview {{/str}}">
<span data-active-item-text>
{{#card}}{{#str}} card, block_myoverview {{/str}}{{/card}}
{{#list}}{{#str}} list, block_myoverview {{/str}}{{/list}}
{{#summary}}{{#str}} summary, block_myoverview {{/str}}{{/summary}}
</span>
</button>
<ul class="dropdown-menu" role="menu" data-show-active-item data-skip-active-class="true" aria-labelledby="displaydropdown">
{{#layouts}}
<li>
<a class="dropdown-item" href="#" data-display-option="display" data-value="{{id}}" data-pref="{{id}}" aria-label="{{arialabel}}" aria-controls="courses-view-{{uniqid}}" role="menuitem" {{#active}}aria-current="true"{{/active}}>
{{name}}
</a>
</li>
{{/layouts}}
</ul>
</div>
@@ -0,0 +1,150 @@
{{!
This file is part of Moodle - http://moodle.org/
Moodle is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Moodle is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Moodle. If not, see <http://www.gnu.org/licenses/>.
}}
{{!
@template block_myoverview/nav-grouping-selector
This template renders grouping dropdown.
Example context (json):
{
"allincludinghidden": false,
"all": true,
"inprogress": false,
"future": false,
"past": false,
"favourites": false,
"hidden": false,
"displaygroupingallincludinghidden": false,
"displaygroupingall": true,
"displaygroupinginprogress": true,
"displaygroupingfuture": true,
"displaygroupingpast": true,
"displaygroupingfavourites": true,
"displaygroupinghidden": true,
"displaygroupingselector": true
}
}}
{{#displaygroupingselector}}
<div class="dropdown mb-1 mr-1">
<button id="groupingdropdown" type="button" class="btn btn-outline-secondary dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" aria-label="{{#str}} aria:groupingdropdown, block_myoverview {{/str}}">
<span data-active-item-text>
{{#allincludinghidden}}{{#str}} allincludinghidden, block_myoverview {{/str}}{{/allincludinghidden}}
{{#all}}{{#str}} all, block_myoverview {{/str}}{{/all}}
{{#inprogress}}{{#str}} inprogress, block_myoverview {{/str}}{{/inprogress}}
{{#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}}
{{selectedcustomfield}}
</span>
</button>
<ul class="dropdown-menu" role="menu" data-show-active-item data-skip-active-class="true" data-active-item-text aria-labelledby="groupingdropdown">
{{#displaygroupingallincludinghidden}}
<li>
<a class="dropdown-item" href="#" data-filter="grouping" data-value="allincludinghidden" data-pref="allincludinghidden" aria-label="{{#str}} aria:allcoursesincludinghidden, block_myoverview {{/str}}" aria-controls="courses-view-{{uniqid}}" role="menuitem" {{#allincludinghidden}}aria-current="true"{{/allincludinghidden}}>
{{#str}} allincludinghidden, block_myoverview {{/str}}
</a>
</li>
{{/displaygroupingallincludinghidden}}
{{#displaygroupingall}}
<li class="dropdown-divider" role="presentation">
<span class="filler">&nbsp;</span>
</li>
<li>
<a class="dropdown-item" href="#" data-filter="grouping" data-value="all" data-pref="all" aria-label="{{#str}} aria:allcourses, block_myoverview {{/str}}" aria-controls="courses-view-{{uniqid}}" role="menuitem" {{#all}}aria-current="true"{{/all}}>
{{#str}} all, block_myoverview {{/str}}
</a>
</li>
{{/displaygroupingall}}
{{#displaygroupinginprogress}}
<li class="dropdown-divider" role="presentation">
<span class="filler">&nbsp;</span>
</li>
<li>
<a class="dropdown-item" href="#" data-filter="grouping" data-value="inprogress" data-pref="inprogress" aria-label="{{#str}} aria:inprogress, block_myoverview {{/str}}" aria-controls="courses-view-{{uniqid}}" role="menuitem" {{#inprogress}}aria-current="true"{{/inprogress}}>
{{#str}} inprogress, block_myoverview {{/str}}
</a>
</li>
{{/displaygroupinginprogress}}
{{#displaygroupingfuture}}
{{^displaygroupinginprogress}}
<li class="dropdown-divider" role="presentation">
<span class="filler">&nbsp;</span>
</li>
{{/displaygroupinginprogress}}
<li>
<a class="dropdown-item" href="#" data-filter="grouping" data-value="future" data-pref="future" aria-label="{{#str}} aria:future, block_myoverview {{/str}}" aria-controls="courses-view-{{uniqid}}" role="menuitem" {{#future}}aria-current="true"{{/future}}>
{{#str}} future, block_myoverview {{/str}}
</a>
</li>
{{/displaygroupingfuture}}
{{#displaygroupingpast}}
{{^displaygroupinginprogress}}
{{^displaygroupingfuture}}
<li class="dropdown-divider" role="presentation">
<span class="filler">&nbsp;</span>
</li>
{{/displaygroupingfuture}}
{{/displaygroupinginprogress}}
<li>
<a class="dropdown-item" href="#" data-filter="grouping" data-value="past" data-pref="past" aria-label="{{#str}} aria:past, block_myoverview {{/str}}" aria-controls="courses-view-{{uniqid}}" role="menuitem" {{#past}}aria-current="true"{{/past}}>
{{#str}} past, block_myoverview {{/str}}
</a>
</li>
{{/displaygroupingpast}}
{{#displaygroupingcustomfield}}
<li class="dropdown-divider" role="presentation">
<span class="filler">&nbsp;</span>
</li>
{{#customfieldvalues}}
<li>
<a class="dropdown-item" href="#" data-filter="grouping"
data-value="customfield" data-pref="customfield" data-customfieldvalue="{{value}}"
aria-label="{{#str}}aria:customfield, block_myoverview, {{name}}{{/str}}"
aria-controls="courses-view-{{uniqid}}" role="menuitem" {{#active}}aria-current="true"{{/active}}>
{{name}}
</a>
</li>
{{/customfieldvalues}}
{{/displaygroupingcustomfield}}
{{#displaygroupingfavourites}}
<li class="dropdown-divider" role="presentation">
<span class="filler">&nbsp;</span>
</li>
<li>
<a class="dropdown-item" href="#" data-filter="grouping" data-value="favourites" data-pref="favourites" aria-label="{{#str}} aria:favourites, block_myoverview {{/str}}" aria-controls="courses-view-{{uniqid}}" role="menuitem" {{#favourites}}aria-current="true"{{/favourites}}>
{{#str}} favourites, block_myoverview {{/str}}
</a>
{{/displaygroupingfavourites}}
{{#displaygroupinghidden}}
<li class="dropdown-divider" role="presentation">
<span class="filler">&nbsp;</span>
</li>
<li>
<a class="dropdown-item" href="#" data-filter="grouping" data-value="hidden" data-pref="hidden" aria-label="{{#str}} aria:hiddencourses, block_myoverview {{/str}}" aria-controls="courses-view-{{uniqid}}" role="menuitem" {{#hidden}}aria-current="true"{{/hidden}}>
{{#str}} hiddencourses, block_myoverview {{/str}}
</a>
</li>
{{/displaygroupinghidden}}
</ul>
</div>
{{/displaygroupingselector}}
{{^displaygroupingselector}}
<div class="mb-1 mr-auto">
<span class="filler">&nbsp;</span>
</div>
{{/displaygroupingselector}}
@@ -0,0 +1,34 @@
{{!
This file is part of Moodle - http://moodle.org/
Moodle is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Moodle is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Moodle. If not, see <http://www.gnu.org/licenses/>.
}}
{{!
@template block_myoverview/nav-search-widget
This template renders the search input within the filters area.
Example context (json):
{}
}}
<div class="mb-1 mr-1 flex-grow-1">
{{< core/search_input_auto }}
{{$label}}{{#str}}
searchcourses, block_myoverview
{{/str}}{{/label}}
{{$placeholder}}{{#str}}
search, core
{{/str}}{{/placeholder}}
{{/ core/search_input_auto }}
</div>
@@ -0,0 +1,58 @@
{{!
This file is part of Moodle - http://moodle.org/
Moodle is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Moodle is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Moodle. If not, see <http://www.gnu.org/licenses/>.
}}
{{!
@template block_myoverview/nav-sort-selector
This template renders sorting dropdown.
Example context (json):
{
"title": false,
"lastaccessed": true
}
}}
<div class="mb-1 mr-1 d-flex flex-wrap align-items-center">
<div class="dropdown">
<button id="sortingdropdown" type="button" class="btn btn-outline-secondary dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" aria-label="{{#str}} aria:sortingdropdown, block_myoverview {{/str}}">
<span data-active-item-text>
{{#title}}{{#str}} sortbytitle, block_myoverview {{/str}}{{/title}}
{{#lastaccessed}}{{#str}} sortbylastaccessed, block_myoverview {{/str}}{{/lastaccessed}}
{{#shortname}}{{#str}} sortbyshortname, block_myoverview {{/str}}{{/shortname}}
</span>
</button>
<ul class="dropdown-menu" role="menu" data-show-active-item data-skip-active-class="true" aria-labelledby="sortingdropdown">
<li>
<a class="dropdown-item" href="#" data-filter="sort" data-pref="title" data-value="fullname" aria-controls="courses-view-{{uniqid}}" role="menuitem" {{#title}}aria-current="true"{{/title}}>
{{#str}} sortbytitle, block_myoverview {{/str}}
</a>
</li>
{{#showsortbyshortname}}
<li>
<a class="dropdown-item" href="#" data-filter="sort" data-pref="shortname" data-value="shortname" aria-controls="courses-view-{{uniqid}}" role="menuitem" {{#shortname}}aria-current="true"{{/shortname}}>
{{#str}} sortbyshortname, block_myoverview {{/str}}
</a>
</li>
{{/showsortbyshortname}}
<li>
<a class="dropdown-item" href="#" data-filter="sort" data-pref="lastaccessed" data-value="ul.timeaccess desc" aria-controls="courses-view-{{uniqid}}" role="menuitem" {{#lastaccessed}}aria-current="true"{{/lastaccessed}}>
{{#str}} sortbylastaccessed, block_myoverview {{/str}}
</a>
</li>
</ul>
</div>
</div>
@@ -0,0 +1,34 @@
{{!
This file is part of Moodle - http://moodle.org/
Moodle is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Moodle is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Moodle. If not, see <http://www.gnu.org/licenses/>.
}}
{{!
@template block_myoverview/placeholder-course-list-item
This template renders the loading placeholder for the list & summary course items.
Example context (json):
{}
}}
<li class="list-group-item course-listitem border-left-0 border-right-0 border-top-0 px-2 rounded-0">
<div class="row">
<div class="col-md-2 d-flex align-items-center">
<div class="bg-pulse-grey card-img w-100" style="height: 1rem; width: 1rem;"></div>
</div>
<div class="col-md-10 d-flex flex-column">
<div class="bg-pulse-grey w-50" style="height: 1rem; margin: 0.5rem 0"></div>
</div>
</div>
</li>
@@ -0,0 +1,49 @@
{{!
This file is part of Moodle - http://moodle.org/
Moodle is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Moodle is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Moodle. If not, see <http://www.gnu.org/licenses/>.
}}
{{!
@template block_myoverview/placeholders
This template renders the loading placeholders for the myoverview block.
Example context (json):
{}
}}
<div data-region="loading-placeholder-content" aria-hidden="true">
{{#card}}
<div class="card-grid row row-cols-1 row-cols-sm-2 row-cols-md-3 mx-0 flex-nowrap overflow-hidden" style="height: 13rem">
<div class="col d-flex px-1">{{> core_course/placeholder-course }}</div>
<div class="col d-flex px-1">{{> core_course/placeholder-course }}</div>
<div class="col d-flex px-1">{{> core_course/placeholder-course }}</div>
</div>
{{/card}}
{{#list}}
<ul class="list-group">
{{> block_myoverview/placeholder-course-list-item }}
{{> block_myoverview/placeholder-course-list-item }}
{{> block_myoverview/placeholder-course-list-item }}
{{> block_myoverview/placeholder-course-list-item }}
</ul>
{{/list}}
{{#summary}}
<ul class="list-group">
{{> block_myoverview/placeholder-course-list-item }}
{{> block_myoverview/placeholder-course-list-item }}
{{> block_myoverview/placeholder-course-list-item }}
{{> block_myoverview/placeholder-course-list-item }}
</ul>
{{/summary}}
</div>
@@ -0,0 +1,30 @@
{{!
This file is part of Moodle - http://moodle.org/
Moodle is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Moodle is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Moodle. If not, see <http://www.gnu.org/licenses/>.
}}
{{!
@template block_myoverview/progress-bar
This template renders a simple progress bar.
Example context (json):
{
"progress": 50
}
}}
<div class="progress-text">
<span class="sr-only">{{#str}}aria:courseprogress, block_myoverview{{/str}}</span>
{{#str}}completepercent, block_myoverview, <span>{{progress}}</span>{{/str}}
</div>
@@ -0,0 +1,75 @@
{{!
This file is part of Moodle - http://moodle.org/
Moodle is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Moodle is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Moodle. If not, see <http://www.gnu.org/licenses/>.
}}
{{!
@template block_myoverview/view-cards
This template renders the cards view for the myoverview block.
Example context (json):
{
"courses": [
{
"name": "Assignment due 1",
"viewurl": "https://moodlesite/course/view.php?id=2",
"courseimage": "https://moodlesite/pluginfile/123/course/overviewfiles/123.jpg",
"fullname": "Course 3 for \"Statistical and Computational Tools\" ",
"hasprogress": true,
"progress": 10,
"coursecategory": "Category 1",
"visible": true
}
]
}
}}
{{< core_course/coursecards }}
{{$menu}}
<div class="card-footer menu border-0 bg-white ml-auto">
{{> block_myoverview/course-action-menu }}
</div>
{{/menu}}
{{$progress}}
{{#hasprogress}}
<div class="card-footer border-0 bg-white w-100">
{{> block_myoverview/progress-bar}}
</div>
{{/hasprogress}}
{{/progress}}
{{$coursename}}
<span class="multiline" title="{{fullname}}">
<span class="sr-only">{{{fullname}}}</span>
<span aria-hidden="true">
{{#shortentext}}55, {{{fullname}}} {{/shortentext}}
</span>
</span>
{{/coursename}}
{{$coursecategory}}
{{#showcoursecategory}}
<span class="sr-only">
{{#str}}aria:coursecategory, core_course{{/str}}
</span>
<span class="categoryname text-truncate">
{{{coursecategory}}}
</span>
{{/showcoursecategory}}
{{/coursecategory}}
{{$divider}}
{{#showcoursecategory}}
<div class="pl-1 pr-1">|</div>
{{/showcoursecategory}}
{{/divider}}
{{/ core_course/coursecards }}
@@ -0,0 +1,98 @@
{{!
This file is part of Moodle - http://moodle.org/
Moodle is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Moodle is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Moodle. If not, see <http://www.gnu.org/licenses/>.
}}
{{!
@template block_myoverview/view-list
This template renders the list view for the myoverview block.
Example context (json):
{
"courses": [
{
"name": "Assignment due 1",
"viewurl": "https://moodlesite/course/view.php?id=2",
"courseimage": "https://moodlesite/pluginfile/123/course/overviewfiles/123.jpg",
"fullname": "course 3",
"hasprogress": true,
"progress": 10,
"coursecategory": "Category 1",
"visible": true
}
]
}
}}
<ul class="list-group">
{{#courses}}
<li class="list-group-item course-listitem border-left-0 border-right-0 border-top-0 px-2 rounded-0"
data-region="course-content"
data-course-id="{{{id}}}">
<div class="row">
<div class="col-md-2 d-flex align-items-center mb-sm-3 mb-md-0">
<a href="{{viewurl}}" tabindex="-1" class="mw-100 w-100">
<div class="rounded list-image mw-100" style='background-image: url("{{{courseimage}}}");'>
<span class="sr-only">{{fullname}}</span>
</div>
</a>
</div>
<div class="col-md-9 d-flex flex-column">
{{#showshortname}}
<div class="text-muted muted d-flex flex-wrap">
{{#showcoursecategory}}
<div class="pl-1 pr-1">|</div>
{{/showcoursecategory}}
<span class="sr-only">
{{#str}}aria:courseshortname, core_course{{/str}}
</span>
<div>{{{shortname}}}</div>
</div>
{{/showshortname}}
<a href="{{viewurl}}" class="aalink coursename">
{{> core_course/favouriteicon }}
<span class="sr-only">
{{#str}}aria:coursename, core_course{{/str}}
</span>
{{{fullname}}}
</a>
{{#showcoursecategory}}
<div class="text-muted muted d-flex flex-wrap">
<span class="sr-only">
{{#str}}aria:coursecategory, core_course{{/str}}
</span>
<span class="categoryname">
{{{coursecategory}}}
</span>
</div>
{{/showcoursecategory}}
{{^visible}}
<div class="d-flex flex-wrap">
<span class="badge bg-info text-white">{{#str}} hiddenfromstudents {{/str}}</span>
</div>
{{/visible}}
{{#hasprogress}}
<div class="text-muted muted d-flex flex-wrap mt-auto">
{{> block_myoverview/progress-bar}}
</div>
{{/hasprogress}}
</div>
<div class="col-md-1 p-0 d-flex menu">
{{> block_myoverview/course-action-menu }}
</div>
</div>
</li>
{{/courses}}
</ul>
@@ -0,0 +1,104 @@
{{!
This file is part of Moodle - http://moodle.org/
Moodle is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Moodle is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Moodle. If not, see <http://www.gnu.org/licenses/>.
}}
{{!
@template block_myoverview/view-summary
This template renders the list view for the myoverview block.
Example context (json):
{
"courses": [
{
"name": "Assignment due 1",
"viewurl": "https://moodlesite/course/view.php?id=2",
"courseimage": "https://moodlesite/pluginfile/123/course/overviewfiles/123.jpg",
"fullname": "course 3",
"summary": "This course is about assignments",
"hasprogress": true,
"progress": 10,
"coursecategory": "Category 1",
"visible": true
}
]
}
}}
<div role="list">
{{#courses}}
<div class="course-summaryitem list-group-item course-listitem border-left-0 border-right-0 border-top-0 px-2 rounded-0" role="listitem"
data-region="course-content"
data-course-id="{{{id}}}">
<div class="row">
<div class="col-md-2 d-flex align-items-center mb-sm-3 mb-md-0">
<a href="{{viewurl}}" tabindex="-1" class="mw-100 w-100">
<div class="summary-image rounded mw-100" style='background-image: url("{{{courseimage}}}");'>
<span class="sr-only">{{fullname}}</span>
</div>
</a>
</div>
<div class="col-md-9 d-flex flex-column">
{{#showshortname}}
<div class="text-muted muted d-flex flex-wrap">
{{#showcoursecategory}}
<div class="pl-1 pr-1">|</div>
{{/showcoursecategory}}
<span class="sr-only">
{{#str}}aria:courseshortname, core_course{{/str}}
</span>
<div>{{{shortname}}}</div>
</div>
{{/showshortname}}
<a href="{{viewurl}}" class="aalink coursename">
{{> core_course/favouriteicon }}
<span class="sr-only">
{{#str}}aria:coursename, core_course{{/str}}
</span>
{{{fullname}}}
</a>
{{$coursecategory}}
<span class="sr-only">
{{#str}}aria:coursecategory, core_course{{/str}}
</span>
{{#showcoursecategory}}
<span class="categoryname">
{{#coursecategory}}
{{{coursecategory}}}
{{/coursecategory}}
</span>
{{/showcoursecategory}}
{{/coursecategory}}
{{^visible}}
<div class="d-flex flex-wrap">
<span class="badge bg-info text-white">{{#str}} hiddenfromstudents {{/str}}</span>
</div>
{{/visible}}
<div class="summary">
<span class="sr-only">{{#str}}aria:coursesummary, block_myoverview{{/str}}</span>
{{{summary}}}
</div>
{{#hasprogress}}
<div class="text-muted muted d-flex flex-wrap mt-auto">
{{> block_myoverview/progress-bar}}
</div>
{{/hasprogress}}
</div>
<div class="col-md-1 p-0 d-flex menu">
{{> block_myoverview/course-action-menu }}
</div>
</div>
</div>
{{/courses}}
</div>
@@ -0,0 +1,66 @@
{{!
This file is part of Moodle - http://moodle.org/
Moodle is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Moodle is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Moodle. If not, see <http://www.gnu.org/licenses/>.
}}
{{!
@template block_myoverview/zero-state
This template renders the main content area when there is no course to show.
Example context (json):
{
"nocoursesimg": "https://moodle.org/img/nocourses.svg",
"title": "No courses",
"text": "Moodle community",
"intro": "Come back later",
"buttons": [
{
"id": "buttons1",
"method": "get",
"url": "#",
"primary": true,
"label": "Button1"
},
{
"id": "buttons2",
"method": "get",
"url": "#",
"primary": false,
"label": "Button2"
}
]
}
}}
<div class="block-myoverview block-cards" data-region="myoverview" role="navigation">
<hr class="mt-0"/>
<div class="container-fluid p-0">
<div class="text-xs-center text-center mt-3" data-region="empty-message">
<img class="empty-placeholder-image-lg mt-1"
src="{{nocoursesimg}}"
alt=""
>
<h5 class="h5 mt-3 mb-0">{{{ title }}}</h5>
<p class="mt-3 mb-0">{{{ intro }}}</p>
<div class="mt-5 mb-0 whitebutton" id="action_bar">
{{#buttons}}
{{>core/single_button}}
{{/buttons}}
</div>
</div>
</div>
</div>
@@ -0,0 +1,210 @@
@block @block_myoverview @javascript
Feature: The my overview block allows admins to easily configure the students' course list
In order to adapt the my overview block to my users' needs
As an admin
I can configure the appearance of the my overview block
Background:
Given the following "users" exist:
| username | firstname | lastname | email | idnumber |
| student1 | Student | X | student1@example.com | S1 |
And the following "categories" exist:
| name | category | idnumber |
| Category 1 | 0 | CAT1 |
And the following "courses" exist:
| fullname | shortname | category | startdate | enddate |
| Course 1 | C1 | 0 | ##1 month ago## | ##15 days ago## |
| Course 2 | C2 | 0 | ##yesterday## | ##tomorrow## |
| Course 3 | C3 | 0 | ##yesterday## | ##tomorrow## |
| Course 4 | C4 | CAT1 | ##yesterday## | ##tomorrow## |
| Course 5 | C5 | 0 | ##first day of next month## | ##last day of next month## |
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: Enable 'All (including removed from view)' course filter option
Given I log in as "admin"
And I navigate to "Plugins > Blocks > Course overview" in site administration
And I set the field "All (including removed from view)" to "1"
And I press "Save"
And I log out
Then I am on the "My courses" page logged in as "student1"
And I click on "All" "button" in the "Course overview" "block"
# We have to check for the data attribute instead of the list element text as we would get false positives from the "All" element otherwise
Then "All (including removed from view)" "list_item" should exist in the ".block_myoverview .dropdown-menu" "css_element"
Scenario: Disable 'All (including removed from view)' course filter option
Given I log in as "admin"
And I navigate to "Plugins > Blocks > Course overview" in site administration
And I set the field "All (including removed from view)" to "0"
And I press "Save"
And I log out
Then I am on the "My courses" page logged in as "student1"
And I click on "All" "button" in the "Course overview" "block"
Then "All (including removed from view)" "list_item" should not exist in the ".block_myoverview .dropdown-menu" "css_element"
Scenario: Enable 'All' course filter option
Given I log in as "admin"
And I navigate to "Plugins > Blocks > Course overview" in site administration
And I set the field "All" in the "//*[@id=\"admin-displaygroupingall\"]" "xpath_element" to "1"
And I press "Save"
And I log out
Then I am on the "My courses" page logged in as "student1"
And I click on "All" "button" in the "Course overview" "block"
Then "[data-value='all']" "css_element" should exist in the ".block_myoverview .dropdown-menu" "css_element"
Scenario: Disable 'All' course filter option
Given I log in as "admin"
And I navigate to "Plugins > Blocks > Course overview" in site administration
And I set the field "All" in the "//*[@id=\"admin-displaygroupingall\"]" "xpath_element" to "0"
And I press "Save"
And I log out
Then I am on the "My courses" page logged in as "student1"
# 'All' option has been disabled, so the button is falling back to the 'In progress' option which is the next enabled option.
And I click on "In progress" "button" in the "Course overview" "block"
Then "[data-value='all']" "css_element" should not exist in the ".block_myoverview .dropdown-menu" "css_element"
Scenario: Enable 'In progress' course filter option
Given I log in as "admin"
And I navigate to "Plugins > Blocks > Course overview" in site administration
And I set the field "In progress" to "1"
And I press "Save"
And I log out
Then I am on the "My courses" page logged in as "student1"
And I click on "All" "button" in the "Course overview" "block"
# We have to check for the data attribute instead of the list element text as we would get false negatives "All (including removed from view)" element otherwise
Then "In progress" "list_item" should exist in the ".block_myoverview .dropdown-menu" "css_element"
Scenario: Disable 'In progress' course filter option
Given I log in as "admin"
And I navigate to "Plugins > Blocks > Course overview" in site administration
And I set the field "In progress" to "0"
And I press "Save"
And I log out
Then I am on the "My courses" page logged in as "student1"
And I click on "All" "button" in the "Course overview" "block"
Then "In progress" "list_item" should not exist in the ".block_myoverview .dropdown-menu" "css_element"
Scenario: Enable 'Future' course filter option
Given I log in as "admin"
And I navigate to "Plugins > Blocks > Course overview" in site administration
And I set the field "Future" to "1"
And I press "Save"
And I log out
Then I am on the "My courses" page logged in as "student1"
And I click on "All" "button" in the "Course overview" "block"
Then "Future" "list_item" should exist in the ".block_myoverview .dropdown-menu" "css_element"
Scenario: Disable 'Future' course filter option
Given I log in as "admin"
And I navigate to "Plugins > Blocks > Course overview" in site administration
And I set the field "Future" to "0"
And I press "Save"
And I log out
Then I am on the "My courses" page logged in as "student1"
And I click on "All" "button" in the "Course overview" "block"
Then "Future" "list_item" should not exist in the ".block_myoverview .dropdown-menu" "css_element"
Scenario: Enable 'Past' course filter option
Given I log in as "admin"
And I navigate to "Plugins > Blocks > Course overview" in site administration
And I set the field "Past" to "1"
And I press "Save"
And I log out
Then I am on the "My courses" page logged in as "student1"
And I click on "All" "button" in the "Course overview" "block"
Then "Past" "list_item" should exist in the ".block_myoverview .dropdown-menu" "css_element"
Scenario: Disable 'Past' course filter option
Given I log in as "admin"
And I navigate to "Plugins > Blocks > Course overview" in site administration
And I set the field "Past" to "0"
And I press "Save"
And I log out
Then I am on the "My courses" page logged in as "student1"
And I click on "All" "button" in the "Course overview" "block"
Then "Past" "list_item" should not exist in the ".block_myoverview .dropdown-menu" "css_element"
Scenario: Enable 'Starred' course filter option
Given I log in as "admin"
And I navigate to "Plugins > Blocks > Course overview" in site administration
And I set the field "Starred" to "1"
And I press "Save"
And I log out
Then I am on the "My courses" page logged in as "student1"
And I click on "All" "button" in the "Course overview" "block"
Then "Starred" "list_item" should exist in the ".block_myoverview .dropdown-menu" "css_element"
Scenario: Disable 'Starred' course filter option
Given I log in as "admin"
And I navigate to "Plugins > Blocks > Course overview" in site administration
And I set the field "Starred" to "0"
And I press "Save"
And I log out
Then I am on the "My courses" page logged in as "student1"
And I click on "All" "button" in the "Course overview" "block"
Then "Starred" "list_item" should not exist in the ".block_myoverview .dropdown-menu" "css_element"
Scenario: Enable 'Removed courses' course filter option
Given I log in as "admin"
And I navigate to "Plugins > Blocks > Course overview" in site administration
And I set the field "Removed from view" to "1"
And I press "Save"
And I log out
Then I am on the "My courses" page logged in as "student1"
And I click on "All" "button" in the "Course overview" "block"
Then "Removed from view" "list_item" should exist in the ".block_myoverview .dropdown-menu" "css_element"
Scenario: Disable 'Removed courses' course filter option
Given I log in as "admin"
And I navigate to "Plugins > Blocks > Course overview" in site administration
And I set the field "Removed from view" to "0"
And I press "Save"
And I log out
Then I am on the "My courses" page logged in as "student1"
And I click on "All" "button" in the "Course overview" "block"
Then "Removed from view" "list_item" should not exist in the ".block_myoverview .dropdown-menu" "css_element"
Scenario: Disable all course filter options
Given I log in as "admin"
And I navigate to "Plugins > Blocks > Course overview" in site administration
And I set the field "All (including removed from view)" to "0"
And I set the field "All" in the "//*[@id=\"admin-displaygroupingall\"]" "xpath_element" to "0"
And I set the field "In progress" to "0"
And I set the field "Future" to "0"
And I set the field "Past" to "0"
And I set the field "Starred" to "0"
And I set the field "Removed from view" to "0"
And I press "Save"
And I log out
And I am on the "My courses" page logged in as "student1"
Then "button#groupingdropdown" "css_element" should not exist in the ".block_myoverview" "css_element"
And I should see "Course 1" in the "Course overview" "block"
And I should see "Course 2" in the "Course overview" "block"
And I should see "Course 3" in the "Course overview" "block"
And I should see "Course 4" in the "Course overview" "block"
And I should see "Course 5" in the "Course overview" "block"
Scenario: Disable all but one course filter option
Given I log in as "admin"
And I navigate to "Plugins > Blocks > Course overview" in site administration
And I set the field "All (including removed from view)" to "0"
And I set the field "All" in the "//*[@id=\"admin-displaygroupingall\"]" "xpath_element" to "0"
And I set the field "In progress" to "1"
And I set the field "Future" to "0"
And I set the field "Past" to "0"
And I set the field "Starred" to "0"
And I set the field "Removed from view" to "0"
And I press "Save"
And I log out
And I am on the "My courses" page logged in as "student1"
Then "button#groupingdropdown" "css_element" should not exist in the ".block_myoverview" "css_element"
And I should see "Course 2" in the "Course overview" "block"
And I should see "Course 3" in the "Course overview" "block"
And I should see "Course 4" 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"
@@ -0,0 +1,197 @@
@block @block_myoverview @javascript
Feature: The my overview block allows users to group courses by custom fields
Background:
Given the following "users" exist:
| username | firstname | lastname | email | idnumber |
| student1 | Student | X | student1@example.com | S1 |
And the following "custom field categories" exist:
| name | component | area | itemid |
| Course fields | core_course | course | 0 |
And the following "custom fields" exist:
| name | category | type | shortname | configdata |
| Checkbox field | Course fields | checkbox | checkboxfield | |
| Date field | Course fields | date | datefield | {"mindate":0, "maxdate":0} |
| Select field | Course fields | select | selectfield | {"options":"Option 1\nOption 2\nOption 3\nOption 4"} |
| Text field | Course fields | text | textfield | {"visibility":"2"} |
| Text field 2 | Course fields | text | textfield2 | {"visibility":"2"} |
| Hidden field | Course fields | text | hiddenfield | {"visibility":"0"} |
And the following "courses" exist:
| fullname | shortname | category | customfield_checkboxfield | customfield_datefield | customfield_selectfield | customfield_textfield | customfield_textfield2 |
| Course 1 | C1 | 0 | 1 | 981028800 | 1 | fish | penguin |
| Course 2 | C2 | 0 | 0 | 334324800 | | | |
| Course 3 | C3 | 0 | 0 | 981028800 | 2 | dog | |
| Course 4 | C4 | 0 | 1 | | 3 | cat | |
| Course 5 | C5 | 0 | | 334411200 | 2 | fish | penguin |
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: Group courses by checkbox: Yes
Given the following config values are set as admin:
| displaygroupingcustomfield | 1 | block_myoverview |
| customfiltergrouping | checkboxfield | block_myoverview |
And I am on the "My courses" page logged in as "student1"
And I click on "All" "button" in the "Course overview" "block"
When I click on "Checkbox field: Yes" "link" in the "Course overview" "block"
Then I should 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 see "Course 4" in the "Course overview" "block"
And I should not see "Course 5" in the "Course overview" "block"
Scenario: Group courses by checkbox: No
Given the following config values are set as admin:
| displaygroupingcustomfield | 1 | block_myoverview |
| customfiltergrouping | checkboxfield | block_myoverview |
And I am on the "My courses" page logged in as "student1"
And I click on "All" "button" in the "Course overview" "block"
When I click on "Checkbox field: No" "link" in the "Course overview" "block"
Then I should not see "Course 1" in the "Course overview" "block"
And I should see "Course 2" in the "Course overview" "block"
And I should see "Course 3" in the "Course overview" "block"
And I should not see "Course 4" in the "Course overview" "block"
And I should see "Course 5" in the "Course overview" "block"
Scenario: Group courses by date: 1 February 2001
Given the following config values are set as admin:
| displaygroupingcustomfield | 1 | block_myoverview |
| customfiltergrouping | datefield | block_myoverview |
And I am on the "My courses" page logged in as "student1"
And I click on "All" "button" in the "Course overview" "block"
When I click on "1 February 2001" "link" in the "Course overview" "block"
Then I should see "Course 1" in the "Course overview" "block"
And I should not see "Course 2" in the "Course overview" "block"
And I should 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"
Scenario: Group courses by date: 6 August 1980
Given the following config values are set as admin:
| displaygroupingcustomfield | 1 | block_myoverview |
| customfiltergrouping | datefield | block_myoverview |
And I am on the "My courses" page logged in as "student1"
And I click on "All" "button" in the "Course overview" "block"
When I click on "6 August 1980" "link" in the "Course overview" "block"
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 see "Course 5" in the "Course overview" "block"
Scenario: Group courses by date: No Date field
Given the following config values are set as admin:
| displaygroupingcustomfield | 1 | block_myoverview |
| customfiltergrouping | datefield | block_myoverview |
And I am on the "My courses" page logged in as "student1"
And I click on "All" "button" in the "Course overview" "block"
When I click on "No Date field" "link" in the "Course overview" "block"
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 see "Course 4" in the "Course overview" "block"
And I should not see "Course 5" in the "Course overview" "block"
Scenario: Group courses by select: Option 1
Given the following config values are set as admin:
| displaygroupingcustomfield | 1 | block_myoverview |
| customfiltergrouping | selectfield | block_myoverview |
And I am on the "My courses" page logged in as "student1"
And I click on "All" "button" in the "Course overview" "block"
And I should not see "Option 4" in the "Course overview" "block"
When I click on "Option 1" "link" in the "Course overview" "block"
Then I should 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"
Scenario: Group courses by select: Option 2
Given the following config values are set as admin:
| displaygroupingcustomfield | 1 | block_myoverview |
| customfiltergrouping | selectfield | block_myoverview |
And I am on the "My courses" page logged in as "student1"
And I click on "All" "button" in the "Course overview" "block"
When I click on "Option 2" "link" in the "Course overview" "block"
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 see "Course 3" in the "Course overview" "block"
And I should not see "Course 4" in the "Course overview" "block"
And I should see "Course 5" in the "Course overview" "block"
Scenario: Group courses by select: No Select field
Given the following config values are set as admin:
| displaygroupingcustomfield | 1 | block_myoverview |
| customfiltergrouping | selectfield | block_myoverview |
And I am on the "My courses" page logged in as "student1"
And I click on "All" "button" in the "Course overview" "block"
When I click on "No Select field" "link" in the "Course overview" "block"
Then I should not see "Course 1" in the "Course overview" "block"
And I should 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"
Scenario: Group courses by text: fish
Given the following config values are set as admin:
| displaygroupingcustomfield | 1 | block_myoverview |
| customfiltergrouping | textfield | block_myoverview |
And I am on the "My courses" page logged in as "student1"
And I click on "All" "button" in the "Course overview" "block"
When I click on "fish" "link" in the "Course overview" "block"
Then I should 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 see "Course 5" in the "Course overview" "block"
Scenario: Group courses by text: dog
Given the following config values are set as admin:
| displaygroupingcustomfield | 1 | block_myoverview |
| customfiltergrouping | textfield | block_myoverview |
And I am on the "My courses" page logged in as "student1"
And I click on "All" "button" in the "Course overview" "block"
When I click on "dog" "link" in the "Course overview" "block"
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 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"
Scenario: Group courses by text: No Text field
Given the following config values are set as admin:
| displaygroupingcustomfield | 1 | block_myoverview |
| customfiltergrouping | textfield | block_myoverview |
And I am on the "My courses" page logged in as "student1"
And I click on "All" "button" in the "Course overview" "block"
When I click on "No Text field" "link" in the "Course overview" "block"
Then I should not see "Course 1" in the "Course overview" "block"
And I should 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"
Scenario: Hidden fields not displayed when configuring the custom field filters
Given I log in as "admin"
When I navigate to "Plugins > Blocks > Course overview" in site administration
And I set the field "Custom field" to "1"
Then the "Field to use" select box should not contain "Hidden field"
Scenario: Hidden fields not displayed in the filter
Given the following config values are set as admin:
| displaygroupingcustomfield | 1 | block_myoverview |
| customfiltergrouping | textfield2 | block_myoverview |
And I log in as "admin"
And I navigate to "Courses > Default settings > Course custom fields" in site administration
And I click on "Edit" "link" in the "Text field 2" "table_row"
And I set the field "Visible to" to "Nobody"
And I press "Save changes"
And I log out
When I am on the "My courses" page logged in as "student1"
And I click on "All" "button" in the "Course overview" "block"
Then I should not see "penguin" in the "Course overview" "block"
Then I should not see "No text field" in the "Course overview" "block"
@@ -0,0 +1,319 @@
@block @block_myoverview @javascript
Feature: The my overview block allows users to easily access 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 "categories" exist:
| name | category | idnumber |
| Category 1 | 0 | CAT1 |
And the following "courses" exist:
| fullname | shortname | category | startdate | enddate |
| Course 1 | C1 | 0 | ##1 month ago## | ##15 days ago## |
| Course 2 | C2 | 0 | ##yesterday## | ##tomorrow## |
| Course 3 | C3 | 0 | ##yesterday## | ##tomorrow## |
| Course 4 | C4 | CAT1 | ##yesterday## | ##tomorrow## |
| Course 5 | C5 | 0 | ##first day of next month## | ##last day of next month## |
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: View past courses
Given I am on the "My courses" page logged 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"
Then I should 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"
Scenario: View future courses
Given I am on the "My courses" page logged 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"
Then I should 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"
Scenario: View inprogress courses
Given I am on the "My courses" page logged 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"
Then I should see "Course 2" in the "Course overview" "block"
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 1" in the "Course overview" "block"
And I should not see "Course 5" in the "Course overview" "block"
Scenario: View all (except removed) courses
Given I am on the "My courses" page logged in as "student1"
And I click on "All" "button" in the "Course overview" "block"
When I click on "All" "link" in the "Course overview" "block"
Then I should see "Course 1" in the "Course overview" "block"
Then I should see "Course 2" in the "Course overview" "block"
Then I should see "Course 3" in the "Course overview" "block"
Then I should see "Course 4" in the "Course overview" "block"
Then I should see "Course 5" in the "Course overview" "block"
Scenario: View all (including removed from view) courses
Given the following config values are set as admin:
| config | value | plugin |
| displaygroupingallincludinghidden | 1 | block_myoverview |
And I am on the "My courses" page logged in as "student1"
And I click on "All" "button" in the "Course overview" "block"
# We have to click on the data attribute instead of the button element text as we might risk to click on the false positive "All (including removed from view)" element instead
When I click on "[data-value='allincludinghidden']" "css_element" in the "Course overview" "block"
Then I should see "Course 1" in the "Course overview" "block"
Then I should see "Course 2" in the "Course overview" "block"
Then I should see "Course 3" in the "Course overview" "block"
Then I should see "Course 4" in the "Course overview" "block"
Then I should see "Course 5" in the "Course overview" "block"
Scenario: View inprogress courses - test persistence
Given I am on the "My courses" page logged in as "student1"
And I click on "All" "button" in the "Course overview" "block"
And I click on "In progress" "link" in the "Course overview" "block"
And I reload the page
Then I should see "In progress" in the "Course overview" "block"
Then I should see "Course 2" in the "Course overview" "block"
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 1" in the "Course overview" "block"
And I should not see "Course 5" in the "Course overview" "block"
Scenario: View all (except removed) courses - w/ persistence
Given I am on the "My courses" page logged in as "student1"
And I click on "All" "button" in the "Course overview" "block"
When I click on "All" "link" in the "Course overview" "block"
And I reload the page
Then I should see "All" in the "Course overview" "block"
Then I should see "Course 1" in the "Course overview" "block"
Then I should see "Course 2" in the "Course overview" "block"
Then I should see "Course 3" in the "Course overview" "block"
Then I should see "Course 4" in the "Course overview" "block"
Then I should see "Course 5" in the "Course overview" "block"
Scenario: View past courses - w/ persistence
Given I am on the "My courses" page logged 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 reload the page
Then I should see "Past" in the "Course overview" "block"
Then I should 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"
Scenario: View future courses - w/ persistence
Given I am on the "My courses" page logged 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 reload the page
Then I should see "Future" in the "Course overview" "block"
Then I should 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"
Scenario: View favourite courses - w/ persistence
Given I am on the "My courses" page logged in as "student1"
And I click on ".coursemenubtn" "css_element" in the "//div[contains(@class, 'course-card') and contains(.,'Course 2')]" "xpath_element"
And I click on "Star this course" "link" in the "//div[contains(@class, 'course-card') and contains(.,'Course 2')]" "xpath_element"
And I click on "All" "button" in the "Course overview" "block"
When I click on "Starred" "link" in the "Course overview" "block"
And I reload the page
Then I should see "Starred" in the "Course overview" "block"
And I should 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 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"
Scenario: List display persistence
Given I am on the "My courses" page logged in as "student1"
And I click on "Display drop-down menu" "button" in the "Course overview" "block"
And I click on "List" "link" in the "Course overview" "block"
And I reload the page
Then I should see "List" in the "Course overview" "block"
And "[data-display='list']" "css_element" in the "Course overview" "block" should be visible
Scenario: Cards display persistence
Given I am on the "My courses" page logged in as "student1"
And I click on "Display drop-down menu" "button" in the "Course overview" "block"
And I click on "Card" "link" in the "Course overview" "block"
And I reload the page
Then I should see "Card" in the "Course overview" "block"
And "[data-display='card']" "css_element" in the "Course overview" "block" should be visible
Scenario: Summary display persistence
Given I am on the "My courses" page logged in as "student1"
And I click on "Display drop-down menu" "button" in the "Course overview" "block"
And I click on "Summary" "link" in the "Course overview" "block"
And I reload the page
Then I should see "Summary" in the "Course overview" "block"
And "[data-display='summary']" "css_element" in the "Course overview" "block" should be visible
Scenario: Course name sort persistence
Given I am on the "My courses" page logged in as "student1"
And I click on "sortingdropdown" "button" in the "Course overview" "block"
And I click on "Sort by course name" "link" in the "Course overview" "block"
And I reload the page
Then I should see "Sort by course name" in the "Course overview" "block"
And "[data-sort='fullname']" "css_element" in the "Course overview" "block" should be visible
Scenario: Last accessed sort persistence
Given I am on the "My courses" page logged in as "student1"
And I click on "sortingdropdown" "button" in the "Course overview" "block"
And I click on "Sort by last accessed" "link" in the "Course overview" "block"
And I reload the page
Then I should see "Sort by last accessed" in the "Course overview" "block"
And "[data-sort='ul.timeaccess desc']" "css_element" in the "Course overview" "block" should be visible
Scenario: Short name sort persistence
Given I am on the "My courses" page logged in as "student1"
When I click on "sortingdropdown" "button" in the "Course overview" "block"
Then I should not see "Sort by short name" in the "Course overview" "block"
When the following config values are set as admin:
| config | value |
| courselistshortnames | 1 |
And I reload the page
And I click on "sortingdropdown" "button" in the "Course overview" "block"
And I click on "Sort by short name" "link" in the "Course overview" "block"
And I reload the page
Then I should see "Sort by short name" in the "Course overview" "block"
And "[data-sort='shortname']" "css_element" in the "Course overview" "block" should be visible
Scenario: View inprogress courses with hide persistent functionality
Given I am on the "My courses" page logged 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[contains(@class, 'course-card') and contains(.,'Course 2')]" "xpath_element"
And I click on "Remove from view" "link" in the "//div[contains(@class, '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"
Scenario: View past courses with hide persistent functionality
Given I am on the "My courses" page logged 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[contains(@class, 'course-card') and contains(.,'Course 1')]" "xpath_element"
And I click on "Remove from view" "link" in the "//div[contains(@class, '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"
Scenario: View future courses with hide persistent functionality
Given I am on the "My courses" page logged 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[contains(@class, 'course-card') and contains(.,'Course 5')]" "xpath_element"
And I click on "Remove from view" "link" in the "//div[contains(@class, '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"
Scenario: View all (except hidden) courses with hide persistent functionality
Given I am on the "My courses" page logged in as "student1"
And I click on "All" "button" in the "Course overview" "block"
When I click on "All" "link" in the "Course overview" "block"
And I click on ".coursemenubtn" "css_element" in the "//div[contains(@class, 'course-card') and contains(.,'Course 5')]" "xpath_element"
And I click on "Remove from view" "link" in the "//div[contains(@class, '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 see "Course 1" in the "Course overview" "block"
And I should see "Course 2" in the "Course overview" "block"
And I should see "Course 3" in the "Course overview" "block"
And I should see "Course 4" in the "Course overview" "block"
Scenario: View all (including removed from view) courses with hide persistent functionality
Given the following config values are set as admin:
| config | value | plugin |
| displaygroupingallincludinghidden | 1 | block_myoverview |
And I am on the "My courses" page logged in as "student1"
And I click on "All" "button" in the "Course overview" "block"
# We have to click on the data attribute instead of the button element text as we might risk to click on the false positive "All (including removed from view)" element instead
When I click on "[data-value='allincludinghidden']" "css_element" in the "Course overview" "block"
And I click on ".coursemenubtn" "css_element" in the "//div[contains(@class, 'course-card') and contains(.,'Course 5')]" "xpath_element"
And I click on "Remove from view" "link" in the "//div[contains(@class, 'course-card') and contains(.,'Course 5')]" "xpath_element"
And I reload the page
Then I should see "Course 5" in the "Course overview" "block"
And I should see "Course 1" in the "Course overview" "block"
And I should see "Course 2" in the "Course overview" "block"
And I should see "Course 3" in the "Course overview" "block"
And I should see "Course 4" in the "Course overview" "block"
Scenario: Show course category in cards display
Given the following config values are set as admin:
| displaycategories | 1 | block_myoverview |
And I am on the "My courses" page logged in as "student1"
And I click on "Display drop-down menu" "button" in the "Course overview" "block"
When I click on "Card" "link" in the "Course overview" "block"
Then I should see "Category 1" in the "Course overview" "block"
Scenario: Show course category in list display
Given the following config values are set as admin:
| displaycategories | 1 | block_myoverview |
And I am on the "My courses" page logged in as "student1"
And I click on "Display drop-down menu" "button" in the "Course overview" "block"
When I click on "List" "link" in the "Course overview" "block"
Then I should see "Category 1" in the "Course overview" "block"
Scenario: Show course category in summary display
Given the following config values are set as admin:
| displaycategories | 1 | block_myoverview |
And I am on the "My courses" page logged in as "student1"
And I click on "Display drop-down menu" "button" in the "Course overview" "block"
When I click on "Summary" "link" in the "Course overview" "block"
Then I should see "Category 1" in the "Course overview" "block"
Scenario: Hide course category in cards display
Given the following config values are set as admin:
| displaycategories | 0 | block_myoverview |
And I am on the "My courses" page logged in as "student1"
And I click on "Display drop-down menu" "button" in the "Course overview" "block"
When I click on "Card" "link" in the "Course overview" "block"
Then I should not see "Category 1" in the "Course overview" "block"
Scenario: Hide course category in list display
Given the following config values are set as admin:
| displaycategories | 0 | block_myoverview |
And I am on the "My courses" page logged in as "student1"
And I click on "Display drop-down menu" "button" in the "Course overview" "block"
When I click on "List" "link" in the "Course overview" "block"
Then I should not see "Category 1" in the "Course overview" "block"
Scenario: Show course category in summary display
Given the following config values are set as admin:
| displaycategories | 0 | block_myoverview |
And I am on the "My courses" page logged in as "student1"
And I click on "Display drop-down menu" "button" in the "Course overview" "block"
When I click on "Summary" "link" in the "Course overview" "block"
Then I should not see "Category 1" in the "Course overview" "block"
@accessibility
Scenario: The dashboard page must have sufficient colour contrast
When I am on the "My courses" page logged in as "student1"
Then the page should meet "wcag143" accessibility standards
@@ -0,0 +1,56 @@
@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: Favourite a course on a course card
Given I am on the "My courses" page logged in as "student1"
When I click on ".coursemenubtn" "css_element" in the "//div[contains(@class, 'course-card') and contains(.,'Course 2')]" "xpath_element"
And I click on "Star this course" "link" in the "//div[contains(@class, 'course-card') and contains(.,'Course 2')]" "xpath_element"
And I reload the page
Then "//div[contains(@class, 'course-card') and contains(.,'Course 2')]//span[@data-region='is-favourite' and @aria-hidden='false']" "xpath_element" should exist
And "//div[contains(@class, 'course-card') and contains(.,'Course 2')]//span[@data-region='is-favourite' and @aria-hidden='true']" "xpath_element" should not exist
And "//div[contains(@class, 'course-card') and contains(.,'Course 1')]//span[@data-region='is-favourite' and @aria-hidden='true']" "xpath_element" should exist
And "//div[contains(@class, 'course-card') and contains(.,'Course 3')]//span[@data-region='is-favourite' and @aria-hidden='true']" "xpath_element" should exist
Scenario: Star a course and switch display to list
Given I am on the "My courses" page logged in as "student1"
When I click on ".coursemenubtn" "css_element" in the "//div[contains(@class, 'course-card') and contains(.,'Course 5')]" "xpath_element"
And I click on "Star this course" "link" in the "//div[contains(@class, 'course-card') and contains(.,'Course 5')]" "xpath_element"
And I click on "Display drop-down menu" "button" in the "Course overview" "block"
And I click on "List" "link" in the "Course overview" "block"
Then "//li[contains(concat(' ', normalize-space(@class), ' '), 'list-group-item') and contains(.,'Course 5')]//span[@data-region='is-favourite' and @aria-hidden='false']" "xpath_element" should exist
And "//li[contains(concat(' ', normalize-space(@class), ' '), 'list-group-item') and contains(.,'Course 5')]//span[@data-region='is-favourite' and @aria-hidden='true']" "xpath_element" should not exist
And "//li[contains(concat(' ', normalize-space(@class), ' '), 'list-group-item') and contains(.,'Course 1')]//span[@data-region='is-favourite' and @aria-hidden='true']" "xpath_element" should exist
And "//li[contains(concat(' ', normalize-space(@class), ' '), 'list-group-item') and contains(.,'Course 3')]//span[@data-region='is-favourite' and @aria-hidden='true']" "xpath_element" should exist
Scenario: Star a course and switch display to summary
Given I am on the "My courses" page logged in as "student1"
When I click on ".coursemenubtn" "css_element" in the "//div[contains(@class, 'course-card') and contains(.,'Course 5')]" "xpath_element"
And I click on "Star this course" "link" in the "//div[contains(@class, 'course-card') and contains(.,'Course 5')]" "xpath_element"
And I click on "Display drop-down menu" "button" in the "Course overview" "block"
And I click on "Summary" "link" in the "Course overview" "block"
Then "//div[contains(concat(' ', normalize-space(@class), ' '), 'course-summaryitem') and contains(.,'Course 5')]//span[@data-region='is-favourite' and @aria-hidden='false']" "xpath_element" should exist
And "//div[contains(concat(' ', normalize-space(@class), ' '), 'course-summaryitem') and contains(.,'Course 5')]//span[@data-region='is-favourite' and @aria-hidden='true']" "xpath_element" should not exist
And "//div[contains(concat(' ', normalize-space(@class), ' '), 'course-summaryitem') and contains(.,'Course 1')]//span[@data-region='is-favourite' and @aria-hidden='true']" "xpath_element" should exist
And "//div[contains(concat(' ', normalize-space(@class), ' '), 'course-summaryitem') and contains(.,'Course 3')]//span[@data-region='is-favourite' and @aria-hidden='true']" "xpath_element" should exist
@@ -0,0 +1,117 @@
@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 am on the "My courses" page logged in as "student1"
And I click on "All" "button" in the "Course overview" "block"
When I click on "All" "link" in the "Course overview" "block"
And I click on ".coursemenubtn" "css_element" in the "//div[contains(@class, 'course-card') and contains(.,'Course 2')]" "xpath_element"
And I click on "Remove from view" "link" in the "//div[contains(@class, '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"
Scenario: Test hide toggle functionality w/ favorites
Given I am on the "My courses" page logged in as "student1"
And I click on "All" "button" in the "Course overview" "block"
And I click on "All" "link" in the "Course overview" "block"
And I click on ".coursemenubtn" "css_element" in the "//div[contains(@class, 'course-card') and contains(.,'Course 2')]" "xpath_element"
And I click on "Star this course" "link" in the "//div[contains(@class, 'course-card') and contains(.,'Course 2')]" "xpath_element"
And I click on ".coursemenubtn" "css_element" in the "//div[contains(@class, 'course-card') and contains(.,'Course 2')]" "xpath_element"
And I click on "Remove from view" "link" in the "//div[contains(@class, 'course-card') and contains(.,'Course 2')]" "xpath_element"
When I reload the page
And 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 "Removed from view" "link" in the "Course overview" "block"
And I should see "Course 2" in the "Course overview" "block"
Scenario: Test show toggle functionality
Given I am on the "My courses" page logged in as "student1"
And I click on "All" "button" in the "Course overview" "block"
And I click on "All" "link" in the "Course overview" "block"
And I click on ".coursemenubtn" "css_element" in the "//div[contains(@class, 'course-card') and contains(.,'Course 2')]" "xpath_element"
And I click on "Remove from view" "link" in the "//div[contains(@class, 'course-card') and contains(.,'Course 2')]" "xpath_element"
And I click on "All" "button" in the "Course overview" "block"
And I click on "Removed from view" "link" in the "Course overview" "block"
And I click on ".coursemenubtn" "css_element" in the "//div[contains(@class, 'course-card') and contains(.,'Course 2')]" "xpath_element"
And I click on "Restore to view" "link" in the "//div[contains(@class, 'course-card') and contains(.,'Course 2')]" "xpath_element"
And I reload the page
And I should not see "Course 2" in the "Course overview" "block"
And I click on "Removed from view" "button" in the "Course overview" "block"
When I click on "All" "link" in the "Course overview" "block"
And I reload the page
Then I should see "Course 2" in the "Course overview" "block"
Scenario: Test star and unstar functionality
Given I am on the "My courses" page logged in as "student1"
And I click on "All" "button" in the "Course overview" "block"
And I click on "All" "link" in the "Course overview" "block"
And I click on ".coursemenubtn" "css_element" in the "//div[contains(@class, 'course-card') and contains(.,'Course 2')]" "xpath_element"
And I click on "Star this course" "link" in the "//div[contains(@class, 'course-card') and contains(.,'Course 2')]" "xpath_element"
And I click on ".coursemenubtn" "css_element" in the "//div[contains(@class, 'course-card') and contains(.,'Course 2')]" "xpath_element"
And I click on "Remove from view" "link" in the "//div[contains(@class, 'course-card') and contains(.,'Course 2')]" "xpath_element"
And I click on "All" "button" in the "Course overview" "block"
And I click on "Removed from view" "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[contains(@class, 'course-card') and contains(.,'Course 2')]" "xpath_element"
And I click on "Restore to view" "link" in the "//div[contains(@class, '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 "Removed from view" "button" in the "Course overview" "block"
And I click on "All" "link" in the "Course overview" "block"
And 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"
And I should see "Course 2" in the "Course overview" "block"
Scenario: Test a course is hidden directly with "All" courses
Given I am on the "My courses" page logged in as "student1"
And I click on "All" "button" in the "Course overview" "block"
When I click on "All" "link" in the "Course overview" "block"
And I click on ".coursemenubtn" "css_element" in the "//div[contains(@class, 'course-card') and contains(.,'Course 2')]" "xpath_element"
And I click on "Remove from view" "link" in the "//div[contains(@class, 'course-card') and contains(.,'Course 2')]" "xpath_element"
Then I should not see "Course 2" in the "Course overview" "block"
Scenario: Test a course is never hidden with "All (including removed from view)" courses
Given the following config values are set as admin:
| config | value | plugin |
| displaygroupingallincludinghidden | 1 | block_myoverview |
And I am on the "My courses" page logged in as "student1"
And I click on "All" "button" in the "Course overview" "block"
# We have to click on the data attribute instead of the button element text as we might risk to click on the false positive "All (except hidden)" element instead
When I click on "[data-value='allincludinghidden']" "css_element" in the "Course overview" "block"
And I click on ".coursemenubtn" "css_element" in the "//div[contains(@class, 'course-card') and contains(.,'Course 2')]" "xpath_element"
And I click on "Remove from view" "link" in the "//div[contains(@class, 'course-card') and contains(.,'Course 2')]" "xpath_element"
Then I should see "Course 2" in the "Course overview" "block"
And I click on ".coursemenubtn" "css_element" in the "//div[contains(@class, 'course-card') and contains(.,'Course 2')]" "xpath_element"
And I should not see "Remove from view" in the "//div[contains(@class, 'course-card') and contains(.,'Course 2')]" "xpath_element"
And I should see "Restore to view" in the "//div[contains(@class, 'course-card') and contains(.,'Course 2')]" "xpath_element"
And I click on "Restore to view" "link" in the "//div[contains(@class, 'course-card') and contains(.,'Course 2')]" "xpath_element"
And I should see "Course 2" in the "Course overview" "block"
And I click on ".coursemenubtn" "css_element" in the "//div[contains(@class, 'course-card') and contains(.,'Course 2')]" "xpath_element"
And I should see "Remove from view" in the "//div[contains(@class, 'course-card') and contains(.,'Course 2')]" "xpath_element"
And I should not see "Restore to view" in the "//div[contains(@class, 'course-card') and contains(.,'Course 2')]" "xpath_element"
@@ -0,0 +1,55 @@
@block @block_myoverview @javascript
Feature: The my overview block allows users to persistence of their page limits
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 | C01 | 0 |
| Course 2 | C02 | 0 |
| Course 3 | C03 | 0 |
| Course 4 | C04 | 0 |
| Course 5 | C05 | 0 |
| Course 6 | C06 | 0 |
| Course 7 | C07 | 0 |
| Course 8 | C08 | 0 |
| Course 9 | C09 | 0 |
| Course 10 | C10 | 0 |
| Course 11 | C11 | 0 |
| Course 12 | C12 | 0 |
| Course 13 | C13 | 0 |
And the following "course enrolments" exist:
| user | course | role |
| student1 | C01 | student |
| student1 | C02 | student |
| student1 | C03 | student |
| student1 | C04 | student |
| student1 | C05 | student |
| student1 | C06 | student |
| student1 | C07 | student |
| student1 | C08 | student |
| student1 | C09 | student |
| student1 | C10 | student |
| student1 | C11 | student |
| student1 | C12 | student |
| student1 | C13 | student |
Scenario: Toggle the page limit between page reloads
Given I am on the "My courses" page logged in as "student1"
When I click on "[data-action='limit-toggle']" "css_element" in the "Course overview" "block"
And I click on "All" "link" in the ".dropdown-menu.show" "css_element"
Then I should see "Course 13"
And I reload the page
Then I should see "Course 13"
And I should see "All" in the ".block-myoverview [data-action='limit-toggle']" "css_element"
Scenario: Toggle the page limit between grouping changes
Given I am on the "My courses" page logged in as "student1"
When I click on "[data-action='limit-toggle']" "css_element" in the "Course overview" "block"
And I click on "All" "link" in the ".dropdown-menu.show" "css_element"
And I click on "All" "button" in the "Course overview" "block"
And I click on "In progress" "link" in the "Course overview" "block"
Then I should see "Course 13"
And I should see "All" in the ".block-myoverview [data-action='limit-toggle']" "css_element"
@@ -0,0 +1,191 @@
@block @block_myoverview @javascript
Feature: My overview block pagination
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 01 | C1 | 0 |
| Course 02 | C2 | 0 |
| Course 03 | C3 | 0 |
| Course 04 | C4 | 0 |
| Course 05 | C5 | 0 |
| Course 06 | C6 | 0 |
| Course 07 | C7 | 0 |
| Course 08 | C8 | 0 |
| Course 09 | C9 | 0 |
| Course 10 | C10 | 0 |
| Course 11 | C11 | 0 |
| Course 12 | C12 | 0 |
| Course 13 | C13 | 0 |
| Course 14 | C14 | 0 |
| Course 15 | C15 | 0 |
| Course 16 | C16 | 0 |
| Course 17 | C17 | 0 |
| Course 18 | C18 | 0 |
| Course 19 | C19 | 0 |
| Course 20 | C20 | 0 |
| Course 21 | C21 | 0 |
| Course 22 | C22 | 0 |
| Course 23 | C23 | 0 |
| Course 24 | C24 | 0 |
| Course 25 | C25 | 0 |
Scenario: The pagination controls should be hidden if I am not enrolled in any courses
When I am on the "My courses" page logged in as "student1"
Then I should see "You're not enrolled in any course" in the "Course overview" "block"
And I should not see "Show" in the "Course overview" "block"
And ".block_myoverview .dropdown-menu.show" "css_element" should not be visible
And ".block_myoverview [data-control='next']" "css_element" should not be visible
And ".block_myoverview [data-control='previous']" "css_element" should not be visible
And I log out
Scenario: The pagination controls should be hidden if I am enrolled in 12 courses or less
Given the following "course enrolments" exist:
| user | course | role |
| student1 | C1 | student |
| student1 | C2 | student |
| student1 | C3 | student |
| student1 | C4 | student |
| student1 | C5 | student |
| student1 | C6 | student |
| student1 | C7 | student |
| student1 | C8 | student |
| student1 | C9 | student |
| student1 | C10 | student |
| student1 | C11 | student |
| student1 | C12 | student |
When I am on the "My courses" page logged in as "student1"
Then I should not see "Show" in the "Course overview" "block"
And ".block_myoverview .dropdown-menu.show" "css_element" should not be visible
And ".block_myoverview [data-control='next']" "css_element" should not be visible
And ".block_myoverview [data-control='previous']" "css_element" should not be visible
And I log out
Scenario: The default pagination should be 12 courses
Given the following "course enrolments" exist:
| user | course | role |
| student1 | C1 | student |
| student1 | C2 | student |
| student1 | C3 | student |
| student1 | C4 | student |
| student1 | C5 | student |
| student1 | C6 | student |
| student1 | C7 | student |
| student1 | C8 | student |
| student1 | C9 | student |
| student1 | C10 | student |
| student1 | C11 | student |
| student1 | C12 | student |
| student1 | C13 | student |
When I am on the "My courses" page logged in as "student1"
Then I should see "12" in the ".block_myoverview [data-action='limit-toggle']" "css_element"
And I log out
Scenario: I should only see pagination limit options less than total number of enrolled courses
Given the following "course enrolments" exist:
| user | course | role |
| student1 | C1 | student |
| student1 | C2 | student |
| student1 | C3 | student |
| student1 | C4 | student |
| student1 | C5 | student |
| student1 | C6 | student |
| student1 | C7 | student |
| student1 | C8 | student |
| student1 | C9 | student |
| student1 | C10 | student |
| student1 | C11 | student |
| student1 | C12 | student |
| student1 | C13 | student |
And I am on the "My courses" page logged in as "student1"
When I click on "[data-action='limit-toggle']" "css_element" in the "Course overview" "block"
Then I should see "All" in the ".dropdown-menu.show" "css_element"
And I should see "12" in the ".dropdown-menu.show" "css_element"
And ".block_myoverview [data-control='next']" "css_element" should be visible
And ".block_myoverview [data-control='previous']" "css_element" should be visible
But I should not see "24" in the ".block_myoverview .dropdown-menu.show" "css_element"
And I log out
Scenario: Previous page button should be disabled when on the first page of courses
Given the following "course enrolments" exist:
| user | course | role |
| student1 | C1 | student |
| student1 | C2 | student |
| student1 | C3 | student |
| student1 | C4 | student |
| student1 | C5 | student |
| student1 | C6 | student |
| student1 | C7 | student |
| student1 | C8 | student |
| student1 | C9 | student |
| student1 | C10 | student |
| student1 | C11 | student |
| student1 | C12 | student |
| student1 | C13 | student |
When I am on the "My courses" page logged in as "student1"
Then the "class" attribute of ".block_myoverview [data-control='previous']" "css_element" should contain "disabled"
And I log out
Scenario: Next page button should be disabled when on the last page of courses
Given the following "course enrolments" exist:
| user | course | role |
| student1 | C1 | student |
| student1 | C2 | student |
| student1 | C3 | student |
| student1 | C4 | student |
| student1 | C5 | student |
| student1 | C6 | student |
| student1 | C7 | student |
| student1 | C8 | student |
| student1 | C9 | student |
| student1 | C10 | student |
| student1 | C11 | student |
| student1 | C12 | student |
| student1 | C13 | student |
When I am on the "My courses" page logged in as "student1"
And I wait until ".block_myoverview [data-control='next']" "css_element" exists
And I click on "[data-control='next']" "css_element" in the "Course overview" "block"
Then the "class" attribute of ".block_myoverview [data-control='next']" "css_element" should contain "disabled"
And I log out
Scenario: Next and previous page buttons should both be enabled when not on last or first page of courses
Given the following "course enrolments" exist:
| user | course | role |
| student1 | C1 | student |
| student1 | C2 | student |
| student1 | C3 | student |
| student1 | C4 | student |
| student1 | C5 | student |
| student1 | C6 | student |
| student1 | C7 | student |
| student1 | C8 | student |
| student1 | C9 | student |
| student1 | C10 | student |
| student1 | C11 | student |
| student1 | C12 | student |
| student1 | C13 | student |
| student1 | C14 | student |
| student1 | C15 | student |
| student1 | C16 | student |
| student1 | C17 | student |
| student1 | C18 | student |
| student1 | C19 | student |
| student1 | C20 | student |
| student1 | C21 | student |
| student1 | C22 | student |
| student1 | C23 | student |
| student1 | C24 | student |
| student1 | C25 | student |
When I am on the "My courses" page logged in as "student1"
And I wait until ".block_myoverview [data-control='next']" "css_element" exists
And I click on "[data-control='next']" "css_element" in the "Course overview" "block"
Then the "class" attribute of ".block_myoverview [data-control='next']" "css_element" should not contain "disabled"
And the "class" attribute of ".block_myoverview [data-control='previous']" "css_element" should not contain "disabled"
And I should see "Course 13" in the "Course overview" "block"
And I should see "Course 24" in the "Course overview" "block"
But I should not see "Course 12" in the "Course overview" "block"
And I should not see "Course 25" in the "Course overview" "block"
And I log out
@@ -0,0 +1,44 @@
@block @block_myoverview @javascript
Feature: Course overview block show users their progress on courses
In order to enable the my overview block in a course
As a student
I can see the progress percentage of the courses I am enrolled in
Background:
Given the following "users" exist:
| username | firstname | lastname | email | idnumber |
| teacher1 | Teacher | 1 | teacher1@example.com | T1 |
| student1 | Student | 1 | student1@example.com | S1 |
And the following "courses" exist:
| fullname | shortname | category | enablecompletion | startdate | enddate |
| Course 1 | C1 | 0 | 1 | ##yesterday## | ##tomorrow## |
And the following "activities" exist:
| activity | course | idnumber | name | intro | timeopen | timeclose |
| choice | C1 | choice1 | Test choice 1 | Test choice description | ##yesterday## | ##tomorrow## |
And the following "course enrolments" exist:
| user | course | role |
| teacher1 | C1 | editingteacher |
| student1 | C1 | student |
Scenario: Course progress percentage should not be displayed if completion is not enabled
Given I am on the "My courses" page logged in as "student1"
And I click on "All" "button" in the "Course overview" "block"
When I click on "All" "link" in the "Course overview" "block"
Then I should not see "0%" in the "Course overview" "block"
Scenario: User complete activity and verify his progress
Given I am on the "Test choice 1" "choice activity" page logged in as teacher1
And I navigate to "Settings" in current page administration
And I set the following fields to these values:
| Add requirements | 1 |
| id_completionview | 1 |
And I press "Save and return to course"
And I log out
When I am on the "My courses" page logged in as "student1"
And I click on "All" "button" in the "Course overview" "block"
Then I should see "Course 1" in the "Course overview" "block"
And I should see "0%" in the "Course overview" "block"
And I am on the "Test choice 1" "choice activity" page
And I am on the "My courses" page
And I click on "All" "button" in the "Course overview" "block"
And I should see "100%" in the "Course overview" "block"
@@ -0,0 +1,62 @@
@block @block_myoverview @javascript
Feature: My overview block searching
Background:
Given the following "users" exist:
| username | firstname | lastname | email | idnumber |
| student1 | Student | X | student1@example.com | S1 |
| student2 | Student | Y | student2@example.com | S2 |
And the following "courses" exist:
| fullname | shortname | category |
| Course 01 | C1 | 0 |
| Course 02 | C2 | 0 |
| Course 03 | C3 | 0 |
| Course 04 | C4 | 0 |
| Course 05 | C5 | 0 |
| Course 06 | C6 | 0 |
| Course 07 | C7 | 0 |
| Course 08 | C8 | 0 |
| Course 09 | C9 | 0 |
| Course 10 | C10 | 0 |
| Course 11 | C11 | 0 |
| Course 12 | C12 | 0 |
| Course 13 | C13 | 0 |
| Fake example | Fake | 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 |
| student1 | C6 | student |
| student1 | C7 | student |
| student1 | C8 | student |
| student1 | C9 | student |
| student1 | C10 | student |
| student1 | C11 | student |
| student1 | C12 | student |
| student1 | C13 | student |
Scenario: There is no search if I am not enrolled in any course
When I am on the "My courses" page logged in as "student2"
Then I should see "You're not enrolled in any course" in the "Course overview" "block"
And "Search courses" "field" should not exist in the "Course overview" "block"
And I log out
Scenario: Single page search
Given I am on the "My courses" page logged in as "student1"
And I set the field "Search courses" in the "Course overview" "block" to "Course 0"
Then I should see "Course 01" in the "Course overview" "block"
And I should not see "Course 13" in the "Course overview" "block"
And I log out
Scenario: Paginated search
Given I am on the "My courses" page logged in as "student1"
And I set the field "Search courses" in the "Course overview" "block" to "Course"
And I should see "Course 01" in the "Course overview" "block"
And I should not see "Course 13" in the "Course overview" "block"
And I click on "[data-control='next']" "css_element" in the "Course overview" "block"
And I wait until ".block_myoverview [data-control='next']" "css_element" exists
Then I should see "Course 13" in the "Course overview" "block"
And I should not see "Course 01" in the "Course overview" "block"
@@ -0,0 +1,77 @@
@block @block_myoverview @javascript
Feature: Zero state on my overview block
In order to know what should be the next step
As a user
I should see the proper information based on my capabilities
Background:
Given the following "users" exist:
| username | firstname | lastname | email | idnumber |
| user | User | X | user@example.com | U1 |
| manager | Manager | X | manager@example.com | M1 |
And the following "role assigns" exist:
| user | role | contextlevel | reference |
| manager | manager | System | |
Scenario: Users with no permissions don't see any CTA
Given I am on the "My courses" page logged in as "user"
When I should see "You're not enrolled in any course"
Then I should see "Once you're enrolled in a course, it will appear here."
And I should not see "Create course"
And I should not see "Request a course"
Scenario: Users with permissions to request a course should see a Request course button
Given the following "permission overrides" exist:
| capability | permission | role | contextlevel | reference |
| moodle/course:request | Allow | user | System | |
When I am on the "My courses" page logged in as "user"
Then I should see "Request your first course"
And "Moodle documentation" "link" should exist
And "Quickstart guide" "link" should exist
And "Request a course" "button" should exist
And I click on "Request a course" "button"
And I should see "Details of the course"
Scenario: Users with permissions to create a course when there is no course created
Given I am on the "My courses" page logged in as "manager"
When I should see "Create your first course"
Then "Moodle documentation" "link" should exist
And "View Quickstart guide" "button" should exist
And "Create course" "button" should exist
And I click on "Create course" "button"
And I should see "Add a new course"
Scenario: Users with permissions to create a course but is not enrolled in any existing course
Given the following "course" exists:
| fullname | Course 1 |
| shortname | C1 |
When I am on the "My courses" page logged in as "manager"
Then I should see "You're not enrolled in any course"
Then I should see "Once you're enrolled in a course, it will appear here."
And "Manage courses" "button" should exist
And "Create course" "button" should exist
And I click on "Create course" "button"
And I should see "Add a new course"
And I am on the "My courses" page
And I click on "Manage courses" "button"
And I should see "Course 1"
Scenario: Users with permissions to create but not to manage courses and is not enrolled in any existing course
Given the following "permission overrides" exist:
| capability | permission | role | contextlevel | reference |
| moodle/category:manage | Prohibit | manager | System | |
And the following "course" exists:
| fullname | Course 1 |
| shortname | C1 |
When I am on the "My courses" page logged in as "manager"
Then I should see "You're not enrolled in any course"
Then I should not see "To view all courses on this sie, go to Manage courses"
And "Manage courses" "button" should not exist
And "Create course" "button" should exist
And I click on "Create course" "button"
And I should see "Add a new course"
@accessibility
Scenario: Evaluate the accessibility of the My courses (zero state)
When I am on the "My courses" page logged in as "manager"
Then the page should meet accessibility standards
+118
View File
@@ -0,0 +1,118 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
namespace block_myoverview;
/**
* Online users testcase
*
* @package block_myoverview
* @category test
* @copyright 2019 Juan Leyva <juan@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class myoverview_test extends \advanced_testcase {
/**
* Test getting block configuration
*/
public function test_get_block_config_for_external(): void {
global $PAGE, $CFG, $OUTPUT;
require_once($CFG->dirroot . '/my/lib.php');
$this->resetAfterTest(true);
$user = $this->getDataGenerator()->create_user();
$fieldcategory = self::getDataGenerator()->create_custom_field_category(['name' => 'Other fields']);
$customfield = ['shortname' => 'test', 'name' => 'Custom field', 'type' => 'text',
'categoryid' => $fieldcategory->get('id')];
$field = self::getDataGenerator()->create_custom_field($customfield);
$customfieldvalue = ['shortname' => 'test', 'value' => 'Test value I'];
$course1 = self::getDataGenerator()->create_course(['customfields' => [$customfieldvalue]]);
$customfieldvalue = ['shortname' => 'test', 'value' => 'Test value II'];
$course2 = self::getDataGenerator()->create_course(['customfields' => [$customfieldvalue]]);
$this->getDataGenerator()->enrol_user($user->id, $course1->id, 'student');
$this->getDataGenerator()->enrol_user($user->id, $course2->id, 'student');
// Force a setting change to check the returned blocks settings.
set_config('displaygroupingcustomfield', 1, 'block_myoverview');
set_config('customfiltergrouping', $field->get('shortname'), 'block_myoverview');
$this->setUser($user);
$context = \context_user::instance($user->id);
if (!$currentpage = my_get_page($user->id, MY_PAGE_PUBLIC, MY_PAGE_COURSES)) {
throw new \moodle_exception('mymoodlesetup');
}
$PAGE->set_url('/my/courses.php'); // Need this because some internal API calls require the $PAGE url to be set.
$PAGE->set_context($context);
$PAGE->set_pagelayout('mydashboard');
$PAGE->set_pagetype('my-index');
$PAGE->blocks->add_region('content'); // Need to add this special region to retrieve the central blocks.
$PAGE->set_subpage($currentpage->id);
// Load the block instances for all the regions.
$PAGE->blocks->load_blocks();
$PAGE->blocks->create_all_block_instances();
$blocks = $PAGE->blocks->get_content_for_all_regions($OUTPUT);
$configs = null;
foreach ($blocks as $region => $regionblocks) {
$regioninstances = $PAGE->blocks->get_blocks_for_region($region);
foreach ($regioninstances as $ri) {
// Look for myoverview block only.
if ($ri->instance->blockname == 'myoverview') {
$configs = $ri->get_config_for_external();
break 2;
}
}
}
// Test we receive all we expect (exact number and values of settings).
$this->assertNotEmpty($configs);
$this->assertEmpty((array) $configs->instance);
$this->assertCount(13, (array) $configs->plugin);
$this->assertEquals('test', $configs->plugin->customfiltergrouping);
// Test default values.
$this->assertEquals(1, $configs->plugin->displaycategories);
$this->assertEquals(1, $configs->plugin->displaygroupingall);
$this->assertEquals(0, $configs->plugin->displaygroupingallincludinghidden);
$this->assertEquals(1, $configs->plugin->displaygroupingcustomfield);
$this->assertEquals(1, $configs->plugin->displaygroupingfuture);
$this->assertEquals(1, $configs->plugin->displaygroupinghidden);
$this->assertEquals(1, $configs->plugin->displaygroupinginprogress);
$this->assertEquals(1, $configs->plugin->displaygroupingpast);
$this->assertEquals(1, $configs->plugin->displaygroupingfavourites);
$this->assertEquals('card,list,summary', $configs->plugin->layouts);
$this->assertEquals(get_config('block_myoverview', 'version'), $configs->plugin->version);
// Test custom fields.
$this->assertJson($configs->plugin->customfieldsexport);
$fields = json_decode($configs->plugin->customfieldsexport);
$this->assertEquals('Test value I', $fields[0]->name);
$this->assertEquals('Test value I', $fields[0]->value);
$this->assertFalse($fields[0]->active);
$this->assertEquals('Test value II', $fields[1]->name);
$this->assertEquals('Test value II', $fields[1]->value);
$this->assertFalse($fields[1]->active);
$this->assertEquals('No Custom field', $fields[2]->name);
$this->assertFalse($fields[2]->active);
}
}
@@ -0,0 +1,111 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Unit tests for the block_myoverview implementation of the privacy API.
*
* @package block_myoverview
* @category test
* @copyright 2018 Peter Dias <peter@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace block_myoverview\privacy;
defined('MOODLE_INTERNAL') || die();
use core_privacy\local\request\writer;
use block_myoverview\privacy\provider;
/**
* Unit tests for the block_myoverview implementation of the privacy API.
*
* @copyright 2018 Peter Dias <peter@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class provider_test extends \core_privacy\tests\provider_testcase {
/**
* Ensure that export_user_preferences returns no data if the user has not visited the myoverview block.
*/
public function test_export_user_preferences_no_pref(): void {
$this->resetAfterTest();
$user = $this->getDataGenerator()->create_user();
provider::export_user_preferences($user->id);
$writer = writer::with_context(\context_system::instance());
$this->assertFalse($writer->has_any_data());
}
/**
* Test the export_user_preferences given different inputs
*
* @param string $type The name of the user preference to get/set
* @param string $value The value you are storing
*
* @dataProvider user_preference_provider
*/
public function test_export_user_preferences($type, $value, $expected): void {
$this->resetAfterTest();
$user = $this->getDataGenerator()->create_user();
set_user_preference($type, $value, $user);
provider::export_user_preferences($user->id);
$writer = writer::with_context(\context_system::instance());
$blockpreferences = $writer->get_user_preferences('block_myoverview');
if (!$expected) {
$expected = get_string($value, 'block_myoverview');
}
$this->assertEquals($expected, $blockpreferences->{$type}->value);
}
/**
* Create an array of valid user preferences for the myoverview block.
*
* @return array Array of valid user preferences.
*/
public function user_preference_provider() {
return array(
array('block_myoverview_user_sort_preference', 'lastaccessed', ''),
array('block_myoverview_user_sort_preference', 'title', ''),
array('block_myoverview_user_sort_preference', 'shortname', ''),
array('block_myoverview_user_grouping_preference', 'allincludinghidden', ''),
array('block_myoverview_user_grouping_preference', 'all', ''),
array('block_myoverview_user_grouping_preference', 'inprogress', ''),
array('block_myoverview_user_grouping_preference', 'future', ''),
array('block_myoverview_user_grouping_preference', 'past', ''),
array('block_myoverview_user_grouping_preference', 'hidden', ''),
array('block_myoverview_user_grouping_preference', 'favourites', ''),
array('block_myoverview_user_view_preference', 'card', ''),
array('block_myoverview_user_view_preference', 'list', ''),
array('block_myoverview_user_view_preference', 'summary', ''),
array('block_myoverview_user_paging_preference', 12, 12)
);
}
public function test_export_user_preferences_with_hidden_courses(): void {
$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
);
}
}
+9
View File
@@ -0,0 +1,9 @@
This file describes API changes in the myoverview block code.
=== 3.7 ===
* The 'block/myoverview:addinstance' capability has been removed. It has never been used in code.
=== 3.9 ===
* Rename setting block_myoverview->displaygroupingstarred to block_myoverview->displaygroupingfavourites.
+29
View File
@@ -0,0 +1,29 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Version details for the My overview block.
*
* @package block_myoverview
* @copyright Mark Nelson <markn@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
$plugin->version = 2024042200; // The current plugin version (Date: YYYYMMDDXX).
$plugin->requires = 2024041600; // Requires this Moodle version.
$plugin->component = 'block_myoverview'; // Full name of the plugin (used for diagnostics).