MDL-70801 core_my: Add a new courses page

This commit is contained in:
Mathew May 2021-10-21 12:13:36 +08:00
parent c69c33b14d
commit 6ca9c2154a
33 changed files with 417 additions and 51 deletions

View File

@ -71,9 +71,9 @@ Feature: Use core page resolvers for the I am on the page steps
Then I should see "<shouldsee>"
Examples:
| description | identifier | shouldsee |
| Admin page | "Admin notifications" | Check for available updates |
| Home page | Homepage | Course overview |
| description | identifier | shouldsee |
| Admin page | "Admin notifications" | Check for available updates |
| Home page | Homepage | Calendar |
Scenario Outline: When I am on a named page logged in as
When I am on the <identifier> page logged in as admin
@ -82,4 +82,4 @@ Feature: Use core page resolvers for the I am on the page steps
Examples:
| description | identifier | shouldsee |
| Admin page | "Admin notifications" | Check for available updates |
| Home page | Homepage | Course overview |
| Home page | Homepage | Calendar |

View File

@ -36,7 +36,7 @@ Feature: Viewing acceptances reports and accepting on behalf of other users
And I press "Next"
And I set the field "I agree to the This site policy" to "1"
And I press "Next"
And I should see "Course overview"
And I should see "Calendar"
And I log out
And I log in as "manager"
And I press "Next"
@ -82,7 +82,7 @@ Feature: Viewing acceptances reports and accepting on behalf of other users
And I press "Next"
And I set the field "I agree to the This site policy" to "1"
And I press "Next"
And I should see "Course overview"
And I should see "Calendar"
And I navigate to "Users > Privacy and policies > Manage policies" in site administration
And I click on "1 of 4 (25%)" "link" in the "This site policy" "table_row"
And I click on "Accept This site policy" "link" in the "User One" "table_row"
@ -111,7 +111,7 @@ Feature: Viewing acceptances reports and accepting on behalf of other users
And I set the field "I agree to the This site policy" to "1"
And I set the field "I agree to the This privacy policy" to "1"
And I press "Next"
And I should see "Course overview"
And I should see "Calendar"
And I log out
And I log in as "manager"
And I press "Next"
@ -300,7 +300,7 @@ Feature: Viewing acceptances reports and accepting on behalf of other users
And I press "Next"
And I set the field "I agree to the This site policy" to "1"
And I press "Next"
And I should see "Course overview"
And I should see "Calendar"
And I log out
And I log in as "admin"
And I navigate to "Users > Privacy and policies > Manage policies" in site administration
@ -311,4 +311,4 @@ Feature: Viewing acceptances reports and accepting on behalf of other users
And I press "Continue"
And I log out
When I log in as "user1"
Then I should see "Course overview"
Then I should see "Calendar"

View File

@ -20,7 +20,7 @@ Feature: Add a new user tour
| Display in middle of page | Welcome | Welcome to your personal learning space. We'd like to give you a quick tour to show you some of the areas you may find helpful |
And I add steps to the "First tour" tour:
| targettype | targetvalue_block | Title | Content |
| Block | Course overview | Course overview | This area shows you what's happening in some of your courses |
| Block | Timeline | Timeline | This is the Timeline. All of your upcoming activities can be found here |
| Block | Calendar | Calendar | This is the Calendar. All of your assignments and due dates can be found here |
And I add steps to the "First tour" tour:
| targettype | targetvalue_selector | Title | Content |
@ -28,7 +28,7 @@ Feature: Add a new user tour
When I am on homepage
Then I should see "Welcome to your personal learning space. We'd like to give you a quick tour to show you some of the areas you may find helpful"
And I click on "Next" "button" in the "[data-role='flexitour-step']" "css_element"
And I should see "This area shows you what's happening in some of your courses"
And I should see "This is the Timeline. All of your upcoming activities can be found here"
And I should not see "This is the Calendar. All of your assignments and due dates can be found here"
And I click on "Next" "button" in the "[data-role='flexitour-step']" "css_element"
And I should see "This is the Calendar. All of your assignments and due dates can be found here"
@ -95,7 +95,7 @@ Feature: Add a new user tour
| Display in middle of page | Welcome | First step of the Tour |
And I add steps to the "Steps tour" tour:
| targettype | targetvalue_block | Title | Content |
| Block | Course overview | Course overview | Second step of the Tour |
| Block | Timeline | Timeline | Second step of the Tour |
| Block | Calendar | Calendar | Third step of the Tour |
When I am on homepage
Then I should see "First step of the Tour"
@ -129,7 +129,7 @@ Feature: Add a new user tour
| Display in middle of page | Welcome | First step of the Tour |
And I add steps to the "Steps tour" tour:
| targettype | targetvalue_block | Title | Content |
| Block | Course overview | Course overview | Second step of the Tour |
| Block | Timeline | Timeline | Second step of the Tour |
| Block | Calendar | Calendar | Third step of the Tour |
When I am on homepage
Then I should see "First step of the Tour"

View File

@ -53,14 +53,14 @@ Feature: Steps can be navigated within a tour
| Display in middle of page | Welcome | Welcome to your personal learning space. We'd like to give you a quick tour to show you some of the areas you may find helpful |
And I add steps to the "First tour" tour:
| targettype | targetvalue_block | Title | Content |
| Block | Course overview | Course overview | This area shows you what's happening in some of your courses |
| Block | Timeline | Timeline | This is the Timeline. All of your upcoming activities can be found here |
| Block | Calendar | Calendar | This is the Calendar. All of your assignments and due dates can be found here |
When I am on homepage
Then I should see "Skip tour"
And I should see "Next (1/3)"
And I click on "Next (1/3)" "button" in the "Welcome" "dialogue"
And I should see "Skip tour"
And I click on "Next (2/3)" "button" in the "Course overview" "dialogue"
And I click on "Next (2/3)" "button" in the "Timeline" "dialogue"
And I should see "End tour"
@javascript

View File

@ -249,7 +249,7 @@ class block_base {
$this->arialabel = $bc->arialabel;
}
if ($this->page->user_is_editing()) {
if ($this->page->user_is_editing() && $this->instance_can_be_edited()) {
$bc->controls = $this->page->blocks->edit_controls($this);
} else {
// we must not use is_empty on hidden blocks
@ -692,6 +692,15 @@ class block_base {
return true;
}
/**
* If overridden and set to false by the block it will not be editable.
*
* @return bool
*/
public function instance_can_be_edited() {
return true;
}
/** @callback callback functions for comments api */
public static function comment_template($options) {
$ret = <<<EOD

View File

@ -70,4 +70,4 @@ Feature: Add and configure blocks throughout the site
| Text block title | Foo " onload="document.getElementsByTagName('body')[0].remove()" alt=" |
| Content | Example |
When I press "Save changes"
Then I should see "Course overview"
Then I should see "Example"

View File

@ -327,9 +327,14 @@ class core_block_externallib_testcase extends externallib_advanced_testcase {
// Force a setting change to check the returned blocks settings.
set_config('displaycategories', 0, 'block_myoverview');
$systempage = $DB->get_record('my_pages', array('userid' => null, 'name' => MY_PAGE_DEFAULT, 'private' => true));
// Get the expected default blocks.
$alldefaultblocksordered = $DB->get_records_menu('block_instances',
array('pagetypepattern' => 'my-index'), 'defaultregion, defaultweight ASC', 'id, blockname');
$alldefaultblocksordered = $DB->get_records_menu(
'block_instances',
array('pagetypepattern' => 'my-index', 'subpagepattern' => $systempage->id),
'defaultregion, defaultweight ASC',
'id, blockname'
);
$this->setUser($user);
@ -368,8 +373,13 @@ class core_block_externallib_testcase extends externallib_advanced_testcase {
$user = $this->getDataGenerator()->create_user();
$PAGE->set_url('/my/index.php'); // Need this because some internal API calls require the $PAGE url to be set.
$systempage = $DB->get_record('my_pages', array('userid' => null, 'name' => MY_PAGE_DEFAULT, 'private' => true));
// Get the expected default blocks.
$alldefaultblocks = $DB->get_records_menu('block_instances', array('pagetypepattern' => 'my-index'), '', 'id, blockname');
$alldefaultblocks = $DB->get_records_menu(
'block_instances', array('pagetypepattern' => 'my-index', 'subpagepattern' => $systempage->id),
'',
'id, blockname'
);
// Now, add a sticky block.
$page = new moodle_page();
@ -411,8 +421,14 @@ class core_block_externallib_testcase extends externallib_advanced_testcase {
$user = $this->getDataGenerator()->create_user();
$PAGE->set_url('/my/index.php'); // Need this because some internal API calls require the $PAGE url to be set.
$systempage = $DB->get_record('my_pages', array('userid' => null, 'name' => MY_PAGE_DEFAULT, 'private' => true));
// Get the expected default blocks.
$alldefaultblocks = $DB->get_records_menu('block_instances', array('pagetypepattern' => 'my-index'), '', 'id, blockname');
$alldefaultblocks = $DB->get_records_menu(
'block_instances',
array('pagetypepattern' => 'my-index', 'subpagepattern' => $systempage->id),
'',
'id, blockname'
);
// Add a custom block.
$page = new moodle_page();

View File

@ -38,7 +38,6 @@ Feature: We can change what we are viewing on the grader report
And I give the grade "80.00" to the user "Student 1" for the grade item "Test assignment name 1"
And I give the grade "90.00" to the user "Student 1" for the grade item "Test assignment name 2"
And I press "Save changes"
And I turn editing mode off
@javascript
Scenario: View and minimise the grader report containing hidden activities

View File

@ -858,6 +858,7 @@ $string['modsettings'] = 'Manage activities';
$string['modulesecurity'] = 'Module security';
$string['multilangforceold'] = 'Force old multilang syntax: &lt;span&gt; without the class="multilang" and &lt;lang&gt;';
$string['mustenablestats'] = 'Statistics have not yet been enabled on this site.';
$string['mycourses'] = 'My courses';
$string['mycoursesperpage'] = 'Number of courses';
$string['mydashboard'] = 'System default dashboard';
$string['mymoodle'] = 'Dashboard';

View File

@ -22,6 +22,7 @@
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
$string['coursemanagementoptions'] = 'Course management options';
$string['mymoodle'] = 'Dashboard';
$string['nocourses'] = 'No course information to show.';
$string['noguest'] = 'The Dashboard page is not available to guest users';

View File

@ -60,6 +60,7 @@ $PAGE->blocks->add_custom_regions_for_pagetype($pagetype);
$pagetype = explode('-', $pagetype);
switch ($pagetype[0]) {
case 'my':
case 'mycourses':
$PAGE->set_blocks_editing_capability('moodle/my:manageblocks');
break;
case 'user':

View File

@ -2647,7 +2647,30 @@ function blocks_add_default_system_blocks() {
$subpagepattern = null;
}
$newblocks = array('timeline', 'private_files', 'badges', 'calendar_month');
$newcontent = array('myoverview');
$page->blocks->add_blocks(array(BLOCK_POS_RIGHT => $newblocks, 'content' => $newcontent), 'my-index', $subpagepattern);
if ($defaultmycoursespage = $DB->get_record('my_pages', array('userid' => null, 'name' => '__courses', 'private' => 0))) {
$mycoursesubpagepattern = $defaultmycoursespage->id;
} else {
$mycoursesubpagepattern = null;
}
$page->blocks->add_blocks([
BLOCK_POS_RIGHT => [
'private_files',
'badges',
],
'content' => [
'timeline',
'calendar_month',
]],
'my-index',
$subpagepattern
);
$page->blocks->add_blocks([
'content' => [
'myoverview'
]],
'my-index',
$mycoursesubpagepattern
);
}

View File

@ -0,0 +1,58 @@
<?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 core\event;
/**
* My courses viewed event class.
*
* Class for event to be triggered when a user views their My courses page.
*
* @package core
* @copyright 2021 Mathew May <mathew.solutions>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class mycourses_viewed extends base {
/**
* Init method.
*
* @return void
*/
protected function init(): void {
$this->data['crud'] = 'r';
$this->data['edulevel'] = self::LEVEL_OTHER;
}
/**
* Returns description of what happened.
*
* @return string
*/
public function get_description(): string {
return "The user with id '$this->userid' has viewed their my courses page";
}
/**
* Return localised event name.
*
* @return string
*/
public static function get_name(): string {
return get_string('eventmycoursesviewed', 'core');
}
}

View File

@ -57,8 +57,8 @@ class primary extends view {
}
}
// Add a dummy mycourse link to a mycourses page.
$this->add(get_string('mycourses'), new \moodle_url('/course/index.php'), self::TYPE_ROOTNODE, null, 'courses');
// Add the mycourses link.
$this->add(get_string('mycourses'), new \moodle_url('/my/courses.php'), self::TYPE_ROOTNODE, null, 'courses');
// Add the site admin node. We are using the settingsnav so as to avoid rechecking permissions again.
$settingsnav = $this->page->settingsnav;

View File

@ -310,6 +310,13 @@ function xmldb_main_install() {
$mypage->private = 1;
$DB->insert_record('my_pages', $mypage);
$mycoursespage = new stdClass();
$mycoursespage->userid = null;
$mycoursespage->name = '__courses';
$mycoursespage->private = 0;
$mycoursespage->sortorder = 0;
$DB->insert_record('my_pages', $mycoursespage);
// Set a sensible default sort order for the most-used question types.
set_config('multichoice_sortorder', 1, 'question');
set_config('truefalse_sortorder', 2, 'question');

View File

@ -3145,5 +3145,17 @@ function xmldb_main_upgrade($oldversion) {
upgrade_main_savepoint(true, 2021110800.03);
}
if ($oldversion < 2021111200.01) {
$mycoursespage = new stdClass();
$mycoursespage->userid = null;
$mycoursespage->name = '__courses';
$mycoursespage->private = 0;
$mycoursespage->sortorder = 0;
$DB->insert_record('my_pages', $mycoursespage);
upgrade_main_savepoint(true, 2021111200.01);
}
return true;
}

View File

@ -1328,7 +1328,14 @@ class global_navigation extends navigation_node {
$this->rootnodes['site'] = $this->add_course($SITE);
$this->rootnodes['myprofile'] = $this->add(get_string('profile'), null, self::TYPE_USER, null, 'myprofile');
$this->rootnodes['currentcourse'] = $this->add(get_string('currentcourse'), null, self::TYPE_ROOTNODE, null, 'currentcourse');
$this->rootnodes['mycourses'] = $this->add(get_string('mycourses'), null, self::TYPE_ROOTNODE, null, 'mycourses', new pix_icon('i/course', ''));
$this->rootnodes['mycourses'] = $this->add(
get_string('mycourses'),
new moodle_url('/my/courses.php'),
self::TYPE_ROOTNODE,
null,
'mycourses',
new pix_icon('i/course', '')
);
$this->rootnodes['courses'] = $this->add(get_string('courses'), new moodle_url('/course/index.php'), self::TYPE_ROOTNODE, null, 'courses');
if (!core_course_category::user_top()) {
$this->rootnodes['courses']->hide();
@ -1521,7 +1528,7 @@ class global_navigation extends navigation_node {
foreach ($this->rootnodes as $node) {
// Dont remove the home node
/** @var navigation_node $node */
if (!in_array($node->key, ['home', 'myhome']) && !$node->has_children() && !$node->isactive) {
if (!in_array($node->key, ['home', 'mycourses', 'myhome']) && !$node->has_children() && !$node->isactive) {
$node->remove();
}
}
@ -2880,6 +2887,9 @@ class global_navigation extends navigation_node {
// This required as there are not other guaranteed nodes that may be loaded.
$coursenode->add('frontpageloaded', null, self::TYPE_CUSTOM, null, 'frontpageloaded')->display = false;
// Add My courses to the site pages within the navigation structure so the block can read it.
$coursenode->add(get_string('mycourses'), new moodle_url('/my/courses.php'), self::TYPE_CUSTOM, null, 'mycourses');
// Participants.
if ($navoptions->participants) {
$coursenode->add(get_string('participants'), new moodle_url('/user/index.php?id='.$course->id), self::TYPE_CUSTOM, get_string('participants'), 'participants');

View File

@ -4372,7 +4372,8 @@ EOD;
$pagetype = $this->page->pagetype;
$homepage = get_home_page();
$homepagetype = null;
if ($homepage == HOMEPAGE_MY) {
// Add a special case since /my/courses is a part of the /my subsystem.
if ($homepage == HOMEPAGE_MY && $this->page->title !== get_string('mycourses')) {
$homepagetype = 'my-index';
} else if ($homepage == HOMEPAGE_SITE) {
$homepagetype = 'site-index';

View File

@ -717,6 +717,9 @@ class behat_navigation extends behat_base {
case 'Homepage':
return new moodle_url('/');
case 'My courses':
return new moodle_url('/my/courses.php');
case 'Admin notifications':
return new moodle_url('/admin/');

86
my/courses.php Normal file
View File

@ -0,0 +1,86 @@
<?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/>.
/**
* My Courses.
*
* - each user can currently have their own page (cloned from system and then customised)
* - only the user can see their own dashboard
* - users can add any blocks they want
*
* @package core
* @subpackage my
* @copyright 2021 Mathew May <mathew.solutions>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
require_once(__DIR__ . '/../config.php');
require_once($CFG->dirroot . '/my/lib.php');
redirect_if_major_upgrade_required();
require_login();
$hassiteconfig = has_capability('moodle/site:config', context_system::instance());
if ($hassiteconfig && moodle_needs_upgrading()) {
redirect(new moodle_url('/admin/index.php'));
}
$context = context_system::instance();
// Get the My Moodle page info. Should always return something unless the database is broken.
if (!$currentpage = my_get_page(null, MY_PAGE_PUBLIC, MY_PAGE_COURSES)) {
throw new Exception('mymoodlesetup');
}
// Start setting up the page.
$PAGE->set_context($context);
$PAGE->set_url('/my/courses.php');
$PAGE->add_body_classes(['limitedwidth', 'page-mycourses']);
$PAGE->set_pagelayout('mycourses');
$PAGE->has_secondary_navigation_setter(false);
$PAGE->set_pagetype('my-index');
$PAGE->set_subpage($currentpage->id);
$PAGE->set_title(get_string('mycourses'));
$PAGE->set_heading(get_string('mycourses'));
// Force the add block out of the default area.
$PAGE->theme->addblockposition = BLOCK_ADDBLOCK_POSITION_CUSTOM;
// Add course management if the user has the capabilities for it.
$coursecat = core_course_category::user_top();
if ($coursecat->can_create_course() || $coursecat->has_manage_capability()) {
$data = [
'newcourseurl' => new moodle_url('/course/edit.php', ['category' => $coursecat->id]),
'manageurl' => new moodle_url('/course/management.php', ['categoryid' => $coursecat->id]),
];
$PAGE->add_header_action($OUTPUT->render_from_template('my/dropdown', $data));
}
echo $OUTPUT->header();
if (core_userfeedback::should_display_reminder()) {
core_userfeedback::print_reminder_block();
}
echo $OUTPUT->custom_block_region('content');
echo $OUTPUT->footer();
// Trigger dashboard has been viewed event.
$eventparams = array('context' => $context);
$event = \core\event\mycourses_viewed::create($eventparams);
$event->trigger();

View File

@ -29,41 +29,70 @@
define('MY_PAGE_PUBLIC', 0);
define('MY_PAGE_PRIVATE', 1);
define('MY_PAGE_DEFAULT', '__default');
define('MY_PAGE_COURSES', '__courses');
require_once("$CFG->libdir/blocklib.php");
/*
/**
* For a given user, this returns the $page information for their My Moodle page
*
* @param int|null $userid the id of the user whose page should be retrieved
* @param int|null $private either MY_PAGE_PRIVATE or MY_PAGE_PUBLIC
* @param string|null $pagename Differentiate between standard /my or /courses pages.
*/
function my_get_page($userid, $private=MY_PAGE_PRIVATE) {
function my_get_page(?int $userid, int $private = MY_PAGE_PRIVATE, string $pagename = MY_PAGE_DEFAULT) {
global $DB, $CFG;
if (empty($CFG->forcedefaultmymoodle) && $userid) { // Ignore custom My Moodle pages if admin has forced them
// Does the user have their own page defined? If so, return it.
if ($customised = $DB->get_record('my_pages', array('userid' => $userid, 'private' => $private))) {
if ($customised = $DB->get_record(
'my_pages',
array('userid' => $userid, 'private' => $private, 'name' => $pagename),
'*',
IGNORE_MULTIPLE
)) {
return $customised;
}
}
// Otherwise return the system default page
return $DB->get_record('my_pages', array('userid' => null, 'name' => '__default', 'private' => $private));
return $DB->get_record('my_pages', array('userid' => null, 'name' => $pagename, 'private' => $private), '*', IGNORE_MULTIPLE);
}
/*
/**
* This copies a system default page to the current user
*
* @param int $userid the id of the user whose page should be reset
* @param int $private either MY_PAGE_PRIVATE or MY_PAGE_PUBLIC
* @param string $pagetype either my-index or user-profile
* @param string $pagename Differentiate between standard /my or /courses pages.
*/
function my_copy_page($userid, $private=MY_PAGE_PRIVATE, $pagetype='my-index') {
function my_copy_page(
int $userid,
int $private = MY_PAGE_PRIVATE,
string $pagetype = 'my-index',
string $pagename = MY_PAGE_DEFAULT
) {
global $DB;
if ($customised = $DB->get_record('my_pages', array('userid' => $userid, 'private' => $private))) {
if ($customised = $DB->get_record(
'my_pages',
array('userid' => $userid, 'name' => $pagename, 'private' => $private),
'*',
IGNORE_MULTIPLE
)) {
return $customised; // We're done!
}
// Get the system default page
if (!$systempage = $DB->get_record('my_pages', array('userid' => null, 'name' => '__default', 'private' => $private))) {
if (!$systempage = $DB->get_record(
'my_pages',
array('userid' => null, 'name' => $pagename, 'private' => $private),
'*',
IGNORE_MULTIPLE
)) {
return false; // error
}
@ -117,18 +146,24 @@ function my_copy_page($userid, $private=MY_PAGE_PRIVATE, $pagetype='my-index') {
return $page;
}
/*
/**
* For a given user, this deletes their My Moodle page and returns them to the system default.
*
* @param int $userid the id of the user whose page should be reset
* @param int $private either MY_PAGE_PRIVATE or MY_PAGE_PUBLIC
* @param string $pagetype either my-index or user-profile
* @param string $pagename Differentiate between standard /my or /courses pages.
* @return mixed system page, or false on error
*/
function my_reset_page($userid, $private=MY_PAGE_PRIVATE, $pagetype='my-index') {
function my_reset_page(
int $userid,
int $private = MY_PAGE_PRIVATE,
string $pagetype='my-index',
string $pagename = MY_PAGE_DEFAULT
) {
global $DB, $CFG;
$page = my_get_page($userid, $private);
$page = my_get_page($userid, $private, $pagename);
if ($page->userid == $userid) {
$context = context_user::instance($userid);
if ($blocks = $DB->get_records('block_instances', array('parentcontextid' => $context->id,
@ -140,11 +175,16 @@ function my_reset_page($userid, $private=MY_PAGE_PRIVATE, $pagetype='my-index')
}
}
$DB->delete_records('block_positions', ['subpage' => $page->id, 'pagetype' => $pagetype, 'contextid' => $context->id]);
$DB->delete_records('my_pages', array('id' => $page->id));
$DB->delete_records('my_pages', array('id' => $page->id, 'name' => $pagename));
}
// Get the system default page
if (!$systempage = $DB->get_record('my_pages', array('userid' => null, 'name' => '__default', 'private' => $private))) {
if (!$systempage = $DB->get_record(
'my_pages',
array('userid' => null, 'name' => $pagename, 'private' => $private),
'*',
IGNORE_MULTIPLE
)) {
return false; // error
}
@ -166,10 +206,16 @@ function my_reset_page($userid, $private=MY_PAGE_PRIVATE, $pagetype='my-index')
*
* @param int $private Either MY_PAGE_PRIVATE or MY_PAGE_PUBLIC.
* @param string $pagetype Either my-index or user-profile.
* @param progress_bar $progressbar A progress bar to update.
* @param progress_bar|null $progressbar A progress bar to update.
* @param string $pagename Differentiate between standard /my or /courses pages.
* @return void
*/
function my_reset_page_for_all_users($private = MY_PAGE_PRIVATE, $pagetype = 'my-index', $progressbar = null) {
function my_reset_page_for_all_users(
int $private = MY_PAGE_PRIVATE,
string $pagetype = 'my-index',
?progress_bar $progressbar = null,
string $pagename = MY_PAGE_DEFAULT
) {
global $DB;
// This may take a while. Raise the execution time limit.
@ -199,12 +245,14 @@ function my_reset_page_for_all_users($private = MY_PAGE_PRIVATE, $pagetype = 'my
AND bi.pagetypepattern = :pagetypepattern
AND (bi.subpagepattern IS NULL OR bi.subpagepattern = " . $DB->sql_concat("''", 'p.id') . ")
WHERE p.private = :private
AND p.name = :name
AND p.userid $infragment";
$params = array_merge([
'private' => $private,
'usercontextlevel' => CONTEXT_USER,
'pagetypepattern' => $pagetype
'pagetypepattern' => $pagetype,
'name' => $pagename
], $inparams);
$blockids = $DB->get_fieldset_sql($sql, $params);

View File

@ -0,0 +1,37 @@
{{!
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 my/dropdown
Simple dropdown for the my/courses page
Example context (json):
{
"newcourseurl": "https://moodle.test/course/edit.php?category=1",
"manageurl": "https://moodle.test/course/management.php?categoryid=1"
}
}}
<div class="btn-group">
<!-- Set as a link to appease Goutte behat. -->
<a href="#" class="btn btn-link btn-icon icon-size-3" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" title="{{#str}}coursemanagementoptions, my{{/str}}">
<i class="fa fa-ellipsis-v text-dark py-2" aria-hidden="true"></i>
</a>
<div class="dropdown-menu dropdown-menu-right">
<a class="dropdown-item" href="{{newcourseurl}}">{{#str}}newcourse, core{{/str}}</a>
<a class="dropdown-item" href="{{manageurl}}">{{#str}}managecourses, core{{/str}}</a>
</div>
</div>

View File

@ -0,0 +1,13 @@
@core @core_my
Feature: Run tests over my courses.
Scenario: Admin can add new courses or manage them from my courses
Given I am on the "My courses" page logged in as "admin"
And I click on "Course management options" "link"
And I click on "New course" "link"
And I wait to be redirected
Then I should see "Add a new course"
And I am on the "My courses" page
And I click on "Course management options" "link"
And I click on "Manage courses" "link"
And I should see "Course and category management"

4
my/upgrade.txt Normal file
View File

@ -0,0 +1,4 @@
This files describes changes in the my system.
=== 4.0 ===
* Introduce a new /courses page
* Tighten up the typing within the lib file

View File

@ -64,10 +64,9 @@ class boostnavbar implements \renderable {
// Set the designated one path for courses.
$mycoursesnode = $this->get_item('mycourses');
if (!is_null($mycoursesnode)) {
// TODO: Once MDL-70801 lands point this to the new page.
$url = new \moodle_url('/course/');
$url = new \moodle_url('/my/courses.php');
$mycoursesnode->action = $url;
$mycoursesnode->text = get_string('courses');
$mycoursesnode->text = get_string('mycourses');
}
$this->remove_no_link_items();

View File

@ -78,6 +78,12 @@ $THEME->layouts = [
'regions' => array('side-pre'),
'defaultregion' => 'side-pre',
),
// My courses page.
'mycourses' => array(
'file' => 'columns2.php',
'regions' => array('content'),
'defaultregion' => 'content',
),
// My dashboard page.
'mydashboard' => array(
'file' => 'columns2.php',

View File

@ -49,6 +49,7 @@ $string['rawscss_desc'] = 'Use this field to provide SCSS or CSS code which will
$string['rawscsspre'] = 'Raw initial SCSS';
$string['rawscsspre_desc'] = 'In this field you can provide initialising SCSS code, it will be injected before everything else. Most of the time you will use this setting to define variables.';
$string['region-side-pre'] = 'Right';
$string['region-content'] = 'Content';
$string['showfooter'] = 'Show footer';
$string['privacy:metadata:preference:draweropennav'] = 'The user\'s preference for hiding or showing the drawer menu navigation.';
$string['privacy:drawernavclosed'] = 'The current preference for the navigation drawer is closed.';

View File

@ -2875,3 +2875,13 @@ body.dragging {
}
}
}
.page-mycourses {
#region-main {
padding: 0;
}
#region-main,
.block_myoverview {
border: 0;
}
}

View File

@ -12211,6 +12211,13 @@ body.dragging .dragging {
.collapse-list .collapse-list-item-content .collapse-list-item {
padding-left: calc(1rem * 3); }
.page-mycourses #region-main {
padding: 0; }
.page-mycourses #region-main,
.page-mycourses .block_myoverview {
border: 0; }
.icon {
font-size: 16px;
width: 16px;

View File

@ -72,6 +72,12 @@ $THEME->layouts = [
'regions' => array('side-pre'),
'defaultregion' => 'side-pre',
),
// My courses page.
'mycourses' => array(
'file' => 'columns.php',
'regions' => array('content'),
'defaultregion' => 'content',
),
// My dashboard page.
'mydashboard' => array(
'file' => 'columns.php',

View File

@ -12211,6 +12211,13 @@ body.dragging .dragging {
.collapse-list .collapse-list-item-content .collapse-list-item {
padding-left: calc(1rem * 3); }
.page-mycourses #region-main {
padding: 0; }
.page-mycourses #region-main,
.page-mycourses .block_myoverview {
border: 0; }
.icon {
font-size: 16px;
width: 16px;

View File

@ -43,7 +43,8 @@ class behat_theme_classic_behat_course extends behat_course {
public function i_navigate_to_course_participants() {
$coursestr = behat_context_helper::escape(get_string('courses'));
$mycoursestr = behat_context_helper::escape(get_string('mycourses'));
$xpath = "//div[contains(@class,'block')]//li[p/*[string(.)=$coursestr or string(.)=$mycoursestr]]";
$xpath = "//div[contains(@class,'block')]//li[contains(@class,'contains_branch')]" .
"[p/*[string(.)=$coursestr or string(.)=$mycoursestr]]";
$this->execute('behat_general::i_click_on_in_the', [get_string('participants'), 'link', $xpath, 'xpath_element']);
}

View File

@ -29,7 +29,7 @@
defined('MOODLE_INTERNAL') || die();
$version = 2021111200.00; // YYYYMMDD = weekly release date of this DEV branch.
$version = 2021111200.01; // YYYYMMDD = weekly release date of this DEV branch.
// RR = release increments - 00 in DEV branches.
// .XX = incremental changes.
$release = '4.0dev+ (Build: 20211112)'; // Human-friendly version name