Merge branch 'MDL-82565-main' of https://github.com/andrewnicols/moodle
This commit is contained in:
+2
-33
@@ -20,37 +20,6 @@
|
||||
*
|
||||
* @copyright 2016 Damyon Wiese
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @deprecated since 5.0
|
||||
*/
|
||||
|
||||
require_once("../config.php");
|
||||
|
||||
$courseid = required_param('courseid', PARAM_INT);
|
||||
|
||||
$PAGE->set_url('/course/admin.php', array('courseid'=>$courseid));
|
||||
|
||||
$course = $DB->get_record('course', array('id' => $courseid), '*', MUST_EXIST);
|
||||
|
||||
require_login($course);
|
||||
$context = context_course::instance($course->id);
|
||||
|
||||
$PAGE->set_pagelayout('incourse');
|
||||
|
||||
if ($courseid == $SITE->id) {
|
||||
$title = get_string('frontpagesettings');
|
||||
$node = $PAGE->settingsnav->find('frontpage', navigation_node::TYPE_SETTING);
|
||||
$PAGE->set_primary_active_tab('home');
|
||||
} else {
|
||||
$title = get_string('courseadministration');
|
||||
$node = $PAGE->settingsnav->find('courseadmin', navigation_node::TYPE_COURSE);
|
||||
}
|
||||
$PAGE->set_title($title);
|
||||
$PAGE->set_heading($course->fullname);
|
||||
$PAGE->navbar->add($title);
|
||||
echo $OUTPUT->header();
|
||||
echo $OUTPUT->heading($title);
|
||||
|
||||
if ($node) {
|
||||
echo $OUTPUT->render_from_template('core/settings_link_page', ['node' => $node]);
|
||||
}
|
||||
|
||||
echo $OUTPUT->footer();
|
||||
require_once("../r.php");
|
||||
|
||||
@@ -14,40 +14,37 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Edit course tags form
|
||||
*
|
||||
* @package core_course
|
||||
* @copyright 2015 Marina Glancy
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
namespace core_course\form;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
require_once($CFG->libdir.'/formslib.php');
|
||||
require_once($CFG->libdir . '/formslib.php');
|
||||
|
||||
/**
|
||||
* Edit course tags form
|
||||
* Edit course tags form.
|
||||
*
|
||||
* @package core_course
|
||||
* @copyright 2015 Marina Glancy
|
||||
* @copyright Marina Glancy
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class coursetags_form extends moodleform {
|
||||
class tags_form extends \moodleform {
|
||||
#[\Override]
|
||||
public function definition(): void {
|
||||
$mform = $this->_form;
|
||||
|
||||
/**
|
||||
* Form definition
|
||||
*/
|
||||
public function definition() {
|
||||
$mform = $this->_form;
|
||||
|
||||
$mform->addElement('tags', 'tags', get_string('tags'),
|
||||
array('itemtype' => 'course', 'component' => 'core'));
|
||||
$mform->addElement(
|
||||
'tags',
|
||||
'tags',
|
||||
get_string('tags'),
|
||||
[
|
||||
'itemtype' => 'course',
|
||||
'component' => 'core',
|
||||
],
|
||||
);
|
||||
|
||||
$mform->addElement('hidden', 'id', null);
|
||||
$mform->setType('id', PARAM_INT);
|
||||
|
||||
$this->add_action_buttons();
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
<?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_course\route\controller;
|
||||
|
||||
use core\router\route;
|
||||
use core\router\require_login;
|
||||
use navigation_node;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
|
||||
/**
|
||||
* Course Management.
|
||||
*
|
||||
* @package core_course
|
||||
* @copyright Andrew Lyons <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class course_management {
|
||||
use \core\router\route_controller;
|
||||
|
||||
/**
|
||||
* Administer a course.
|
||||
*
|
||||
* @param ResponseInterface $response
|
||||
* @param \stdClass $course
|
||||
* @return ResponseInterface
|
||||
*/
|
||||
#[route(
|
||||
path: '/{course}/manage',
|
||||
pathtypes: [
|
||||
new \core\router\parameters\path_course(),
|
||||
],
|
||||
requirelogin: new require_login(
|
||||
requirelogin: true,
|
||||
courseattributename: 'course',
|
||||
),
|
||||
)]
|
||||
public function administer_course(
|
||||
ResponseInterface $response,
|
||||
\stdClass $course,
|
||||
): ResponseInterface {
|
||||
global $PAGE, $SITE, $OUTPUT;
|
||||
|
||||
$PAGE->set_pagelayout('incourse');
|
||||
|
||||
if ($course->id == $SITE->id) {
|
||||
$title = get_string('frontpagesettings');
|
||||
$node = $PAGE->settingsnav->find('frontpage', navigation_node::TYPE_SETTING);
|
||||
$PAGE->set_primary_active_tab('home');
|
||||
} else {
|
||||
$title = get_string('courseadministration');
|
||||
$node = $PAGE->settingsnav->find('courseadmin', navigation_node::TYPE_COURSE);
|
||||
}
|
||||
$PAGE->set_title($title);
|
||||
$PAGE->set_heading($course->fullname);
|
||||
$PAGE->navbar->add($title);
|
||||
$response->getBody()->write($OUTPUT->header());
|
||||
$response->getBody()->write($OUTPUT->heading($title));
|
||||
|
||||
if ($node) {
|
||||
$response->getBody()->write($OUTPUT->render_from_template('core/settings_link_page', ['node' => $node]));
|
||||
}
|
||||
|
||||
$response->getBody()->write($OUTPUT->footer());
|
||||
|
||||
return $response;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
<?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_course\route\controller;
|
||||
|
||||
use core\exception\moodle_exception;
|
||||
use core\router\parameters\query_returnurl;
|
||||
use core\router\route;
|
||||
use core\router\require_login;
|
||||
use core\router\util;
|
||||
use core_course\form\tags_form;
|
||||
use core_tag_tag;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
|
||||
/**
|
||||
* Tag management for courses.
|
||||
*
|
||||
* @package core_course
|
||||
* @copyright Andrew Lyons <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class tags_controller {
|
||||
use \core\router\route_controller;
|
||||
|
||||
/**
|
||||
* Administer course tags.
|
||||
*
|
||||
* @param ServerRequestInterface $request
|
||||
* @param ResponseInterface $response
|
||||
* @param \stdClass $course
|
||||
* @param \core\context\course $coursecontext
|
||||
* @return ResponseInterface
|
||||
*/
|
||||
#[route(
|
||||
path: '/{course}/tags',
|
||||
method: ['GET', 'POST'],
|
||||
pathtypes: [
|
||||
new \core\router\parameters\path_course(),
|
||||
],
|
||||
queryparams: [
|
||||
new query_returnurl(),
|
||||
],
|
||||
requirelogin: new require_login(
|
||||
requirelogin: true,
|
||||
courseattributename: 'course',
|
||||
),
|
||||
)]
|
||||
public function administer_tags(
|
||||
ServerRequestInterface $request,
|
||||
ResponseInterface $response,
|
||||
\stdClass $course,
|
||||
\core\context\course $coursecontext,
|
||||
): ResponseInterface {
|
||||
global $CFG, $OUTPUT, $PAGE;
|
||||
|
||||
if (!$course->visible && !has_capability('moodle/course:viewhiddencourses', $coursecontext)) {
|
||||
throw new moodle_exception('coursehidden', '', $CFG->wwwroot .'/');
|
||||
}
|
||||
require_capability('moodle/course:tag', $coursecontext);
|
||||
|
||||
$PAGE->set_course($course);
|
||||
$PAGE->set_pagelayout('incourse');
|
||||
$PAGE->set_url(util::get_path_for_callable([self::class, 'administer_tags'], [
|
||||
'course' => $course->id,
|
||||
]));
|
||||
$PAGE->set_title(get_string('coursetags', 'tag'));
|
||||
$PAGE->set_heading($course->fullname);
|
||||
|
||||
$form = new tags_form();
|
||||
$data = [
|
||||
'id' => $course->id,
|
||||
'tags' => core_tag_tag::get_item_tags_array('core', 'course', $course->id),
|
||||
];
|
||||
$form->set_data($data);
|
||||
|
||||
$redirecturl = $this->get_param($request, 'returnurl') ?? course_get_url($course);
|
||||
if ($form->is_cancelled()) {
|
||||
return util::redirect($response, $redirecturl);
|
||||
} else if ($data = $form->get_data()) {
|
||||
core_tag_tag::set_item_tags('core', 'course', $course->id, $coursecontext, $data->tags);
|
||||
return util::redirect($response, $redirecturl);
|
||||
}
|
||||
|
||||
$response->getBody()->write($OUTPUT->header());
|
||||
$response->getBody()->write($OUTPUT->heading(get_string('coursetags', 'tag')));
|
||||
$response->getBody()->write($form->render());
|
||||
$response->getBody()->write($OUTPUT->footer());
|
||||
|
||||
return $response;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
<?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_course\route\shim;
|
||||
|
||||
use core\param;
|
||||
use core\router\route;
|
||||
use core\router\route_controller;
|
||||
use core\router\schema\parameters\query_parameter;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
|
||||
/**
|
||||
* A shim for the course routes.
|
||||
*
|
||||
* @package core_course
|
||||
* @copyright Andrew Lyons <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
final class course_routes {
|
||||
use route_controller;
|
||||
|
||||
/**
|
||||
* Shim /course/admin.php to the course management controller.
|
||||
*
|
||||
* @param ServerRequestInterface $request
|
||||
* @param ResponseInterface $response
|
||||
* @return ResponseInterface
|
||||
*/
|
||||
#[route(
|
||||
path: '/admin.php',
|
||||
queryparams: [
|
||||
new query_parameter(
|
||||
name: 'courseid',
|
||||
type: param::INT,
|
||||
description: 'The course ID',
|
||||
required: true,
|
||||
),
|
||||
],
|
||||
)]
|
||||
public function administer_course(
|
||||
ServerRequestInterface $request,
|
||||
ResponseInterface $response,
|
||||
): ResponseInterface {
|
||||
$params = $request->getQueryParams();
|
||||
return self::redirect_to_callable(
|
||||
$request,
|
||||
$response,
|
||||
[\core_course\route\controller\course_management::class, 'administer_course'],
|
||||
pathparams: $params + ['course' => $params['courseid']],
|
||||
excludeparams: ['courseid'],
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Shim /course/tags.php to the course tag management controller.
|
||||
*
|
||||
* @param ServerRequestInterface $request
|
||||
* @param ResponseInterface $response
|
||||
* @return ResponseInterface
|
||||
*/
|
||||
#[route(
|
||||
path: '/tags.php',
|
||||
queryparams: [
|
||||
new query_parameter(
|
||||
name: 'id',
|
||||
type: param::INT,
|
||||
description: 'The course ID',
|
||||
required: true,
|
||||
),
|
||||
new query_parameter(
|
||||
name: 'returnurl',
|
||||
type: param::LOCALURL,
|
||||
description: 'The return URL',
|
||||
required: false,
|
||||
),
|
||||
],
|
||||
)]
|
||||
public function administer_tags(
|
||||
ServerRequestInterface $request,
|
||||
ResponseInterface $response,
|
||||
): ResponseInterface {
|
||||
$params = $request->getQueryParams();
|
||||
return self::redirect_to_callable(
|
||||
$request,
|
||||
$response,
|
||||
[\core_course\route\controller\tags_controller::class, 'administer_tags'],
|
||||
pathparams: $params + ['course' => $params['id']],
|
||||
excludeparams: ['id'],
|
||||
);
|
||||
}
|
||||
}
|
||||
+2
-44
@@ -20,48 +20,6 @@
|
||||
* @package core_course
|
||||
* @copyright 2015 Marina Glancy
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @deprecated since 5.0
|
||||
*/
|
||||
|
||||
require_once("../config.php");
|
||||
require_once($CFG->dirroot . '/course/tags_form.php');
|
||||
|
||||
$id = required_param('id', PARAM_INT); // Course id.
|
||||
$returnurl = optional_param('return', null, PARAM_LOCALURL);
|
||||
$course = get_course($id);
|
||||
|
||||
require_login();
|
||||
|
||||
// Check capabilities but do not call require_login($course) - the user does not have to be enrolled.
|
||||
$context = context_course::instance($course->id);
|
||||
if (!$course->visible and !has_capability('moodle/course:viewhiddencourses', $context)) {
|
||||
throw new \moodle_exception('coursehidden', '', $CFG->wwwroot .'/');
|
||||
}
|
||||
require_capability('moodle/course:tag', $context);
|
||||
if (!core_tag_tag::is_enabled('core', 'course')) {
|
||||
throw new \moodle_exception('tagsaredisabled', 'tag');
|
||||
}
|
||||
|
||||
$PAGE->set_course($course);
|
||||
$PAGE->set_pagelayout('incourse');
|
||||
$PAGE->set_url('/course/tags.php', array('id' => $course->id));
|
||||
$PAGE->set_title(get_string('coursetags', 'tag'));
|
||||
$PAGE->set_heading($course->fullname);
|
||||
|
||||
$form = new coursetags_form();
|
||||
$data = array('id' => $course->id, 'tags' => core_tag_tag::get_item_tags_array('core', 'course', $course->id));
|
||||
$form->set_data($data);
|
||||
|
||||
$redirecturl = $returnurl ? new moodle_url($returnurl) : course_get_url($course);
|
||||
if ($form->is_cancelled()) {
|
||||
redirect($redirecturl);
|
||||
} else if ($data = $form->get_data()) {
|
||||
core_tag_tag::set_item_tags('core', 'course', $course->id, context_course::instance($course->id), $data->tags);
|
||||
redirect($redirecturl);
|
||||
}
|
||||
|
||||
echo $OUTPUT->header();
|
||||
echo $OUTPUT->heading(get_string('coursetags', 'tag'));
|
||||
|
||||
$form->display();
|
||||
|
||||
echo $OUTPUT->footer();
|
||||
require_once("../r.php");
|
||||
|
||||
+2
-75
@@ -23,80 +23,7 @@
|
||||
* ErrorDocument 404 /error/index.php
|
||||
*
|
||||
* @package core
|
||||
* @copyright 2020 Brendan Heywood <[email protected]>
|
||||
* @copyright Brendan Heywood <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
require('../config.php'); // phpcs:ignore
|
||||
|
||||
// Until we have a more robust routing api in place this is a very simple
|
||||
// and clean way to handle arbitrary urls without a php extension.
|
||||
if ($ME === '/.well-known/change-password') {
|
||||
redirect(new moodle_url('/login/change_password.php'));
|
||||
}
|
||||
|
||||
$context = context_system::instance();
|
||||
$title = get_string('pagenotexisttitle', 'error');
|
||||
$PAGE->set_url('/error/index.php');
|
||||
$PAGE->set_context($context);
|
||||
$PAGE->set_title($title);
|
||||
$PAGE->set_heading($title);
|
||||
$PAGE->navbar->add($title);
|
||||
|
||||
// This allows the webserver to dictate wether the http status should remain
|
||||
// what it would have been, or force it to be a 404. Under other conditions
|
||||
// it could most often be a 403, 405 or a 50x error.
|
||||
$code = optional_param('code', 0, PARAM_INT);
|
||||
if ($code == 404) {
|
||||
header("HTTP/1.0 404 Not Found");
|
||||
}
|
||||
|
||||
$canmessage = has_capability('moodle/site:senderrormessage', $context);
|
||||
|
||||
$supportuser = core_user::get_support_user();
|
||||
|
||||
// We can only message support if both the user has the capability
|
||||
// and the support user is a real user.
|
||||
if ($canmessage) {
|
||||
$canmessage = core_user::is_real_user($supportuser->id);
|
||||
}
|
||||
|
||||
$mform = new \core\form\error_feedback($CFG->wwwroot . '/error/index.php');
|
||||
|
||||
if ($data = $mform->get_data()) {
|
||||
|
||||
if (!$canmessage) {
|
||||
redirect($CFG->wwwroot);
|
||||
}
|
||||
|
||||
// Send the message and redirect.
|
||||
$message = new \core\message\message();
|
||||
$message->courseid = SITEID;
|
||||
$message->component = 'moodle';
|
||||
$message->name = 'errors';
|
||||
$message->userfrom = $USER;
|
||||
$message->userto = core_user::get_support_user();
|
||||
$message->subject = 'Error: '. $data->referer .' -> '. $data->requested;
|
||||
$message->fullmessage = $data->text;
|
||||
$message->fullmessageformat = FORMAT_PLAIN;
|
||||
$message->fullmessagehtml = '';
|
||||
$message->smallmessage = '';
|
||||
$message->contexturl = $data->requested;
|
||||
message_send($message);
|
||||
|
||||
redirect($CFG->wwwroot, get_string('sendmessagesent', 'error', $data->requested), 5);
|
||||
exit;
|
||||
}
|
||||
|
||||
echo $OUTPUT->header();
|
||||
echo $OUTPUT->notification(get_string('pagenotexist', 'error', s($ME)), 'error');
|
||||
echo $OUTPUT->supportemail(['class' => 'text-center d-block mb-3 fw-bold']);
|
||||
|
||||
if ($canmessage) {
|
||||
echo \html_writer::tag('h4', get_string('sendmessage', 'error'));
|
||||
$mform->display();
|
||||
} else {
|
||||
echo $OUTPUT->continue_button($CFG->wwwroot);
|
||||
}
|
||||
|
||||
echo $OUTPUT->footer();
|
||||
require_once("../r.php");
|
||||
|
||||
Vendored
+1
-1
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -32,7 +32,7 @@
|
||||
* });
|
||||
*/
|
||||
|
||||
import Cfg from 'core/config';
|
||||
import * as Cfg from 'core/config';
|
||||
import PendingPromise from './pending';
|
||||
|
||||
/**
|
||||
|
||||
+2
-1
@@ -218,7 +218,8 @@ function behat_clean_init_config() {
|
||||
'umaskpermissions', 'dbtype', 'dblibrary', 'dbhost', 'dbname', 'dbuser', 'dbpass', 'prefix',
|
||||
'dboptions', 'proxyhost', 'proxyport', 'proxytype', 'proxyuser', 'proxypassword',
|
||||
'proxybypass', 'pathtogs', 'pathtophp', 'pathtodu', 'aspellpath', 'pathtodot', 'skiplangupgrade',
|
||||
'altcacheconfigpath', 'pathtounoconv', 'alternative_file_system_class', 'pathtopython'
|
||||
'altcacheconfigpath', 'pathtounoconv', 'alternative_file_system_class', 'pathtopython',
|
||||
'routerconfigured',
|
||||
));
|
||||
|
||||
// Add extra allowed settings.
|
||||
|
||||
@@ -37,10 +37,7 @@ require_once($CFG->libdir.'/formslib.php');
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class error_feedback extends moodleform {
|
||||
|
||||
/**
|
||||
* Error form definition
|
||||
*/
|
||||
#[\Override]
|
||||
public function definition() {
|
||||
global $CFG;
|
||||
|
||||
@@ -55,4 +52,3 @@ class error_feedback extends moodleform {
|
||||
$mform->addElement('submit', 'submitbutton', get_string('sendmessage', 'error'));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4372,7 +4372,10 @@ EOD;
|
||||
// We only add a list to the full settings menu if we didn't include every node in the short menu.
|
||||
if ($skipped) {
|
||||
$text = get_string('morenavigationlinks');
|
||||
$url = new moodle_url('/course/admin.php', ['courseid' => $this->page->course->id]);
|
||||
$url = \core\router\util::get_path_for_callable(
|
||||
[\core_course\route\controller\course_management::class, 'administer_course'],
|
||||
['course' => $this->page->course->id],
|
||||
);
|
||||
$link = new action_link($url, $text, null, null, new pix_icon('t/edit', $text));
|
||||
$menu->add_secondary_action($link);
|
||||
}
|
||||
@@ -4386,7 +4389,10 @@ EOD;
|
||||
// We only add a list to the full settings menu if we didn't include every node in the short menu.
|
||||
if ($skipped) {
|
||||
$text = get_string('morenavigationlinks');
|
||||
$url = new moodle_url('/course/admin.php', ['courseid' => $this->page->course->id]);
|
||||
$url = \core\router\util::get_path_for_callable(
|
||||
[\core_course\route\controller\course_management::class, 'administer_course'],
|
||||
['course' => $this->page->course->id],
|
||||
);
|
||||
$link = new action_link($url, $text, null, null, new pix_icon('t/edit', $text));
|
||||
$menu->add_secondary_action($link);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,178 @@
|
||||
<?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\route\controller;
|
||||
|
||||
use core\form\error_feedback;
|
||||
use core\router;
|
||||
use core\router\route;
|
||||
use core\router\schema\parameters\query_parameter;
|
||||
use core\router\util;
|
||||
use core\url;
|
||||
use core_user;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
|
||||
/**
|
||||
* Page Not Found Controller.
|
||||
*
|
||||
* @package core
|
||||
* @copyright Andrew Lyons <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class page_not_found_controller {
|
||||
use \core\router\route_controller;
|
||||
|
||||
/**
|
||||
* Constructor for the page not found handler.
|
||||
*
|
||||
* @param \core\router $router The router.
|
||||
*/
|
||||
public function __construct(
|
||||
/** @var router The routing engine */
|
||||
private router $router,
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Administer a course.
|
||||
*
|
||||
* @param ServerRequestInterface $request
|
||||
* @param ResponseInterface $response
|
||||
* @return ResponseInterface
|
||||
*/
|
||||
#[route(
|
||||
path: '/error',
|
||||
method: ['GET', 'POST'],
|
||||
queryparams: [
|
||||
new query_parameter(
|
||||
name: 'code',
|
||||
type: \core\param::INT,
|
||||
default: 404,
|
||||
),
|
||||
],
|
||||
)]
|
||||
public function page_not_found_handler(
|
||||
ServerRequestInterface $request,
|
||||
): ResponseInterface {
|
||||
global $CFG, $PAGE, $OUTPUT, $ME;
|
||||
|
||||
$context = \core\context\system::instance();
|
||||
$title = get_string('pagenotexisttitle', 'error');
|
||||
$PAGE->set_url('/error');
|
||||
$PAGE->set_context($context);
|
||||
$PAGE->set_title($title);
|
||||
$PAGE->set_heading($title);
|
||||
$PAGE->navbar->add($title);
|
||||
|
||||
// This allows the webserver to dictate wether the http status should remain
|
||||
// what it would have been, or force it to be a 404. Under other conditions
|
||||
// it could most often be a 403, 405 or a 50x error.
|
||||
$code = $request->getQueryParams()['code'] ?? 404;
|
||||
$response = $this->router->get_app()->getResponseFactory()->createResponse($code);
|
||||
|
||||
$mform = $this->get_message_form($response);
|
||||
if ($mform) {
|
||||
if ($this->process_message_form($mform)) {
|
||||
// The form was submitted. Redirect to the home page.
|
||||
return util::redirect(
|
||||
$response,
|
||||
new url('/'),
|
||||
);
|
||||
}
|
||||
|
||||
// Form not submitted. We need to set the referer and request path because the URI may be different on submission.
|
||||
$mform->set_data([
|
||||
'referer' => $request->getHeaderLine('Referer'),
|
||||
'requested' => $request->getUri()->getPath(),
|
||||
]);
|
||||
}
|
||||
|
||||
$response->getBody()->write($OUTPUT->header());
|
||||
$response->getBody()->write($OUTPUT->notification(get_string('pagenotexist', 'error', s($ME)), 'error'));
|
||||
$response->getBody()->write($OUTPUT->supportemail(['class' => 'text-center d-block mb-3 fw-bold']));
|
||||
|
||||
if ($mform) {
|
||||
$response->getBody()->write(\html_writer::tag('h4', get_string('sendmessage', 'error')));
|
||||
$response->getBody()->write($mform->render());
|
||||
} else {
|
||||
$response->getBody()->write($OUTPUT->continue_button($CFG->wwwroot));
|
||||
}
|
||||
|
||||
$response->getBody()->write($OUTPUT->footer());
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the message form, or null if it should not be displayed.
|
||||
*
|
||||
* @param \Psr\Http\Message\ResponseInterface $response
|
||||
* @return error_feedback|null
|
||||
*/
|
||||
protected function get_message_form(
|
||||
ResponseInterface $response,
|
||||
): ?\moodleform {
|
||||
$canmessage = has_capability('moodle/site:senderrormessage', \core\context\system::instance());
|
||||
$supportuser = core_user::get_support_user();
|
||||
|
||||
// We can only message support if both the user has the capability
|
||||
// and the support user is a real user.
|
||||
$canmessage = $canmessage && core_user::is_real_user($supportuser->id);
|
||||
|
||||
if (!$canmessage) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new error_feedback(util::get_path_for_callable([self::class, 'page_not_found_handler'], [], [])->out());
|
||||
}
|
||||
|
||||
/**
|
||||
* Process the message form.
|
||||
*
|
||||
* If the form was submitted, send the message and return a redirect response.
|
||||
*
|
||||
* @param error_feedback $mform
|
||||
* @return bool
|
||||
*/
|
||||
protected function process_message_form(
|
||||
error_feedback $mform,
|
||||
): bool {
|
||||
global $CFG, $USER;
|
||||
if ($data = $mform->get_data()) {
|
||||
// Send the message and redirect.
|
||||
$message = new \core\message\message();
|
||||
$message->courseid = SITEID;
|
||||
$message->component = 'moodle';
|
||||
$message->name = 'errors';
|
||||
$message->userfrom = $USER;
|
||||
$message->userto = core_user::get_support_user();
|
||||
$message->subject = 'Error: ' . $data->referer . ' -> ' . $data->requested;
|
||||
$message->fullmessage = $data->text;
|
||||
$message->fullmessageformat = FORMAT_PLAIN;
|
||||
$message->fullmessagehtml = '';
|
||||
$message->smallmessage = '';
|
||||
$message->contexturl = $data->requested;
|
||||
message_send($message);
|
||||
|
||||
\core\notification::success(get_string('sendmessagesent', 'error', $data->requested));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -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/>.
|
||||
|
||||
namespace core\route\shim;
|
||||
|
||||
use core\param;
|
||||
use core\router\route;
|
||||
use core\router\schema\parameters\query_parameter;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
|
||||
/**
|
||||
* Redirect requests for /error/index.php and /error to the page_not_found_controller.
|
||||
*
|
||||
* @package core
|
||||
* @copyright Andrew Lyons <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class error_controller {
|
||||
/**
|
||||
* Shim /error/index.php to /core/error
|
||||
*
|
||||
* @param ServerRequestInterface $request
|
||||
* @param ResponseInterface $response
|
||||
* @return ResponseInterface
|
||||
*/
|
||||
#[route(
|
||||
path: '/error[/index.php]',
|
||||
queryparams: [
|
||||
new query_parameter(
|
||||
name: 'code',
|
||||
type: param::INT,
|
||||
description: 'The HTTP Code',
|
||||
),
|
||||
],
|
||||
)]
|
||||
public function administer_course(
|
||||
ServerRequestInterface $request,
|
||||
ResponseInterface $response,
|
||||
): ResponseInterface {
|
||||
$params = $request->getQueryParams();
|
||||
return \core\router\util::redirect_to_callable(
|
||||
$request,
|
||||
$response,
|
||||
[\core\route\controller\page_not_found_controller::class, 'page_not_found_handler'],
|
||||
);
|
||||
}
|
||||
}
|
||||
+66
-11
@@ -16,9 +16,10 @@
|
||||
|
||||
namespace core;
|
||||
|
||||
use core\output\routed_error_handler;
|
||||
use core\router\middleware\cors_middleware;
|
||||
use core\router\middleware\error_handling_middleware;
|
||||
use core\router\middleware\moodle_api_authentication_middleware;
|
||||
use core\router\middleware\moodle_authentication_middleware;
|
||||
use core\router\middleware\moodle_bootstrap_middleware;
|
||||
use core\router\middleware\moodle_route_attribute_middleware;
|
||||
use core\router\middleware\uri_normalisation_middleware;
|
||||
@@ -31,7 +32,10 @@ use Psr\Http\Message\ResponseFactoryInterface;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Slim\App;
|
||||
use Slim\Exception\HttpForbiddenException;
|
||||
use Slim\Exception\HttpNotFoundException;
|
||||
use Slim\Interfaces\RouteGroupInterface;
|
||||
use Slim\Middleware\ErrorMiddleware;
|
||||
|
||||
/**
|
||||
* Moodle Router.
|
||||
@@ -99,19 +103,19 @@ class router {
|
||||
);
|
||||
// Replace occurrences of backslashes with forward slashes, especially on Windows.
|
||||
$scriptfile = str_replace('\\', '/', $scriptfile);
|
||||
$relativeroot = sprintf(
|
||||
'%s%s',
|
||||
$scriptroot,
|
||||
$scriptfile,
|
||||
);
|
||||
|
||||
// The server is not configured to rewrite unknown requests to automatically use the router.
|
||||
$userphp = false;
|
||||
if ($_SERVER && array_key_exists('REQUEST_URI', $_SERVER)) {
|
||||
if (str_starts_with($_SERVER['REQUEST_URI'], $relativeroot)) {
|
||||
$scriptroot .= '/r.php';
|
||||
if (str_starts_with($_SERVER['REQUEST_URI'], "{$scriptroot}/r.php")) {
|
||||
$userphp = true;
|
||||
}
|
||||
}
|
||||
|
||||
if ($CFG->routerconfigured !== true || $userphp) {
|
||||
$scriptroot .= '/r.php';
|
||||
}
|
||||
|
||||
return $scriptroot;
|
||||
}
|
||||
|
||||
@@ -186,10 +190,36 @@ class router {
|
||||
// This must be done before the Routing Middleware to ensure that the route is matched correctly.
|
||||
$this->app->add(di::get(uri_normalisation_middleware::class));
|
||||
|
||||
// Add the Error Handling Middleware to the App.
|
||||
$this->add_error_handler_middleware();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the Error Handling Middleware to the RouteGroup.
|
||||
*/
|
||||
protected function add_error_handler_middleware(): void {
|
||||
// Add the Error Handling Middleware and configure it to show Moodle Errors for HTML pages.
|
||||
$errormiddleware = $this->app->addErrorMiddleware(true, true, true);
|
||||
$errorhandler = $errormiddleware->getDefaultErrorHandler();
|
||||
$errorhandler->registerErrorRenderer('text/html', routed_error_handler::class);
|
||||
$errormiddleware = new ErrorMiddleware(
|
||||
$this->app->getCallableResolver(),
|
||||
$this->app->getResponseFactory(),
|
||||
displayErrorDetails: true,
|
||||
logErrors: true,
|
||||
logErrorDetails: true,
|
||||
);
|
||||
|
||||
// Set a custom error handler for the HttpNotFoundException and HttpForbiddenException.
|
||||
// We route these to a custom error handler to ensure that the error is displayed with a feedback form.
|
||||
$errormiddleware->setErrorHandler(
|
||||
[
|
||||
HttpNotFoundException::class,
|
||||
HttpForbiddenException::class,
|
||||
],
|
||||
new router\error_handler($this->app),
|
||||
);
|
||||
|
||||
$errormiddleware->getDefaultErrorHandler()->registerErrorRenderer('text/html', router\error_renderer::class);
|
||||
|
||||
$this->app->add($errormiddleware);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -200,6 +230,8 @@ class router {
|
||||
foreach ($routegroups as $name => $collection) {
|
||||
match ($name) {
|
||||
route_loader_interface::ROUTE_GROUP_API => $this->configure_api_route($collection),
|
||||
route_loader_interface::ROUTE_GROUP_PAGE => $this->configure_standard_route($collection),
|
||||
route_loader_interface::ROUTE_GROUP_SHIM => $this->configure_shim_route($collection),
|
||||
default => null,
|
||||
};
|
||||
}
|
||||
@@ -215,6 +247,29 @@ class router {
|
||||
->add(di::get(error_handling_middleware::class))
|
||||
// Add a Middleware to set the CORS headers for all REST Responses.
|
||||
->add(di::get(cors_middleware::class))
|
||||
->add(di::get(moodle_api_authentication_middleware::class))
|
||||
->add(di::get(validation_middleware::class));
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure the Standard page Route Middleware.
|
||||
*
|
||||
* @param RouteGroupInterface $group
|
||||
*/
|
||||
protected function configure_standard_route(RouteGroupInterface $group): void {
|
||||
$group
|
||||
->add(di::get(moodle_authentication_middleware::class))
|
||||
->add(di::get(validation_middleware::class));
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure the Shim Route Middleware.
|
||||
*
|
||||
* @param RouteGroupInterface $group
|
||||
*/
|
||||
protected function configure_shim_route(RouteGroupInterface $group): void {
|
||||
$group
|
||||
// Note: In the future we may wish to add a shim middleware to notify users of updated bookmarks.
|
||||
->add(di::get(validation_middleware::class));
|
||||
}
|
||||
|
||||
|
||||
@@ -31,11 +31,13 @@ abstract class abstract_route_loader {
|
||||
*
|
||||
* @param string $namespace The namespace to get the routes for
|
||||
* @param callable $componentpathcallback A callback to get the component path for a class
|
||||
* @param null|callable $filtercallback A callback to use to filter routes before they are added
|
||||
* @return array[]
|
||||
*/
|
||||
protected function get_all_routes_in_namespace(
|
||||
string $namespace,
|
||||
callable $componentpathcallback,
|
||||
?callable $filtercallback = null,
|
||||
): array {
|
||||
$routes = [];
|
||||
|
||||
@@ -43,6 +45,9 @@ abstract class abstract_route_loader {
|
||||
$classes = \core_component::get_component_classes_in_namespace(namespace: $namespace);
|
||||
foreach (array_keys($classes) as $classname) {
|
||||
$classinfo = new \ReflectionClass($classname);
|
||||
if ($filtercallback && !$filtercallback($classname)) {
|
||||
continue;
|
||||
}
|
||||
$component = \core_component::get_component_from_classname($classname);
|
||||
$componentpath = $componentpathcallback($component);
|
||||
|
||||
@@ -135,7 +140,7 @@ abstract class abstract_route_loader {
|
||||
): ?route {
|
||||
// Fetch the route attribute from the method.
|
||||
// Each method can only have a single route attribute.
|
||||
$routeattributes = $methodinfo->getAttributes(route::class);
|
||||
$routeattributes = $methodinfo->getAttributes(route::class, \ReflectionAttribute::IS_INSTANCEOF);
|
||||
if (empty($routeattributes)) {
|
||||
return null;
|
||||
}
|
||||
@@ -166,19 +171,7 @@ abstract class abstract_route_loader {
|
||||
protected function normalise_component_path(
|
||||
string $component,
|
||||
): string {
|
||||
if ($component === 'core') {
|
||||
return $component;
|
||||
}
|
||||
[$type, $subsystem] = \core_component::normalize_component($component);
|
||||
if ($type === 'core') {
|
||||
$component = $subsystem;
|
||||
}
|
||||
|
||||
if ($component === null) {
|
||||
$component = '';
|
||||
}
|
||||
|
||||
return $component;
|
||||
return util::normalise_component_path($component);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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\router;
|
||||
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Slim\App;
|
||||
use Slim\Handlers\ErrorHandler;
|
||||
|
||||
/**
|
||||
* An Eerror Handler implementation for Moodle which is aware of the REST API.
|
||||
*
|
||||
* @package core
|
||||
* @copyright Andrew Lyons <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class error_handler extends ErrorHandler {
|
||||
/**
|
||||
* Construct a new Error Handler.
|
||||
*
|
||||
* @param \Slim\App $app
|
||||
*/
|
||||
public function __construct(
|
||||
App $app,
|
||||
) {
|
||||
parent::__construct(
|
||||
$app->getCallableResolver(),
|
||||
$app->getResponseFactory(),
|
||||
);
|
||||
|
||||
$this->registerErrorRenderer('text/html', error_renderer::class);
|
||||
}
|
||||
|
||||
#[\Override]
|
||||
protected function determineContentType(ServerRequestInterface $request): ?string {
|
||||
// For anything hitting /rest/api/v2 we will default to JSON.
|
||||
$restbase = (new \core\url('/rest/api/v2/'))->get_path();
|
||||
if (substr($request->getUri()->getPath(), 0, strlen($restbase)) === $restbase) {
|
||||
return 'application/json';
|
||||
}
|
||||
|
||||
// Fall back to the default behaviour of using the Accept header.
|
||||
return parent::determineContentType($request);
|
||||
}
|
||||
}
|
||||
+15
-3
@@ -14,8 +14,9 @@
|
||||
// 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\output;
|
||||
namespace core\router;
|
||||
|
||||
use Slim\Exception\HttpNotFoundException;
|
||||
use Slim\Interfaces\ErrorRendererInterface;
|
||||
use Throwable;
|
||||
|
||||
@@ -28,7 +29,7 @@ use Throwable;
|
||||
* @copyright Andrew Lyons <andrew@nicols.co.uk>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class routed_error_handler implements ErrorRendererInterface {
|
||||
class error_renderer implements ErrorRendererInterface {
|
||||
#[\Override]
|
||||
public function __invoke(Throwable $exception, bool $displayErrorDetails): string {
|
||||
// @codeCoverageIgnoreStart
|
||||
@@ -38,8 +39,19 @@ class routed_error_handler implements ErrorRendererInterface {
|
||||
}
|
||||
// @codeCoverageIgnoreEnd
|
||||
|
||||
if ($exception instanceof HttpNotFoundException) {
|
||||
// This is a 404 error.
|
||||
$controller = \core\di::get(\core\route\controller\page_not_found_controller::class);
|
||||
|
||||
return $controller->page_not_found_handler(
|
||||
$exception->getRequest(),
|
||||
)->getBody();
|
||||
}
|
||||
|
||||
if ($whoops = get_whoops()) {
|
||||
$whoops->sendHttpCode($exception->getCode());
|
||||
if ($exception instanceof \Slim\Exception\HttpException) {
|
||||
$whoops->sendHttpCode($exception->getCode());
|
||||
}
|
||||
$whoops->handleException($exception);
|
||||
} else {
|
||||
default_exception_handler($exception);
|
||||
@@ -0,0 +1,59 @@
|
||||
<?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\router\middleware;
|
||||
|
||||
use core\router\route;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Psr\Http\Server\RequestHandlerInterface;
|
||||
|
||||
/**
|
||||
* Middleware to check Moodle authentication.
|
||||
*
|
||||
* @package core
|
||||
* @copyright Andrew Lyons <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class moodle_api_authentication_middleware extends moodle_authentication_middleware {
|
||||
#[\Override]
|
||||
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface {
|
||||
// Get the Moodle Route from the request. We need this to determine if login is required for this page.
|
||||
$moodleroute = $request->getAttribute(route::class);
|
||||
|
||||
// Currently only Cookie authentication is supported.
|
||||
if ($moodleroute && $moodleroute->requirelogin) {
|
||||
$requirements = $moodleroute->requirelogin;
|
||||
if ($courseattributename = $requirements->get_course_attribute_name()) {
|
||||
$courseorid = $request->getAttribute($courseattributename, null);
|
||||
}
|
||||
|
||||
if ($requirements->should_require_course_login()) {
|
||||
require_course_login(
|
||||
$courseorid,
|
||||
$requirements->should_autologin_guest(),
|
||||
);
|
||||
} else if ($requirements->should_require_login()) {
|
||||
require_login(
|
||||
$courseorid,
|
||||
$requirements->should_autologin_guest(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return $handler->handle($request);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
<?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\router\middleware;
|
||||
|
||||
use core\router\route;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Psr\Http\Server\MiddlewareInterface;
|
||||
use Psr\Http\Server\RequestHandlerInterface;
|
||||
|
||||
/**
|
||||
* Middleware to check Moodle authentication.
|
||||
*
|
||||
* @package core
|
||||
* @copyright 2024 Andrew Lyons <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class moodle_authentication_middleware implements MiddlewareInterface {
|
||||
#[\Override]
|
||||
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface {
|
||||
// Get the Moodle Route from the request. We need this to determine if login is required for this page.
|
||||
$moodleroute = $request->getAttribute(route::class);
|
||||
|
||||
if ($moodleroute && $moodleroute->requirelogin) {
|
||||
$requirements = $moodleroute->requirelogin;
|
||||
if ($courseattributename = $requirements->get_course_attribute_name()) {
|
||||
$courseorid = $request->getAttribute($courseattributename, null);
|
||||
}
|
||||
|
||||
if ($requirements->should_require_course_login()) {
|
||||
require_course_login(
|
||||
$courseorid,
|
||||
$requirements->should_autologin_guest(),
|
||||
);
|
||||
} else if ($requirements->should_require_login()) {
|
||||
require_login(
|
||||
$courseorid,
|
||||
$requirements->should_autologin_guest(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return $handler->handle($request);
|
||||
}
|
||||
}
|
||||
@@ -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\router\parameters;
|
||||
|
||||
use core\param;
|
||||
use core\router\schema\parameters\mapped_property_parameter;
|
||||
use core\router\schema\referenced_object;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
|
||||
/**
|
||||
* A return URL parameter referenced in the query parameters.
|
||||
*
|
||||
* @package core
|
||||
* @copyright Andrew Lyons <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class query_returnurl extends \core\router\schema\parameters\query_parameter implements
|
||||
mapped_property_parameter,
|
||||
referenced_object
|
||||
{
|
||||
/**
|
||||
* Create a new query_returnurl parameter.
|
||||
*
|
||||
* @param string $name The name of the parameter to use for the return URL
|
||||
* @param mixed ...$extra Additional arguments
|
||||
*/
|
||||
public function __construct(
|
||||
string $name = 'returnurl',
|
||||
...$extra,
|
||||
) {
|
||||
$extra['name'] = $name;
|
||||
$extra['type'] = param::LOCALURL;
|
||||
|
||||
parent::__construct(...$extra);
|
||||
}
|
||||
|
||||
#[\Override]
|
||||
public function add_attributes_for_parameter_value(
|
||||
ServerRequestInterface $request,
|
||||
string $value,
|
||||
): ServerRequestInterface {
|
||||
return $request->withAttribute($this->name, new \core\url($value));
|
||||
}
|
||||
}
|
||||
@@ -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/>.
|
||||
|
||||
namespace core\router;
|
||||
|
||||
/**
|
||||
* Login metadata requirements for routes.
|
||||
*
|
||||
* @package core
|
||||
* @copyright Andrew Lyons <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class require_login {
|
||||
/**
|
||||
* Create a new instance of the metadata class.
|
||||
*
|
||||
* @param bool $requirelogin Whether login is required
|
||||
* @param bool $requirecourselogin Whether a course login is required
|
||||
* @param mixed $courseattributename The name of the route attribute that the course object can be found in
|
||||
* @param bool $autologinguest Whether to automatically log in as guest
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function __construct(
|
||||
/** @var bool Whether login is required */
|
||||
public bool $requirelogin = false,
|
||||
/** @var bool Whether a course login is required */
|
||||
public bool $requirecourselogin = false,
|
||||
/** @var bool The name of the route attribute that the course object can be found in */
|
||||
protected ?string $courseattributename = null,
|
||||
/** @var bool Whether to automatically log in as guest */
|
||||
public bool $autologinguest = true,
|
||||
) {
|
||||
if ($requirelogin && $requirecourselogin) {
|
||||
throw new \InvalidArgumentException('Cannot require login and course login at the same time');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the course attribute name.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_course_attribute_name(): string {
|
||||
return $this->courseattributename;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether course login is required.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function should_require_course_login(): bool {
|
||||
return $this->requirecourselogin;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether login is required.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function should_require_login(): bool {
|
||||
return $this->requirelogin;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether automatic guest login is enabled.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function should_autologin_guest(): bool {
|
||||
return $this->autologinguest;
|
||||
}
|
||||
}
|
||||
@@ -117,6 +117,12 @@ class route {
|
||||
/** @var bool Whether to abort after configuration */
|
||||
public readonly bool $abortafterconfig = false,
|
||||
|
||||
/** @var null|array Whether to require login or not */
|
||||
public readonly ?require_login $requirelogin = null,
|
||||
|
||||
/** @var string[] The list of scopes required to access this page */
|
||||
public readonly ?array $scopes = null,
|
||||
|
||||
// Note. We do not make use of these extras.
|
||||
// These allow us to add additional arguments in future versions, whilst allowing plugins to use this version.
|
||||
...$extra,
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
namespace core\router;
|
||||
|
||||
use moodle_url;
|
||||
use Psr\Container\ContainerInterface;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
|
||||
@@ -32,17 +31,6 @@ use Psr\Http\Message\ServerRequestInterface;
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
trait route_controller {
|
||||
/**
|
||||
* Constructor for Route Controllers.
|
||||
*
|
||||
* @param ContainerInterface $container
|
||||
*/
|
||||
public function __construct(
|
||||
/** @var ContainerInterface The DI Container */
|
||||
protected ContainerInterface $container,
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a Page Not Found result.
|
||||
*
|
||||
@@ -51,11 +39,11 @@ trait route_controller {
|
||||
* @return ResponseInterface
|
||||
* @throws \Slim\Exception\HttpNotFoundException
|
||||
*/
|
||||
protected function page_not_found(
|
||||
public static function page_not_found(
|
||||
ServerRequestInterface $request,
|
||||
ResponseInterface $response,
|
||||
): ResponseInterface {
|
||||
throw new \Slim\Exception\HttpNotFoundException($request);
|
||||
return util::throw_page_not_found($request, $response);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -65,13 +53,11 @@ trait route_controller {
|
||||
* @param string|moodle_url $url
|
||||
* @return ResponseInterface
|
||||
*/
|
||||
protected function redirect(
|
||||
public static function redirect(
|
||||
ResponseInterface $response,
|
||||
string|moodle_url $url,
|
||||
): ResponseInterface {
|
||||
return $response
|
||||
->withStatus(302)
|
||||
->withHeader('Location', (string) $url);
|
||||
return util::redirect($response, $url);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -85,7 +71,7 @@ trait route_controller {
|
||||
* @param null|array $excludeparams A list of any parameters to remove the URI during the redirect
|
||||
* @return ResponseInterface
|
||||
*/
|
||||
protected function redirect_to_callable(
|
||||
public static function redirect_to_callable(
|
||||
ServerRequestInterface $request,
|
||||
ResponseInterface $response,
|
||||
array|callable|string $callable,
|
||||
@@ -93,25 +79,14 @@ trait route_controller {
|
||||
?array $queryparams = null,
|
||||
?array $excludeparams = null,
|
||||
): ResponseInterface {
|
||||
// Provide defaults for the path and query params if not specified.
|
||||
if ($pathparams === null) {
|
||||
$pathparams = $request->getQueryParams();
|
||||
}
|
||||
if ($queryparams === null) {
|
||||
$queryparams = $request->getQueryParams();
|
||||
}
|
||||
|
||||
// Generate a URI from the callable and the parameters.
|
||||
$url = util::get_path_for_callable(
|
||||
return util::redirect_to_callable(
|
||||
$request,
|
||||
$response,
|
||||
$callable,
|
||||
$pathparams ?? [],
|
||||
$queryparams ?? [],
|
||||
$pathparams,
|
||||
$queryparams,
|
||||
$excludeparams,
|
||||
);
|
||||
|
||||
// Remove any params.
|
||||
$url->remove_params($excludeparams);
|
||||
|
||||
return $this->redirect($response, $url);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -18,6 +18,7 @@ namespace core\router;
|
||||
|
||||
use Slim\App;
|
||||
use Slim\Interfaces\RouteGroupInterface;
|
||||
use Slim\Interfaces\RouteInterface;
|
||||
use Slim\Routing\RouteCollectorProxy;
|
||||
|
||||
/**
|
||||
@@ -32,6 +33,8 @@ class route_loader extends abstract_route_loader implements route_loader_interfa
|
||||
public function configure_routes(App $app): array {
|
||||
return [
|
||||
route_loader_interface::ROUTE_GROUP_API => $this->configure_api_routes($app, route_loader_interface::ROUTE_GROUP_API),
|
||||
route_loader_interface::ROUTE_GROUP_PAGE => $this->configure_standard_routes($app),
|
||||
route_loader_interface::ROUTE_GROUP_SHIM => $this->configure_shim_routes($app),
|
||||
];
|
||||
}
|
||||
|
||||
@@ -59,6 +62,36 @@ class route_loader extends abstract_route_loader implements route_loader_interfa
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure all standard routes.
|
||||
*
|
||||
* @param App $app
|
||||
* @return RouteGroupInterface
|
||||
*/
|
||||
protected function configure_standard_routes(App $app): RouteGroupInterface {
|
||||
return $app->group('', function (RouteCollectorProxy $group): void {
|
||||
foreach ($this->get_all_standard_routes() as $moodleroute) {
|
||||
$slimroute = $group->map(...$moodleroute);
|
||||
$this->set_route_name_for_callable($slimroute, $moodleroute['callable']);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure all route shims.
|
||||
*
|
||||
* @param App $app
|
||||
* @return RouteGroupInterface
|
||||
*/
|
||||
protected function configure_shim_routes(App $app): RouteGroupInterface {
|
||||
return $app->group('', function (RouteCollectorProxy $group): void {
|
||||
foreach ($this->get_all_shimmed_routes() as $moodleroute) {
|
||||
$slimroute = $group->map(...$moodleroute);
|
||||
$this->set_route_name_for_callable($slimroute, $moodleroute['callable']);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch all API routes.
|
||||
*
|
||||
@@ -80,4 +113,65 @@ class route_loader extends abstract_route_loader implements route_loader_interfa
|
||||
|
||||
return $routes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch all shimmed routes.
|
||||
*
|
||||
* Shimmed routes are routes that are not part of the standard route namespace but allow backwards compatibility with
|
||||
* pages which have been moved to the new routing system.
|
||||
*
|
||||
* Note: This method caches results in MUC.
|
||||
*
|
||||
* @return array|bool|mixed
|
||||
*/
|
||||
protected function get_all_shimmed_routes(): array {
|
||||
$cache = \cache::make('core', 'routes');
|
||||
|
||||
if (!($cachedata = $cache->get('shimmed_routes'))) {
|
||||
$cachedata = $this->get_all_routes_in_namespace(
|
||||
namespace: 'route\shim',
|
||||
componentpathcallback: function (string $component): string {
|
||||
global $CFG;
|
||||
if ($component === 'core') {
|
||||
// The core component is a special case.
|
||||
// It can place routes _anywhere_ in the codebase.
|
||||
return '';
|
||||
}
|
||||
|
||||
// Use the component directory path listed in \core\componnet.
|
||||
return substr(
|
||||
\core_component::get_component_directory($component),
|
||||
strlen($CFG->dirroot) + 1,
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
$cache->set('shimmed_routes', $cachedata);
|
||||
}
|
||||
|
||||
return $cachedata;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch all standard routes.
|
||||
*
|
||||
* Note: This method caches results in MUC.
|
||||
*
|
||||
* @return array[]
|
||||
*/
|
||||
protected function get_all_standard_routes(): array {
|
||||
$cache = \cache::make('core', 'routes');
|
||||
|
||||
if (!($cachedata = $cache->get('standard_routes'))) {
|
||||
$cachedata = $this->get_all_routes_in_namespace(
|
||||
namespace: 'route\controller',
|
||||
componentpathcallback: $this->normalise_component_path(...),
|
||||
filtercallback: fn(string $classname) => !str_contains($classname, '\\shim\\'),
|
||||
);
|
||||
|
||||
$cache->set('standard_routes', $cachedata);
|
||||
}
|
||||
|
||||
return $cachedata;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,6 +31,12 @@ interface route_loader_interface {
|
||||
/** @var string The route path prefix to use for API calls */
|
||||
public const ROUTE_GROUP_API = '/api/rest/v2';
|
||||
|
||||
/** @var string The route path prefix to use for API calls */
|
||||
public const ROUTE_GROUP_SHIM = 'shim';
|
||||
|
||||
/** @var string The route path prefix to use for API calls */
|
||||
public const ROUTE_GROUP_PAGE = '/';
|
||||
|
||||
/**
|
||||
* Configure all routes for the Application.
|
||||
*
|
||||
|
||||
@@ -23,6 +23,7 @@ use core\router\route;
|
||||
use core\router\route_loader_interface;
|
||||
use core\router\schema\objects\type_base;
|
||||
use core\router\schema\response\response;
|
||||
use core\router\util;
|
||||
use core\url;
|
||||
use stdClass;
|
||||
|
||||
@@ -263,17 +264,8 @@ class specification implements
|
||||
route $route,
|
||||
): self {
|
||||
// Compile the final path, complete with component prefix.
|
||||
[$type, $subsystem] = \core_component::normalize_component($component);
|
||||
|
||||
if ($type === 'core') {
|
||||
if ($subsystem) {
|
||||
$path = "/{$subsystem}";
|
||||
} else {
|
||||
$path = "/core";
|
||||
}
|
||||
} else {
|
||||
$path = "/{$component}";
|
||||
}
|
||||
$path = "/";
|
||||
$path .= util::normalise_component_path($component);
|
||||
$path .= $route->get_path();
|
||||
|
||||
// Helper to add the path to the specification.
|
||||
|
||||
+100
-24
@@ -16,8 +16,9 @@
|
||||
|
||||
namespace core\router;
|
||||
|
||||
use moodle_url;
|
||||
use core\url;
|
||||
use GuzzleHttp\Psr7\Uri;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Slim\Routing\RouteContext;
|
||||
|
||||
@@ -31,52 +32,103 @@ use Slim\Routing\RouteContext;
|
||||
* - helpers to fetch the \core\router\route instance
|
||||
*
|
||||
* @package core
|
||||
* @copyright 2024 Andrew Lyons <[email protected]>
|
||||
* @copyright Andrew Lyons <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class util {
|
||||
/**
|
||||
* Redirect to the specified URL, carrying all parameters across too.
|
||||
*
|
||||
* @param string|moodle_url $path
|
||||
* @param string|url $path
|
||||
* @param array $excludeparams Any parameters to exclude from the query params
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
public static function redirect_with_params(
|
||||
string|moodle_url $path,
|
||||
string|url $path,
|
||||
array $excludeparams = [],
|
||||
): never {
|
||||
$params = $_GET;
|
||||
$url = new moodle_url(
|
||||
$url = new url(
|
||||
$path,
|
||||
$params,
|
||||
);
|
||||
$url->remove_params($excludeparams);
|
||||
$url->remove_params(...$excludeparams);
|
||||
|
||||
redirect($url);
|
||||
}
|
||||
|
||||
/**
|
||||
* Redirect to the route at the callable supplied.
|
||||
* Redirect to the requested callable.
|
||||
*
|
||||
* @param callable|array|string $callable
|
||||
* @param array $params Any parameters to include in the path
|
||||
* @codeCoverageIgnore
|
||||
* @param ServerRequestInterface $request
|
||||
* @param ResponseInterface $response
|
||||
* @param array|callable|string $callable
|
||||
* @param null|array $pathparams
|
||||
* @param null|array $queryparams
|
||||
* @param null|array $excludeparams A list of any parameters to remove the URI during the redirect
|
||||
* @return ResponseInterface
|
||||
*/
|
||||
public static function redirect_to_callable(
|
||||
callable|array|string $callable,
|
||||
array $params = [],
|
||||
): never {
|
||||
$params = array_merge(
|
||||
$_GET,
|
||||
$params,
|
||||
ServerRequestInterface $request,
|
||||
ResponseInterface $response,
|
||||
array|callable|string $callable,
|
||||
?array $pathparams = null,
|
||||
?array $queryparams = null,
|
||||
?array $excludeparams = null,
|
||||
): ResponseInterface {
|
||||
// Provide defaults for the path and query params if not specified.
|
||||
if ($pathparams === null) {
|
||||
$pathparams = $request->getQueryParams();
|
||||
}
|
||||
if ($queryparams === null) {
|
||||
$queryparams = $request->getQueryParams();
|
||||
}
|
||||
|
||||
// Generate a URI from the callable and the parameters.
|
||||
$url = self::get_path_for_callable(
|
||||
$callable,
|
||||
$pathparams ?? [],
|
||||
$queryparams ?? [],
|
||||
);
|
||||
|
||||
$url = self::get_path_for_callable($callable, $params, $params);
|
||||
// Remove any params.
|
||||
$url->remove_params($excludeparams);
|
||||
|
||||
redirect($url);
|
||||
return self::redirect($response, $url);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a Page Not Found result.
|
||||
*
|
||||
* @param ServerRequestInterface $request
|
||||
* @param ResponseInterface $response
|
||||
* @return ResponseInterface
|
||||
* @throws \Slim\Exception\HttpNotFoundException
|
||||
*/
|
||||
public static function throw_page_not_found(
|
||||
ServerRequestInterface $request,
|
||||
ResponseInterface $response,
|
||||
): ResponseInterface {
|
||||
throw new \Slim\Exception\HttpNotFoundException($request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Redirect to a URL.
|
||||
*
|
||||
* @param ResponseInterface $response
|
||||
* @param string|url $url
|
||||
* @return ResponseInterface
|
||||
*/
|
||||
public static function redirect(
|
||||
ResponseInterface $response,
|
||||
string|url $url,
|
||||
): ResponseInterface {
|
||||
return $response
|
||||
->withStatus(302)
|
||||
->withHeader('Location', (string) $url);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the route name for the specified callable.
|
||||
*
|
||||
@@ -103,13 +155,13 @@ class util {
|
||||
* @param string|array|callable $callable the Callable to get the URI for
|
||||
* @param array $params Any parameters to include in the path
|
||||
* @param array $queryparams Any parameters to include in the query string
|
||||
* @return moodle_url
|
||||
* @return url
|
||||
*/
|
||||
public static function get_path_for_callable(
|
||||
string|array|callable $callable,
|
||||
array $params,
|
||||
array $queryparams,
|
||||
): moodle_url {
|
||||
array $params = [],
|
||||
array $queryparams = [],
|
||||
): url {
|
||||
global $CFG;
|
||||
|
||||
$router = \core\di::get(\core\router::class);
|
||||
@@ -118,7 +170,7 @@ class util {
|
||||
|
||||
$routename = self::get_route_name_for_callable($callable);
|
||||
|
||||
return new moodle_url(
|
||||
return new url(
|
||||
url: $parser->fullUrlFor(
|
||||
new Uri($CFG->wwwroot),
|
||||
$routename,
|
||||
@@ -149,7 +201,7 @@ class util {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the instance of the \route\router\route attribute for the specified callable if one is available.
|
||||
* Get the instance of the \core\router\route attribute for the specified callable if one is available.
|
||||
*
|
||||
* @param callable|array|string $callable
|
||||
* @return null|route The route if one was found.
|
||||
@@ -227,4 +279,28 @@ class util {
|
||||
|
||||
return $methodroute;
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalise the component for use as part of the path.
|
||||
*
|
||||
* If the component is a subsystem, the `core_` prefix will be removed.
|
||||
* If the component is 'core', it will be kept.
|
||||
* All other components will use their frankenstyle name.
|
||||
*
|
||||
* @param string $component
|
||||
* @return string
|
||||
*/
|
||||
public static function normalise_component_path(
|
||||
string $component,
|
||||
): string {
|
||||
if ($component === 'core') {
|
||||
return $component;
|
||||
}
|
||||
[$type, $subsystem] = \core\component::normalize_component($component);
|
||||
if ($type === 'core') {
|
||||
return str_replace('core_', '', $subsystem);
|
||||
}
|
||||
|
||||
return $component ?? '';
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -637,7 +637,7 @@ class url {
|
||||
): self {
|
||||
global $CFG;
|
||||
|
||||
if (!$CFG->routerconfigured) {
|
||||
if ($CFG->routerconfigured !== true) {
|
||||
$path = '/r.php/' . ltrim($path, '/');
|
||||
}
|
||||
$url = new self($path, $params, $anchor);
|
||||
|
||||
@@ -4859,7 +4859,10 @@ class settings_navigation extends navigation_node {
|
||||
}
|
||||
|
||||
if (!$adminoptions->update && $adminoptions->tags) {
|
||||
$url = new moodle_url('/course/tags.php', array('id' => $course->id));
|
||||
$url = \core\router\util::get_path_for_callable([
|
||||
\core_course\route\controller\tags_controller::class,
|
||||
'administer_tags',
|
||||
], ['course' => $course->id]);
|
||||
$coursenode->add(get_string('coursetags', 'tag'), $url, self::TYPE_SETTING, null, 'coursetags', new pix_icon('i/settings', ''));
|
||||
$coursenode->get('coursetags')->set_force_into_more_menu();
|
||||
}
|
||||
|
||||
@@ -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/>.
|
||||
|
||||
namespace core\router\parameters;
|
||||
|
||||
use core\tests\router\route_testcase;
|
||||
use core\url;
|
||||
use GuzzleHttp\Psr7\ServerRequest;
|
||||
|
||||
/**
|
||||
* Tests for the query_returnurl parameter.
|
||||
*
|
||||
* @package core
|
||||
* @category test
|
||||
* @copyright Andrew Lyons <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
#[\PHPUnit\Framework\Attributes\CoversClass(query_returnurl::class)]
|
||||
final class query_returnurl_test extends route_testcase {
|
||||
public function test_returnurl_specified(): void {
|
||||
$this->resetAfterTest();
|
||||
|
||||
$param = new query_returnurl();
|
||||
$request = $param->add_attributes_for_parameter_value(
|
||||
new ServerRequest('GET', '/course/edit'),
|
||||
'/course/view',
|
||||
);
|
||||
|
||||
$this->assertInstanceOf(url::class, $request->getAttribute('returnurl'));
|
||||
$this->assertTrue(
|
||||
$request->getAttribute('returnurl')->compare(new url('/course/view')),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -41,6 +41,11 @@ final class util_test extends route_testcase {
|
||||
* Test getting the path for a callable.
|
||||
*/
|
||||
public function test_get_path_for_callable(): void {
|
||||
global $CFG;
|
||||
|
||||
$this->resetAfterTest();
|
||||
|
||||
$CFG->routerconfigured = true;
|
||||
self::load_fixture('core', 'router/route_on_class.php');
|
||||
|
||||
$this->add_route_to_route_loader(
|
||||
|
||||
+112
-2
@@ -106,8 +106,8 @@ final class router_test extends route_testcase {
|
||||
}
|
||||
|
||||
public static function basepath_provider(): \Iterator {
|
||||
yield 'Domain' => ['http://example.com', ''];
|
||||
yield 'Subdirectory' => ['http://example.com/moodle', '/moodle'];
|
||||
yield 'Domain' => ['http://example.com', '/r.php'];
|
||||
yield 'Subdirectory' => ['http://example.com/moodle', '/moodle/r.php'];
|
||||
}
|
||||
|
||||
public function test_basepath_guessed_rphp(): void {
|
||||
@@ -119,4 +119,114 @@ final class router_test extends route_testcase {
|
||||
|
||||
$this->assertEquals($wwwroot->get_path(), $router->basepath);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that the basepath is correctly guessed when the router is configured.
|
||||
*
|
||||
* @param string $wwwroot The wwwroot to use.
|
||||
* @param bool|null $configured The value of $CFG->routerconfigured.
|
||||
* @param string $requestedpath The path that was requested.
|
||||
* @param string $expected The expected basepath.
|
||||
*/
|
||||
#[\PHPUnit\Framework\Attributes\DataProvider('router_configured_basepath_provider')]
|
||||
public function test_basepath_guessed_rphp_configuration_provided(
|
||||
string $wwwroot,
|
||||
?bool $configured,
|
||||
string $requestedpath,
|
||||
string $expected,
|
||||
): void {
|
||||
global $CFG;
|
||||
|
||||
$this->resetAfterTest();
|
||||
$CFG->wwwroot = $wwwroot;
|
||||
$CFG->routerconfigured = $configured;
|
||||
$_SERVER['SCRIPT_FILENAME'] = "{$CFG->dirroot}/r.php";
|
||||
$_SERVER['REQUEST_URI'] = $requestedpath;
|
||||
|
||||
$router = di::get(router::class);
|
||||
|
||||
$this->assertEquals($expected, $router->basepath);
|
||||
}
|
||||
|
||||
/**
|
||||
* Data provider for test_basepath_guessed_rphp_configuration_provided.
|
||||
*
|
||||
* @return \Generator<string, array<bool|string|null>, mixed, void>
|
||||
*/
|
||||
public static function router_configured_basepath_provider(): \Iterator {
|
||||
global $CFG;
|
||||
|
||||
yield 'Root domain, Not configured, accessed via r.php' => [
|
||||
'http://example.com',
|
||||
null,
|
||||
'/r.php/example',
|
||||
'/r.php',
|
||||
];
|
||||
yield 'Root domain, Configured true, accessed via r.php' => [
|
||||
'http://example.com',
|
||||
true,
|
||||
"/r.php/example",
|
||||
'/r.php',
|
||||
];
|
||||
yield 'Root domain, Configured false, accessed via r.php' => [
|
||||
'http://example.com',
|
||||
false,
|
||||
'/r.php/example',
|
||||
'/r.php',
|
||||
];
|
||||
yield 'Sub directory, Not configured, accessed via r.php' => [
|
||||
'http://example.com/moodle',
|
||||
null,
|
||||
'/moodle/r.php/example',
|
||||
'/moodle/r.php',
|
||||
];
|
||||
yield 'Sub directory, Configured true, accessed via r.php' => [
|
||||
'http://example.com/moodle',
|
||||
true,
|
||||
'/moodle/r.php/example',
|
||||
'/moodle/r.php',
|
||||
];
|
||||
yield 'Sub directory, Configured false, accessed via r.php' => [
|
||||
'http://example.com/moodle',
|
||||
false,
|
||||
'/moodle/r.php/example',
|
||||
'/moodle/r.php',
|
||||
];
|
||||
yield 'Root domain, Not configured, accessed without r.php' => [
|
||||
'http://example.com',
|
||||
null,
|
||||
'/example',
|
||||
'/r.php',
|
||||
];
|
||||
yield 'Root domain, Configured true, accessed without r.php' => [
|
||||
'http://example.com',
|
||||
true,
|
||||
'/example',
|
||||
'',
|
||||
];
|
||||
yield 'Root domain, Configured false, accessed without r.php' => [
|
||||
'http://example.com',
|
||||
false,
|
||||
'/example',
|
||||
'/r.php',
|
||||
];
|
||||
yield 'Sub directory, Not configured, accessed without r.php' => [
|
||||
'http://example.com/moodle',
|
||||
null,
|
||||
'/example',
|
||||
'/moodle/r.php',
|
||||
];
|
||||
yield 'Sub directory, Configured true, accessed without r.php' => [
|
||||
'http://example.com/moodle',
|
||||
true,
|
||||
'/example',
|
||||
'/moodle',
|
||||
];
|
||||
yield 'Sub directory, Configured false, accessed without r.php' => [
|
||||
'http://example.com/moodle',
|
||||
false,
|
||||
'/example',
|
||||
'/moodle/r.php',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user