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
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1,376 @@
// 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 Recently accessed courses block.
*
* @module block_recentlyaccessedcourses/main
* @copyright 2018 Victor Deniz <victor@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
define(
[
'jquery',
'core/custom_interaction_events',
'core/notification',
'core/pubsub',
'core/paged_content_paging_bar',
'core/templates',
'core_course/events',
'core_course/repository',
'core/aria',
],
function(
$,
CustomEvents,
Notification,
PubSub,
PagedContentPagingBar,
Templates,
CourseEvents,
CoursesRepository,
Aria
) {
// Constants.
var NUM_COURSES_TOTAL = 10;
var SELECTORS = {
BLOCK_CONTAINER: '[data-region="recentlyaccessedcourses"]',
CARD_CONTAINER: '[data-region="card-deck"]',
COURSE_IS_FAVOURITE: '[data-region="is-favourite"]',
CONTENT: '[data-region="view-content"]',
EMPTY_MESSAGE: '[data-region="empty-message"]',
LOADING_PLACEHOLDER: '[data-region="loading-placeholder"]',
PAGING_BAR: '[data-region="paging-bar"]',
PAGING_BAR_NEXT: '[data-control="next"]',
PAGING_BAR_PREVIOUS: '[data-control="previous"]'
};
// Module variables.
var contentLoaded = false;
var allCourses = [];
var visibleCoursesId = null;
var cardWidth = null;
var viewIndex = 0;
var availableVisibleCards = 1;
/**
* Show the empty message when no course are found.
*
* @param {object} root The root element for the courses view.
*/
var showEmptyMessage = function(root) {
root.find(SELECTORS.EMPTY_MESSAGE).removeClass('hidden');
root.find(SELECTORS.LOADING_PLACEHOLDER).addClass('hidden');
root.find(SELECTORS.CONTENT).addClass('hidden');
};
/**
* Show the empty message when no course are found.
*
* @param {object} root The root element for the courses view.
*/
var showContent = function(root) {
root.find(SELECTORS.CONTENT).removeClass('hidden');
root.find(SELECTORS.EMPTY_MESSAGE).addClass('hidden');
root.find(SELECTORS.LOADING_PLACEHOLDER).addClass('hidden');
};
/**
* Show the paging bar.
*
* @param {object} root The root element for the courses view.
*/
var showPagingBar = function(root) {
var pagingBar = root.find(SELECTORS.PAGING_BAR);
pagingBar.css('opacity', 1);
pagingBar.css('visibility', 'visible');
Aria.unhide(pagingBar);
};
/**
* Hide the paging bar.
*
* @param {object} root The root element for the courses view.
*/
var hidePagingBar = function(root) {
var pagingBar = root.find(SELECTORS.PAGING_BAR);
pagingBar.css('opacity', 0);
pagingBar.css('visibility', 'hidden');
Aria.hide(pagingBar);
};
/**
* Show the favourite indicator for the given course (if it's in the list).
*
* @param {object} root The root element for the courses view.
* @param {number} courseId The id of the course to be favourited.
*/
var favouriteCourse = function(root, courseId) {
allCourses.forEach(function(course) {
if (course.attr('data-course-id') == courseId) {
course.find(SELECTORS.COURSE_IS_FAVOURITE).removeClass('hidden');
}
});
};
/**
* Hide the favourite indicator for the given course (if it's in the list).
*
* @param {object} root The root element for the courses view.
* @param {number} courseId The id of the course to be unfavourited.
*/
var unfavouriteCourse = function(root, courseId) {
allCourses.forEach(function(course) {
if (course.attr('data-course-id') == courseId) {
course.find(SELECTORS.COURSE_IS_FAVOURITE).addClass('hidden');
}
});
};
/**
* Render the a list of courses.
*
* @param {array} courses containing array of courses.
* @return {promise} Resolved with list of rendered courses as jQuery objects.
*/
var renderAllCourses = function(courses) {
var showcoursecategory = $(SELECTORS.BLOCK_CONTAINER).data('displaycoursecategory');
var promises = courses.map(function(course) {
course.showcoursecategory = showcoursecategory;
return Templates.render('block_recentlyaccessedcourses/course-card', course);
});
return $.when.apply(null, promises).then(function() {
var renderedCourses = [];
promises.forEach(function(promise) {
promise.then(function(html) {
renderedCourses.push($(html));
return;
})
.catch(Notification.exception);
});
return renderedCourses;
});
};
/**
* Fetch user's recently accessed courses and reload the content of the block.
*
* @param {int} userid User whose courses will be shown
* @returns {promise} The updated content for the block.
*/
var loadContent = function(userid) {
return CoursesRepository.getLastAccessedCourses(userid, NUM_COURSES_TOTAL)
.then(function(courses) {
return renderAllCourses(courses);
});
};
/**
* Recalculate the number of courses that should be visible.
*
* @param {object} root The root element for the courses view.
*/
var recalculateVisibleCourses = function(root) {
var container = root.find(SELECTORS.CONTENT).find(SELECTORS.CARD_CONTAINER);
var availableWidth = parseFloat(root.css('width'));
var numberOfCourses = allCourses.length;
var start = 0;
if (!cardWidth) {
container.html(allCourses[0]);
// Render one card initially to calculate the width of the cards
// including the margins.
cardWidth = allCourses[0].outerWidth(true);
}
availableVisibleCards = Math.floor(availableWidth / cardWidth);
if (viewIndex + availableVisibleCards < numberOfCourses) {
start = viewIndex;
} else {
var overflow = (viewIndex + availableVisibleCards) - numberOfCourses;
start = viewIndex - overflow;
start = start >= 0 ? start : 0;
}
// At least show one card.
if (availableVisibleCards === 0) {
availableVisibleCards = 1;
}
var coursesToShow = allCourses.slice(start, start + availableVisibleCards);
// Create an id for the list of courses we expect to be displayed.
var newVisibleCoursesId = coursesToShow.reduce(function(carry, course) {
return carry + course.attr('data-course-id');
}, '');
// Centre the courses if we have an overflow of courses.
if (allCourses.length > coursesToShow.length) {
container.addClass('justify-content-center');
container.removeClass('justify-content-start');
} else {
container.removeClass('justify-content-center');
container.addClass('justify-content-start');
}
// Don't bother updating the DOM unless the visible courses have changed.
if (visibleCoursesId != newVisibleCoursesId) {
var pagingBar = root.find(PagedContentPagingBar.rootSelector);
container.html(coursesToShow);
visibleCoursesId = newVisibleCoursesId;
if (availableVisibleCards >= allCourses.length) {
hidePagingBar(root);
} else {
showPagingBar(root);
if (viewIndex === 0) {
PagedContentPagingBar.disablePreviousControlButtons(pagingBar);
} else {
PagedContentPagingBar.enablePreviousControlButtons(pagingBar);
}
if (viewIndex + availableVisibleCards >= allCourses.length) {
PagedContentPagingBar.disableNextControlButtons(pagingBar);
} else {
PagedContentPagingBar.enableNextControlButtons(pagingBar);
}
}
}
};
/**
* Register event listeners for the block.
*
* @param {object} root The root element for the recentlyaccessedcourses block.
*/
var registerEventListeners = function(root) {
var resizeTimeout = null;
var drawerToggling = false;
PubSub.subscribe(CourseEvents.favourited, function(courseId) {
favouriteCourse(root, courseId);
});
PubSub.subscribe(CourseEvents.unfavorited, function(courseId) {
unfavouriteCourse(root, courseId);
});
PubSub.subscribe('nav-drawer-toggle-start', function() {
if (!contentLoaded || !allCourses.length || drawerToggling) {
// Nothing to recalculate.
return;
}
drawerToggling = true;
var recalculationCount = 0;
// This function is going to recalculate the number of courses while
// the nav drawer is opening or closes (up to a maximum of 5 recalcs).
var doRecalculation = function() {
setTimeout(function() {
recalculateVisibleCourses(root);
recalculationCount++;
if (recalculationCount < 5 && drawerToggling) {
// If we haven't done too many recalculations and the drawer
// is still toggling then recurse.
doRecalculation();
}
}, 100);
};
// Start the recalculations.
doRecalculation(root);
});
PubSub.subscribe('nav-drawer-toggle-end', function() {
drawerToggling = false;
});
$(window).on('resize', function() {
if (!contentLoaded || !allCourses.length) {
// Nothing to reclculate.
return;
}
// Resize events fire rapidly so recalculating the visible courses each
// time can be expensive. Let's debounce them,
if (!resizeTimeout) {
resizeTimeout = setTimeout(function() {
resizeTimeout = null;
recalculateVisibleCourses(root);
// The recalculateVisibleCourses function will execute at a rate of 15fps.
}, 66);
}
});
CustomEvents.define(root, [CustomEvents.events.activate]);
root.on(CustomEvents.events.activate, SELECTORS.PAGING_BAR_NEXT, function(e, data) {
var button = $(e.target).closest(SELECTORS.PAGING_BAR_NEXT);
if (!button.hasClass('disabled')) {
viewIndex = viewIndex + availableVisibleCards;
recalculateVisibleCourses(root);
}
data.originalEvent.preventDefault();
});
root.on(CustomEvents.events.activate, SELECTORS.PAGING_BAR_PREVIOUS, function(e, data) {
var button = $(e.target).closest(SELECTORS.PAGING_BAR_PREVIOUS);
if (!button.hasClass('disabled')) {
viewIndex = viewIndex - availableVisibleCards;
viewIndex = viewIndex < 0 ? 0 : viewIndex;
recalculateVisibleCourses(root);
}
data.originalEvent.preventDefault();
});
};
/**
* Get and show the recent courses into the block.
*
* @param {int} userid User from which the courses will be obtained
* @param {object} root The root element for the recentlyaccessedcourses block.
*/
var init = function(userid, root) {
root = $(root);
registerEventListeners(root);
loadContent(userid)
.then(function(renderedCourses) {
allCourses = renderedCourses;
contentLoaded = true;
if (allCourses.length) {
showContent(root);
recalculateVisibleCourses(root);
} else {
showEmptyMessage(root);
}
return;
})
.catch(Notification.exception);
};
return {
init: init
};
});
@@ -0,0 +1,95 @@
<?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 definition for the Recently accessed courses block.
*
* @package block_recentlyaccessedcourses
* @copyright 2018 Victor Deniz <victor@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
/**
* Recently accessed courses block class.
*
* @package block_recentlyaccessedcourses
* @copyright Victor Deniz <victor@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class block_recentlyaccessedcourses extends block_base {
/**
* Initialize class member variables
*/
public function init() {
$this->title = get_string('pluginname', 'block_recentlyaccessedcourses');
}
/**
* Returns the contents.
*
* @return stdClass contents of block
*/
public function get_content() {
if (isset($this->content)) {
return $this->content;
}
$renderable = new block_recentlyaccessedcourses\output\main();
$renderer = $this->page->get_renderer('block_recentlyaccessedcourses');
$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_recentlyaccessedcourses');
return (object) [
'instance' => new stdClass(),
'plugin' => $configs,
];
}
}
@@ -0,0 +1,61 @@
<?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 the Recently accessed courses block.
*
* @package block_recentlyaccessedcourses
* @copyright 2018 Victor Deniz <victor@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace block_recentlyaccessedcourses\output;
defined('MOODLE_INTERNAL') || die();
use renderable;
use renderer_base;
use templatable;
/**
* Class containing data for Recently accessed courses block.
*
* @package block_recentlyaccessedcourses
* @copyright 2018 Victor Deniz <victor@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class main implements renderable, templatable {
/**
* Export this data so it can be used as the context for a mustache template.
*
* @param renderer_base $output
* @return \stdClass|array
*/
public function export_for_template(renderer_base $output) {
global $USER;
$nocoursesurl = $output->image_url('courses', 'block_recentlyaccessedcourses')->out(false);
$config = get_config('block_recentlyaccessedcourses');
return [
'userid' => $USER->id,
'nocoursesimgurl' => $nocoursesurl,
'pagingbar' => [
'next' => true,
'previous' => true
],
'displaycategories' => !empty($config->displaycategories)
];
}
}
@@ -0,0 +1,47 @@
<?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/>.
/**
* Recently accessed courses block renderer
*
* @package block_recentlyaccessedcourses
* @copyright 2018 Victor Deniz <victor@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace block_recentlyaccessedcourses\output;
defined('MOODLE_INTERNAL') || die;
use plugin_renderer_base;
/**
* Recently accessed courses block renderer
*
* @package block_recentlyaccessedcourses
* @copyright 2018 Victor Deniz <victor@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 Recently accessed courses block.
*
* @param main $main The main renderable
* @return string HTML string
*/
public function render_recentcourses(main $main) {
return $this->render_from_template('block_recentlyaccessedcourses/main', $main->export_for_template($this));
}
}
@@ -0,0 +1,46 @@
<?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 Recently accessed courses block.
*
* @package block_recentlyaccessedcourses
* @copyright 2018 Victor Deniz <victor@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace block_recentlyaccessedcourses\privacy;
defined('MOODLE_INTERNAL') || die();
/**
* Privacy Subsystem for Recently accessed courses block.
*
* @copyright 2018 Victor Deniz <victor@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class provider implements \core_privacy\local\metadata\null_provider {
/**
* Get the language string identifier with the component's language
* file to explain why this plugin stores no data.
*
* @return string
*/
public static function get_reason(): string {
return 'privacy:metadata';
}
}
@@ -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 Recently accessed courses block.
*
* @package block_recentlyaccessedcourses
* @copyright 2018 Victor Deniz <victor@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
$capabilities = array(
'block/recentlyaccessedcourses:myaddinstance' => array(
'captype' => 'write',
'contextlevel' => CONTEXT_SYSTEM,
'archetypes' => array(
'user' => CAP_ALLOW
),
'clonepermissionsfrom' => 'moodle/my:manageblocks'
)
);
@@ -0,0 +1,106 @@
<?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/>.
/**
* Recently accessed courses block installation.
*
* @package block_recentlyaccessedcourses
* @copyright 2018 Victor Deniz <victor@moodle.com> based on code from 2018 Ryan Wyllie <ryan@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
/**
* Add the Recently accessed courses block to the dashboard for all users by default
* when it is installed.
*/
function xmldb_block_recentlyaccessedcourses_install() {
global $DB;
if ($DB->count_records('block_instances') < 1) {
// Only add the recentlyaccessedcourses block if it's being installed on an existing site.
// For new sites it will be added by blocks_add_default_system_blocks().
return;
}
if ($defaultmypage = $DB->get_record('my_pages', array('userid' => null, 'name' => '__default', 'private' => 1))) {
$subpagepattern = $defaultmypage->id;
} else {
$subpagepattern = null;
}
$page = new moodle_page();
$systemcontext = context_system::instance();
$page->set_context($systemcontext);
// Add the block to the default /my.
$page->blocks->add_region('content');
$page->blocks->add_block('recentlyaccessedcourses', 'content', 0, false, 'my-index', $subpagepattern);
// Now we need to find all users that have viewed their dashboard because it'll have
// made duplicates of the default block_instances for them so they won't see the new
// recentlyaccessedcourses block without the admin resetting all of the dashboards.
//
// Instead we'll just add the recentlyaccessedcourses block to their dashboards here.
$sql = "SELECT parentcontextid, subpagepattern
FROM {block_instances}
WHERE pagetypepattern = 'my-index'
AND parentcontextid != ?";
$params = [$systemcontext->id];
$existingrecords = $DB->get_recordset_sql($sql, $params);
$blockinstances = [];
$seencontexts = [];
$now = time();
foreach ($existingrecords as $existingrecord) {
$parentcontextid = $existingrecord->parentcontextid;
if (isset($seencontexts[$parentcontextid])) {
// If we've seen this context already then skip it because we don't want
// to add duplicate recentlyaccessedcourses blocks to the same context. This happens
// if something funny is going on with the subpagepattern.
continue;
} else {
$seencontexts[$parentcontextid] = true;
}
$blockinstances[] = [
'blockname' => 'recentlyaccessedcourses',
'parentcontextid' => $parentcontextid,
'showinsubcontexts' => false,
'pagetypepattern' => 'my-index',
'subpagepattern' => $existingrecord->subpagepattern,
'defaultregion' => 'content',
'defaultweight' => 0,
'configdata' => '',
'timecreated' => $now,
'timemodified' => $now,
];
if (count($blockinstances) >= 1000) {
// Insert after every 1000 records so that the memory usage doesn't
// get out of control.
$DB->insert_records('block_instances', $blockinstances);
$blockinstances = [];
}
}
$existingrecords->close();
if (!empty($blockinstances)) {
// Insert what ever is left over.
$DB->insert_records('block_instances', $blockinstances);
}
}
@@ -0,0 +1,28 @@
<?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/>.
/**
* Strings for the Recently accessed courses block.
*
* @package block_recentlyaccessedcourses
* @copyright 2018 Victor Deniz <victor@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
$string['displaycategories'] = 'Display categories';
$string['displaycategories_help'] = 'Display the course category on the recently accessed courses block items.';
$string['pluginname'] = 'Recently accessed courses';
$string['privacy:metadata'] = 'The Recently accessed courses block does not store any personal data.';
$string['recentlyaccessedcourses:myaddinstance'] = 'Add a new recently accessed courses block to Dashboard';
$string['nocourses'] = 'No recent courses';
@@ -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

@@ -0,0 +1,34 @@
<?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 recentlyaccessedcourses block
*
* @package block_recentlyaccessedcourses
* @copyright 2019 Mihail Geshoski <mihail@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die;
if ($ADMIN->fulltree) {
// Display Course Categories on the recently accessed courses block items.
$settings->add(new admin_setting_configcheckbox(
'block_recentlyaccessedcourses/displaycategories',
get_string('displaycategories', 'block_recentlyaccessedcourses'),
get_string('displaycategories_help', 'block_recentlyaccessedcourses'),
1));
}
@@ -0,0 +1,51 @@
{{!
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 Licensebllsdsadfasfd
along with Moodle. If not, see <http://www.gnu.org/licenses/>.
}}
{{!
@template block_recentlyaccessedcourses/course-card
This template renders a course for the recentlyaccessedcourses block.
Example context (json):
{
"courses": [
{
"name": "Assignment due 1",
"viewurl": "https://moodlesite/course/view.php?id=2",
"courseimageurl": "https://moodlesite/pluginfile/123/course/overviewfiles/123.jpg",
"fullname": "course 3",
"isfavourite": true
}
]
}
}}
{{< core_course/coursecard }}
{{$coursecategory}}
{{#showcoursecategory}}
<span class="sr-only">
{{#str}}aria:coursecategory, core_course{{/str}}
</span>
<span class="text-truncate">{{{coursecategory}}}</span>
{{/showcoursecategory}}
{{/coursecategory}}
{{$coursename}} <span class="text-truncate">{{{fullname}}}</span> {{/coursename}}
{{$divider}}
{{#showcoursecategory}}
<div class="pl-1 pr-1">|</div>
{{/showcoursecategory}}
{{/divider}}
{{/ core_course/coursecard }}
@@ -0,0 +1,50 @@
{{!
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_recentlyaccessedcourses/main
This template renders the main content area for the Recently accessed courses block.
Example context (json):
{
"userid": 2,
"nocoursesimg": "https://moodlesite/theme/image.php/boost/block_recentlyaccessedcourses/1535727318/courses"
}
}}
<div id="block-recentlyaccessedcourses-{{uniqid}}" class="block-recentlyaccessedcourses block-cards" data-region="recentlyaccessedcourses"
data-userid="{{userid}}" data-displaycoursecategory="{{displaycategories}}">
<div class="container-fluid p-0">
{{> block_recentlyaccessedcourses/recentlyaccessedcourses-view }}
</div>
</div>
{{#js}}
require(
[
'jquery',
'block_recentlyaccessedcourses/main'
],
function(
$,
Main
) {
var root = $('#block-recentlyaccessedcourses-{{uniqid}}');
var userid = root.attr('data-userid');
Main.init(userid, root);
});
{{/js}}
@@ -0,0 +1,52 @@
{{!
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_recentlyaccessedcourses/recentlyaccessedcourses-view
This template renders the courses view for the recentlyaccessedcourses block.
Example context (json):
{
}
}}
<div id="recentlyaccessedcourses-view-{{uniqid}}" data-region="recentlyaccessedcourses-view">
<div data-region="loading-placeholder">
<div class="card-grid mx-0 mt-5 row row-cols-1 row-cols-sm-2 row-cols-md-3 flex-nowrap overflow-hidden" style="height: 13.05rem">
<div class="col p-0">{{> core_course/placeholder-course }}</div>
<div class="col p-0">{{> core_course/placeholder-course }}</div>
<div class="col p-0">{{> core_course/placeholder-course }}</div>
</div>
</div>
<div class="hidden" data-region="view-content">
{{#pagingbar}}
<div class="d-flex paging-bar-container mb-3" data-region="paging-bar-container">
{{> core/paged_content_paging_bar }}
</div>
{{/pagingbar}}
<div class="card-carousel mx-0 justify-content-center flex-nowrap overflow-hidden" data-region="card-deck" role="list">
{{#courses}}
{{> core_course/coursecard }}
{{/courses}}
</div>
</div>
<div class="hidden text-xs-center text-center mt-3" data-region="empty-message">
<img class="empty-placeholder-image-lg mt-1"
src="{{nocoursesimgurl}}"
alt="">
<p class="text-muted mt-3">{{#str}} nocourses, block_recentlyaccessedcourses {{/str}}</p>
</div>
</div>
@@ -0,0 +1,88 @@
@block @block_recentlyaccessedcourses @javascript
Feature: The recently accessed courses block allows users to easily access their most recently accessed courses
In order to access the most recently accessed courses
As a user
I can use the Recently accessed courses block in my dashboard
Background:
Given the following "users" exist:
| username | firstname | lastname | email |
| student1 | Student | 1 | student1@example.com |
And the following "categories" exist:
| name | category | idnumber |
| Category A | 0 | CATA |
And the following "courses" exist:
| fullname | shortname | category |
| Course 1 | C1 | 0 |
| Course 2 | C2 | 0 |
| Course 3 | C3 | 0 |
| Course 4 | C4 | CATA |
| 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 |
And the following "blocks" exist:
| blockname | contextlevel | reference | pagetypepattern | defaultregion |
| recentlyaccessedcourses | System | 1 | my-index | content |
Scenario: User has not accessed any course
Given I log in as "student1"
Then I should see "No recent courses" in the "Recently accessed courses" "block"
Scenario: User has accessed two courses
Given I log in as "student1"
And I should not see "Course 1" in the "Recently accessed courses" "block"
And I should not see "Course 2" in the "Recently accessed courses" "block"
When I am on "Course 1" course homepage
And I am on "Course 2" course homepage
And I follow "Dashboard"
And I change window size to "large"
Then I should see "Course 1" in the "Recently accessed courses" "block"
And I should see "Course 2" in the "Recently accessed courses" "block"
And I should not see "Course 3" in the "Recently accessed courses" "block"
And I should not see "Course 4" in the "Recently accessed courses" "block"
And I should not see "Course 5" in the "Recently accessed courses" "block"
Scenario: Show course category name
Given the following config values are set as admin:
| displaycategories | 1 | block_recentlyaccessedcourses |
And I log in as "student1"
And I am on "Course 1" course homepage
And I am on "Course 4" course homepage
And I follow "Dashboard"
And I should see "Category 1" in the "Recently accessed courses" "block"
And I should see "Category A" in the "Recently accessed courses" "block"
Scenario: Hide course category name
Given the following config values are set as admin:
| displaycategories | 0 | block_recentlyaccessedcourses |
And I log in as "student1"
And I am on "Course 1" course homepage
And I am on "Course 4" course homepage
And I follow "Dashboard"
And I should not see "Category 1" in the "Recently accessed courses" "block"
And I should not see "Category A" in the "Recently accessed courses" "block"
Scenario: Show short course name
Given the following config values are set as admin:
| courselistshortnames | 1 |
And I log in as "student1"
And I am on "Course 1" course homepage
And I am on "Course 4" course homepage
And I follow "Dashboard"
And I should see "C1" in the "Recently accessed courses" "block"
And I should see "C4" in the "Recently accessed courses" "block"
Scenario: Hide short course name
Given the following config values are set as admin:
| courselistshortnames | 0 |
And I log in as "student1"
And I am on "Course 1" course homepage
And I am on "Course 4" course homepage
And I follow "Dashboard"
And I should not see "C1" in the "Recently accessed courses" "block"
And I should not see "C4" in the "Recently accessed courses" "block"
@@ -0,0 +1,26 @@
<?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/>.
/**
* Recently accessed courses block data generator class.
*
* @package block_recentlyaccessedcourses
* @category test
* @copyright 2021 Sara Arjona <sara@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class block_recentlyaccessedcourses_generator extends testing_block_generator {
}
@@ -0,0 +1,10 @@
This file describes API changes in the recentlyaccessedcourses block code.
=== 3.8 ===
* New admin setting block_recentlyaccessedcourses/displaycategories which enables the recently accessed
courses block to display the course category.
=== 3.7 ===
* The 'block/recentlyaccessedcourses:addinstance' capability has been removed. It has never been used in code.
@@ -0,0 +1,27 @@
<?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 Recently accessed courses block.
*
* @package block_recentlyaccessedcourses
* @copyright 2018 Victor Deniz <victor@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_recentlyaccessedcourses'; // Full name of the plugin (used for diagnostics).