diff --git a/blocks/myoverview/amd/build/repository.min.js b/blocks/myoverview/amd/build/repository.min.js index ab272626367..92f1945f9d9 100644 --- a/blocks/myoverview/amd/build/repository.min.js +++ b/blocks/myoverview/amd/build/repository.min.js @@ -1 +1 @@ -define(["core/ajax"],function(a){var b=function(b){var c={methodname:"core_course_get_enrolled_courses_by_timeline_classification",args:b},d=a.call([c])[0];return d};return{getEnrolledCoursesByTimeline:b}}); \ No newline at end of file +define(["core/ajax","core/notification"],function(a,b){var c=function(b){var c={methodname:"core_course_get_enrolled_courses_by_timeline_classification",args:b},d=a.call([c])[0];return d},d=function(b){var c={methodname:"core_course_set_favourite_courses",args:b},d=a.call([c])[0];return d},e=function(c){var d={methodname:"core_user_update_user_preferences",args:c};a.call([d])[0].fail(b.exception)};return{getEnrolledCoursesByTimeline:c,setFavouriteCourses:d,updateUserPreferences:e}}); \ No newline at end of file diff --git a/blocks/myoverview/amd/build/view.min.js b/blocks/myoverview/amd/build/view.min.js index 1d9b2147efd..5f3fb07be37 100644 --- a/blocks/myoverview/amd/build/view.min.js +++ b/blocks/myoverview/amd/build/view.min.js @@ -1 +1 @@ -define(["jquery","core/notification","block_myoverview/repository","core/paged_content_factory","core/templates"],function(a,b,c,d,e){var f={COURSES_CARDS:"block_myoverview/view-cards",COURSES_LIST:"block_myoverview/view-list",COURSES_SUMMARY:"block_myoverview/view-summary",NOCOURSES:"block_myoverview/no-courses"},g=[12,24],h=[],i=function(a){var b={};return b.display=a.attr("data-display"),b.grouping=a.attr("data-grouping"),b.sort=a.attr("data-sort"),b},j={ignoreControlWhileLoading:!0,controlPlacementBottom:!0},k=function(a,b,d){return c.getEnrolledCoursesByTimeline({offset:d*b,limit:b,classification:a.grouping,sort:a.sort})},l=function(a,b,c){var d="";if(d="cards"==c.display?f.COURSES_CARDS:"list"==c.display?f.COURSES_LIST:f.COURSES_SUMMARY,b.courses.length)return e.render(d,{courses:b.courses});var g=a.attr("data-nocoursesimg");return e.render(f.NOCOURSES,{nocoursesimg:g})},m=function(c,f){c=a(c);var m=i(c),n=d.createWithLimit(g,function(a,d){var e=[];return a.forEach(function(a){var f=a.pageNumber-1,g=k(m,a.limit,f).then(function(b){return b.courses.length0?j.forEach(function(b,c){var d=o(a,c);x(a,b).then(function(a,b){return f.replaceNodeContents(d,a,b)})["catch"](e.exception)}):y(a,b)};return{init:y,reset:A}}); \ No newline at end of file diff --git a/blocks/myoverview/amd/build/view_nav.min.js b/blocks/myoverview/amd/build/view_nav.min.js index 978c770879f..7af70de6dd7 100644 --- a/blocks/myoverview/amd/build/view_nav.min.js +++ b/blocks/myoverview/amd/build/view_nav.min.js @@ -1 +1 @@ -define(["jquery","core/custom_interaction_events","block_myoverview/view"],function(a,b,c){var d={FILTERS:'[data-region="filter"]',FILTER_OPTION:"[data-filter]",DISPLAY_OPTION:"[data-display-option]"},e=function(e,f,g){var h=e.find(d.FILTERS);b.define(h,[b.events.activate]),h.on(b.events.activate,d.FILTER_OPTION,function(b,d){var e=a(b.target);if(!e.hasClass("active")){var h="data-"+e.attr("data-filter");f.attr(h,e.attr("data-value")),c.init(f,g),d.originalEvent.preventDefault()}}),b.define(h,[b.events.activate]),h.on(b.events.activate,d.DISPLAY_OPTION,function(b,d){var e=a(b.target);e.hasClass("active")||(f.attr("data-display",e.attr("data-value")),c.reset(f,g),d.originalEvent.preventDefault())})},f=function(b,c,d){b=a(b),e(b,c,d)};return{init:f}}); \ No newline at end of file +define(["jquery","core/custom_interaction_events","block_myoverview/repository","block_myoverview/view"],function(a,b,c,d){var e={FILTERS:'[data-region="filter"]',FILTER_OPTION:"[data-filter]",DISPLAY_OPTION:"[data-display-option]"},f=function(a,b){var d=null;d="display"==a?"block_myoverview_user_view_preference":"sort"==a?"block_myoverview_user_sort_preference":"block_myoverview_user_grouping_preference",c.updateUserPreferences({preferences:[{type:d,value:b}]})},g=function(c,g,h){var i=c.find(e.FILTERS);b.define(i,[b.events.activate]),i.on(b.events.activate,e.FILTER_OPTION,function(b,c){var e=a(b.target);if(!e.hasClass("active")){var i=e.attr("data-filter"),j="data-"+i,k=e.attr("data-value"),l=e.attr("data-pref");g.attr(j,k),f(i,l),d.init(g,h),c.originalEvent.preventDefault()}}),b.define(i,[b.events.activate]),i.on(b.events.activate,e.DISPLAY_OPTION,function(b,c){var e=a(b.target);if(!e.hasClass("active")){var i=e.attr("data-display-option"),j=e.attr("data-value"),k=e.attr("data-pref");f(i,k),g.attr("data-display",j),d.reset(g,h),c.originalEvent.preventDefault()}})},h=function(b,c,d){b=a(b),g(b,c,d)};return{init:h}}); \ No newline at end of file diff --git a/blocks/myoverview/amd/src/repository.js b/blocks/myoverview/amd/src/repository.js index b4f7b5d83e5..412c3e335ab 100644 --- a/blocks/myoverview/amd/src/repository.js +++ b/blocks/myoverview/amd/src/repository.js @@ -20,7 +20,7 @@ * @copyright 2018 Bas Brands * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -define(['core/ajax'], function(Ajax) { +define(['core/ajax', 'core/notification'], function(Ajax, Notification) { /** * Retrieve a list of enrolled courses. @@ -47,7 +47,55 @@ define(['core/ajax'], function(Ajax) { return promise; }; + /** + * 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. + */ + var setFavouriteCourses = function(args) { + + var request = { + methodname: 'core_course_set_favourite_courses', + args: args + }; + + var promise = Ajax.call([request])[0]; + + return promise; + }; + + /** + * Update the user preferences. + * + * @param {Object} args Arguments send to the webservice. + * + * Sample args: + * { + * preferences: [ + * { + * type: 'block_example_user_sort_preference' + * value: 'title' + * } + * ] + * } + */ + var updateUserPreferences = function(args) { + var request = { + methodname: 'core_user_update_user_preferences', + args: args + }; + + Ajax.call([request])[0] + .fail(Notification.exception); + }; + return { - getEnrolledCoursesByTimeline: getEnrolledCoursesByTimeline + getEnrolledCoursesByTimeline: getEnrolledCoursesByTimeline, + setFavouriteCourses: setFavouriteCourses, + updateUserPreferences: updateUserPreferences }; }); diff --git a/blocks/myoverview/amd/src/view.js b/blocks/myoverview/amd/src/view.js index 5f3f15ac975..21e86d23aff 100644 --- a/blocks/myoverview/amd/src/view.js +++ b/blocks/myoverview/amd/src/view.js @@ -24,19 +24,31 @@ define( [ 'jquery', - 'core/notification', 'block_myoverview/repository', 'core/paged_content_factory', + 'core/custom_interaction_events', + 'core/notification', 'core/templates', ], function( $, - Notification, Repository, PagedContentFactory, + CustomEvents, + Notification, Templates ) { + var SELECTORS = { + ACTION_ADD_FAVOURITE: '[data-action="add-favourite"]', + ACTION_REMOVE_FAVOURITE: '[data-action="remove-favourite"]', + FAVOURITE_ICON: '[data-region="favourite-icon"]', + ICON_IS_FAVOURITE: '[data-region="is-favourite"]', + ICON_NOT_FAVOURITE: '[data-region="not-favourite"]', + PAGED_CONTENT_CONTAINER: '[data-region="page-container"]' + + }; + var TEMPLATES = { COURSES_CARDS: 'block_myoverview/view-cards', COURSES_LIST: 'block_myoverview/view-list', @@ -44,9 +56,9 @@ function( NOCOURSES: 'block_myoverview/no-courses' }; - var NUMCOURSES_PERPAGE = [12, 24]; + var NUMCOURSES_PERPAGE = [12, 24, 48]; - var currentCourseList = []; + var loadedPages = []; /** * Get filter values from DOM. @@ -62,7 +74,7 @@ function( return filters; }; - // We want the paged content controls below the paged content area + // We want the paged content controls below the paged content area. // and the controls should be ignored while data is loading. var DEFAULT_PAGED_CONTENT_CONFIG = { ignoreControlWhileLoading: true, @@ -75,9 +87,10 @@ function( * @param {object} filters The filters for this view. * @param {int} limit The number of courses to show. * @param {int} pageNumber The pagenumber to view. - * @return {promise} Resolved with an array of courses. + * @return {promise|Array} Resolved with an array of courses. */ var getMyCourses = function(filters, limit, pageNumber) { + return Repository.getEnrolledCoursesByTimeline({ offset: pageNumber * limit, limit: limit, @@ -86,15 +99,178 @@ function( }); }; + /** + * 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 + */ + var getFavouriteIconContainer = function(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. + */ + var getPagedContentContainer = function(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. + */ + var getFavouriteCourseId = function(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. + */ + var hideFavouriteIcon = function(root, courseId) { + var iconContainer = getFavouriteIconContainer(root, courseId); + var isFavouriteIcon = iconContainer.find(SELECTORS.ICON_IS_FAVOURITE); + isFavouriteIcon.addClass('hidden'); + isFavouriteIcon.attr('aria-hidden', true); + var notFavourteIcon = iconContainer.find(SELECTORS.ICON_NOT_FAVOURITE); + notFavourteIcon.removeClass('hidden'); + notFavourteIcon.attr('aria-hidden', false); + }; + + /** + * Show the favourite icon. + * + * @param {Object} root The course overview container. + * @param {Number} courseId Course id number. + */ + var showFavouriteIcon = function(root, courseId) { + var iconContainer = getFavouriteIconContainer(root, courseId); + var isFavouriteIcon = iconContainer.find(SELECTORS.ICON_IS_FAVOURITE); + isFavouriteIcon.removeClass('hidden'); + isFavouriteIcon.attr('aria-hidden', false); + var notFavourteIcon = iconContainer.find(SELECTORS.ICON_NOT_FAVOURITE); + notFavourteIcon.addClass('hidden'); + notFavourteIcon.attr('aria-hidden', true); + }; + + /** + * Get the action menu item + * + * @param {Object} root root The course overview container + * @param {Number} courseId Course id. + * @return {Object} The add to favourite menu item. + */ + var getAddFavouriteMenuItem = function(root, courseId) { + return root.find('[data-action="add-favourite"][data-course-id="' + courseId + '"]'); + }; + + /** + * Get the action menu item + * + * @param {Object} root root The course overview container + * @param {Number} courseId Course id. + * @return {Object} The remove from favourites menu item. + */ + var getRemoveFavouriteMenuItem = function(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 + */ + var addToFavourites = function(root, courseId) { + var removeAction = getRemoveFavouriteMenuItem(root, courseId); + var addAction = getAddFavouriteMenuItem(root, courseId); + + setCourseFavouriteState(courseId, true).then(function(success) { + if (success) { + 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 + */ + var removeFromFavourites = function(root, courseId) { + var removeAction = getRemoveFavouriteMenuItem(root, courseId); + var addAction = getAddFavouriteMenuItem(root, courseId); + + setCourseFavouriteState(courseId, false).then(function(success) { + if (success) { + removeAction.addClass('hidden'); + addAction.removeClass('hidden'); + hideFavouriteIcon(root, courseId); + } else { + Notification.alert('Starring course failed', 'Could not change favourite state'); + } + return; + }).catch(Notification.exception); + }; + + /** + * Set the courses favourite status and push to repository + * + * @param {Number} courseId Course id to favourite. + * @param {Bool} status new favourite status. + * @return {Promise} Repository promise. + */ + var setCourseFavouriteState = function(courseId, status) { + + return Repository.setFavouriteCourses({ + courses: [ + { + 'id': courseId, + 'favourite': status + } + ] + }).then(function(result) { + if (result.warnings.length == 0) { + loadedPages.forEach(function(courseList) { + courseList.courses.forEach(function(course, index) { + if (course.id == courseId) { + courseList.courses[index].isfavourite = status; + } + }); + }); + return true; + } else { + return false; + } + }).catch(Notification.exception); + }; + /** * Render the dashboard courses. * * @param {object} root The root element for the courses view. * @param {array} coursesData containing array of returned courses. - * @param {object} filters The filters for this view. * @return {promise} jQuery promise resolved after rendering is complete. */ - var renderCourses = function(root, coursesData, filters) { + var renderCourses = function(root, coursesData) { + + var filters = getFilterValues(root); var currentTemplate = ''; if (filters.display == 'cards') { @@ -127,6 +303,11 @@ function( root = $(root); + if (!root.attr('data-init')) { + registerEventListeners(root); + root.attr('data-init', true); + } + var filters = getFilterValues(root); var pagedContentPromise = PagedContentFactory.createWithLimit( @@ -135,6 +316,7 @@ function( var promises = []; pagesData.forEach(function(pageData) { + var currentPage = pageData.pageNumber; var pageNumber = pageData.pageNumber - 1; var pagePromise = getMyCourses( @@ -145,8 +327,8 @@ function( if (coursesData.courses.length < pageData.limit) { actions.allItemsLoaded(pageData.pageNumber); } - currentCourseList = coursesData; - return renderCourses(root, coursesData, filters); + loadedPages[currentPage] = coursesData; + return renderCourses(root, coursesData); }) .catch(Notification.exception); @@ -163,6 +345,35 @@ function( }).catch(Notification.exception); }; + /** + * Listen to, and handle events for the myoverview block. + * + * @param {Object} root The myoverview block container element. + */ + var registerEventListeners = function(root) { + CustomEvents.define(root, [ + CustomEvents.events.activate + ]); + + root.on(CustomEvents.events.activate, SELECTORS.ACTION_ADD_FAVOURITE, function(e, data) { + var favourite = $(e.target).closest(SELECTORS.ACTION_ADD_FAVOURITE); + var courseId = getFavouriteCourseId(favourite); + addToFavourites(root, courseId); + data.originalEvent.preventDefault(); + }); + + root.on(CustomEvents.events.activate, SELECTORS.ACTION_REMOVE_FAVOURITE, function(e, data) { + var favourite = $(e.target).closest(SELECTORS.ACTION_REMOVE_FAVOURITE); + var courseId = getFavouriteCourseId(favourite); + removeFromFavourites(root, courseId); + data.originalEvent.preventDefault(); + }); + + root.on(CustomEvents.events.activate, SELECTORS.FAVOURITE_ICON, function(e, data) { + data.originalEvent.preventDefault(); + }); + }; + /** * Reset the courses views to their original * state on first page load. @@ -170,15 +381,21 @@ function( * This is called when configuration has changed for the event lists * to cause them to reload their data. * - * @param {object} root The root element for the timeline view. - * @param {object} content The content element for the timeline view. + * @param {Object} root The root element for the timeline view. + * @param {Object} content The content element for the timeline view. */ var reset = function(root, content) { - var filters = getFilterValues(root); - renderCourses(root, currentCourseList, filters) - .then(function(html, js) { - return Templates.replaceNodeContents(content, html, js); - }).catch(Notification.exception); + + if (loadedPages.length > 0) { + loadedPages.forEach(function(courseList, index) { + var pagedContentPage = getPagedContentContainer(root, index); + renderCourses(root, courseList).then(function(html, js) { + return Templates.replaceNodeContents(pagedContentPage, html, js); + }).catch(Notification.exception); + }); + } else { + init(root, content); + } }; return { diff --git a/blocks/myoverview/amd/src/view_nav.js b/blocks/myoverview/amd/src/view_nav.js index 42f5e33bd49..49f211d5028 100644 --- a/blocks/myoverview/amd/src/view_nav.js +++ b/blocks/myoverview/amd/src/view_nav.js @@ -25,11 +25,13 @@ define( [ 'jquery', 'core/custom_interaction_events', + 'block_myoverview/repository', 'block_myoverview/view' ], function( $, CustomEvents, + Repository, View ) { @@ -39,6 +41,32 @@ function( DISPLAY_OPTION: '[data-display-option]' }; + /** + * Update the user preference for the block. + * + * @param {String} filter The type of filter: display/sort/grouping. + * @param {String} value The current preferred value. + */ + var updatePreferences = function(filter, value) { + var type = null; + if (filter == 'display') { + type = 'block_myoverview_user_view_preference'; + } else if (filter == 'sort') { + type = 'block_myoverview_user_sort_preference'; + } else { + type = 'block_myoverview_user_grouping_preference'; + } + + Repository.updateUserPreferences({ + preferences: [ + { + type: type, + value: value + } + ] + }); + }; + /** * Event listener for the Display filter (cards, list). * @@ -62,8 +90,14 @@ function( return; } - var attributename = 'data-' + option.attr('data-filter'); - viewRoot.attr(attributename, option.attr('data-value')); + var filter = option.attr('data-filter'); + var attributename = 'data-' + filter; + var value = option.attr('data-value'); + var pref = option.attr('data-pref'); + + viewRoot.attr(attributename, value); + + updatePreferences(filter, pref); // Reset the views. View.init(viewRoot, viewContent); @@ -83,7 +117,12 @@ function( return; } - viewRoot.attr('data-display', option.attr('data-value')); + var filter = option.attr('data-display-option'); + var value = option.attr('data-value'); + var pref = option.attr('data-pref'); + + updatePreferences(filter, pref); + viewRoot.attr('data-display', value); View.reset(viewRoot, viewContent); data.originalEvent.preventDefault(); } diff --git a/blocks/myoverview/block_myoverview.php b/blocks/myoverview/block_myoverview.php index f22ce15decb..cbd2f9db53c 100644 --- a/blocks/myoverview/block_myoverview.php +++ b/blocks/myoverview/block_myoverview.php @@ -49,8 +49,11 @@ class block_myoverview extends block_base { 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'); - $renderable = new \block_myoverview\output\main(); + $renderable = new \block_myoverview\output\main($group, $sort, $view); $renderer = $this->page->get_renderer('block_myoverview'); $this->content = new stdClass(); diff --git a/blocks/myoverview/classes/output/main.php b/blocks/myoverview/classes/output/main.php index c734468c192..7cd6d1e0c9a 100644 --- a/blocks/myoverview/classes/output/main.php +++ b/blocks/myoverview/classes/output/main.php @@ -28,7 +28,7 @@ use renderable; use renderer_base; use templatable; -require_once($CFG->libdir . '/completionlib.php'); +require_once($CFG->dirroot . '/blocks/myoverview/lib.php'); /** * Class containing data for my overview block. @@ -37,18 +37,75 @@ require_once($CFG->libdir . '/completionlib.php'); * @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; + + /** + * main constructor. + * Initialize the user preferences + * + * @param string $grouping Grouping user preference + * @param string $sort Sort user preference + * @param string $view Display user preference + */ + public function __construct($grouping, $sort, $view) { + $this->grouping = $grouping ? $grouping : BLOCK_MYOVERVIEW_GROUPING_ALL; + $this->sort = $sort ? $sort : BLOCK_MYOVERVIEW_SORTING_TITLE; + $this->view = $view ? $view : 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->view] = true; + $preferences[$this->sort] = true; + $preferences[$this->grouping] = true; + + return $preferences; + } + /** * Export this data so it can be used as the context for a mustache template. * * @param \renderer_base $output - * @return stdClass + * @return array Context variables for the template */ public function export_for_template(renderer_base $output) { $nocoursesurl = $output->image_url('courses', 'block_myoverview')->out(); - return (object) [ - 'nocoursesimg' => $nocoursesurl + $defaultvariables = [ + 'nocoursesimg' => $nocoursesurl, + 'grouping' => $this->grouping, + 'sort' => $this->sort == BLOCK_MYOVERVIEW_SORTING_TITLE ? 'fullname' : 'ul.timeaccess desc', + 'view' => $this->view ]; + + $preferences = $this->get_preferences_as_booleans(); + return array_merge($defaultvariables, $preferences); + } -} +} \ No newline at end of file diff --git a/blocks/myoverview/classes/privacy/provider.php b/blocks/myoverview/classes/privacy/provider.php index b3cf042afef..34463a101ab 100644 --- a/blocks/myoverview/classes/privacy/provider.php +++ b/blocks/myoverview/classes/privacy/provider.php @@ -24,6 +24,9 @@ namespace block_myoverview\privacy; +use core_privacy\local\request\user_preference_provider; +use core_privacy\local\metadata\collection; + defined('MOODLE_INTERNAL') || die(); /** @@ -32,15 +35,48 @@ defined('MOODLE_INTERNAL') || die(); * @copyright 2018 Zig Tan * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -class provider implements \core_privacy\local\metadata\null_provider { +class provider implements \core_privacy\local\metadata\provider, user_preference_provider { /** - * Get the language string identifier with the component's language - * file to explain why this plugin stores no data. + * Returns meta-data information about the myoverview block. * - * @return string + * @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_reason() : string { - return 'privacy:metadata'; + 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'); + 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)) { + \core_privacy\local\request\writer::export_user_preference('block_myoverview', + 'block_myoverview_user_sort_preference', get_string($preference, 'block_myoverview'), + get_string('privacy:metadata:overviewsortpreference', 'block_myoverview')); + } + + $preference = get_user_preferences('block_myoverview_user_view_preference', null, $userid); + if (isset($preference)) { + \core_privacy\local\request\writer::export_user_preference('block_myoverview', + '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)) { + \core_privacy\local\request\writer::export_user_preference('block_myoverview', + 'block_myoverview_user_grouping_preference', + get_string($preference, 'block_myoverview'), + get_string('privacy:metadata:overviewgroupingpreference', 'block_myoverview')); + } + } +} \ No newline at end of file diff --git a/blocks/myoverview/lang/en/block_myoverview.php b/blocks/myoverview/lang/en/block_myoverview.php index 49fe21c2907..6de059d056d 100644 --- a/blocks/myoverview/lang/en/block_myoverview.php +++ b/blocks/myoverview/lang/en/block_myoverview.php @@ -23,14 +23,19 @@ */ $string['all'] = 'All'; +$string['addtofavourites'] = 'Star this course'; +$string['aria:addtofavourites'] = 'Star for'; $string['aria:allcourses'] = 'All courses'; $string['aria:card'] = 'Switch to card view'; $string['aria:controls'] = 'Course overview controls'; +$string['aria:courseactions'] = 'Actions for current course'; $string['aria:courseimage'] = 'Course image:'; $string['aria:coursename'] = 'Course name:'; $string['aria:coursesummary'] = 'Course summary text:'; $string['aria:courseprogress'] = 'Course progress:'; $string['aria:displaydropdown'] = 'Display dropdown'; +$string['aria:favourite'] = 'Course is starred'; +$string['aria:favourites'] = 'Show starred courses'; $string['aria:future'] = 'Show future courses'; $string['aria:groupingdropdown'] = 'Grouping dropdown'; $string['aria:inprogress'] = 'Show in courses in progress'; @@ -38,32 +43,45 @@ $string['aria:lastaccessed'] = 'Sort courses by last accessed date'; $string['aria:list'] = 'Switch to list view'; $string['aria:title'] = 'Sort courses by title'; $string['aria:past'] = 'Show past courses'; +$string['aria:removefromfavourites'] = 'Remove star for'; $string['aria:summary'] = 'Switch to summary view'; $string['aria:sortingdropdown'] = 'Sorting dropdown'; $string['card'] = 'Card'; -$string['courseprogress'] = 'Course progress:'; +$string['cards'] = 'Cards'; $string['complete'] = 'Complete'; -$string['favorite'] = 'Favorite'; +$string['favourite'] = 'Starred course'; +$string['favourites'] = 'Starred'; $string['future'] = 'Future'; -$string['future:aria'] = 'View future courses'; -$string['hidden'] = 'Hidden'; $string['inprogress'] = 'In progress'; -$string['inprogress:aria'] = 'View in progress courses'; +$string['notfavourite'] = 'Not starred'; $string['lastaccessed'] = 'Last accessed'; -$string['lastaccessed:aria'] = 'Sort course by lastaccessed'; $string['list'] = 'List'; -$string['morecourses'] = 'More courses'; $string['myoverview:addinstance'] = 'Add a new course overview block'; $string['myoverview:myaddinstance'] = 'Add a new course overview block to Dashboard'; -$string['nocoursesfuture'] = 'No future courses'; -$string['nocoursesinprogress'] = 'No in progress courses'; $string['nocourses'] = 'No courses'; -$string['nocoursespast'] = 'No past courses'; $string['past'] = 'Past'; $string['pluginname'] = 'Course overview'; -$string['privacy:metadata'] = 'The myoverview block does not store any personal data.'; +$string['privacy:metadata:overviewsortpreference'] = 'The myoverview block sort preference.'; +$string['privacy:metadata:overviewviewpreference'] = 'The myoverview block view preference.'; +$string['privacy:metadata:overviewgroupingpreference'] = 'The myoverview block grouping preference.'; +$string['removefromfavourites'] = 'Unstar this course'; $string['summary'] = 'Summary'; $string['title'] = 'Title'; + +// Deprecated since Moodle 3.6. +$string['defaulttab'] = 'Default tab'; +$string['defaulttab_desc'] = 'The tab that will be displayed when a user first views their course overview. When returning to their course overview, the user\'s active tab is remembered.'; +$string['morecourses'] = 'More courses'; +$string['nocoursesinprogress'] = 'No in progress courses'; +$string['nocoursesfuture'] = 'No future courses'; +$string['nocoursespast'] = 'No past courses'; +$string['noevents'] = 'No upcoming activities due'; +$string['next30days'] = 'Next 30 days'; +$string['next7days'] = 'Next 7 days'; +$string['recentlyoverdue'] = 'Recently overdue'; +$string['sortbycourses'] = 'Sort by courses'; +$string['sortbydates'] = 'Sort by dates'; +$string['timeline'] = 'Timeline'; $string['viewcoursename'] = 'View course {$a}'; -$string['viewcourse'] = 'View course'; +$string['privacy:metadata:overviewlasttab'] = 'This stores the last tab selected by the user on the overview block.'; diff --git a/blocks/myoverview/lang/en/deprecated.txt b/blocks/myoverview/lang/en/deprecated.txt new file mode 100644 index 00000000000..7927bbae523 --- /dev/null +++ b/blocks/myoverview/lang/en/deprecated.txt @@ -0,0 +1,15 @@ +defaulttab,block_myoverview +defaulttab_desc,block_myoverview +morecourses,block_myoverview +nocoursesinprogress,block_myoverview +nocoursesfuture,block_myoverview +nocoursespast,block_myoverview +noevents,block_myoverview +next30days,block_myoverview +next7days,block_myoverview +recentlyoverdue,block_myoverview +sortbycourses,block_myoverview +sortbydates,block_myoverview +timeline,block_myoverview +viewcoursename,block_myoverview +privacy:metadata:overviewlasttab,block_myoverview \ No newline at end of file diff --git a/blocks/myoverview/lib.php b/blocks/myoverview/lib.php new file mode 100644 index 00000000000..bc0530e19ac --- /dev/null +++ b/blocks/myoverview/lib.php @@ -0,0 +1,88 @@ +. + +/** + * 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_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'); + +/** + * Constants for the user preferences sorting options + * timeline + */ +define('BLOCK_MYOVERVIEW_SORTING_TITLE', 'title'); +define('BLOCK_MYOVERVIEW_SORTING_LASTACCESSED', 'lastaccessed'); + +/** + * Constants for the user preferences view options + */ +define('BLOCK_MYOVERVIEW_VIEW_CARD', 'cards'); +define('BLOCK_MYOVERVIEW_VIEW_LIST', 'list'); +define('BLOCK_MYOVERVIEW_VIEW_SUMMARY', 'summary'); + +/** + * Get the current user preferences that are available + * + * @return mixed Array representing current options along with defaults + */ +function block_myoverview_user_preferences() { + $preferences['block_myoverview_user_grouping_preference'] = array( + 'null' => NULL_NOT_ALLOWED, + 'default' => BLOCK_MYOVERVIEW_GROUPING_ALL, + 'type' => PARAM_ALPHA, + 'choices' => array( + BLOCK_MYOVERVIEW_GROUPING_ALL, + BLOCK_MYOVERVIEW_GROUPING_INPROGRESS, + BLOCK_MYOVERVIEW_GROUPING_FUTURE, + BLOCK_MYOVERVIEW_GROUPING_PAST, + BLOCK_MYOVERVIEW_GROUPING_FAVOURITES + ) + ); + $preferences['block_myoverview_user_sort_preference'] = array( + 'null' => NULL_NOT_ALLOWED, + 'default' => BLOCK_MYOVERVIEW_SORTING_TITLE, + 'type' => PARAM_ALPHA, + 'choices' => array( + BLOCK_MYOVERVIEW_SORTING_TITLE, + BLOCK_MYOVERVIEW_SORTING_LASTACCESSED + ) + ); + $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 + ) + ); + return $preferences; +} \ No newline at end of file diff --git a/blocks/myoverview/templates/course-action-menu.mustache b/blocks/myoverview/templates/course-action-menu.mustache new file mode 100644 index 00000000000..027bdab3ded --- /dev/null +++ b/blocks/myoverview/templates/course-action-menu.mustache @@ -0,0 +1,61 @@ +{{! + 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 . +}} +{{! + @template block_myoverview/course-action-menu + + This template renders action menu for each course. + + Example context (json): + { + "isfavourite": true + } +}} + \ No newline at end of file diff --git a/blocks/myoverview/templates/courses-view.mustache b/blocks/myoverview/templates/courses-view.mustache index ab97aa4b5e3..d8f2aa4ad79 100644 --- a/blocks/myoverview/templates/courses-view.mustache +++ b/blocks/myoverview/templates/courses-view.mustache @@ -21,18 +21,21 @@ Example context (json): { - "nocoursesimg": "https://moodlesite/theme/image.php/boost/block_myoverview/1535727318/courses" + "nocoursesimg": "https://moodlesite/theme/image.php/boost/block_myoverview/1535727318/courses", + "grouping": "all", + "sort": "fullname", + "view": "card" } }}
-
+
{{> block_myoverview/placeholder-course }} {{> block_myoverview/placeholder-course }} {{> block_myoverview/placeholder-course }} @@ -40,4 +43,4 @@
-
+
\ No newline at end of file diff --git a/blocks/myoverview/templates/favourite-icon.mustache b/blocks/myoverview/templates/favourite-icon.mustache new file mode 100644 index 00000000000..69d4f81b322 --- /dev/null +++ b/blocks/myoverview/templates/favourite-icon.mustache @@ -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 . +}} +{{! + @template block_myoverview/favourite-icon + + This template renders the favourite icon for a course. + + Example context (json): + { + "id": 3, + "isfavourite": true + } +}} +
+ +
\ No newline at end of file diff --git a/blocks/myoverview/templates/main.mustache b/blocks/myoverview/templates/main.mustache index 1d593e8da46..00a51d0f583 100644 --- a/blocks/myoverview/templates/main.mustache +++ b/blocks/myoverview/templates/main.mustache @@ -25,7 +25,7 @@