mirror of
https://github.com/moodle/moodle.git
synced 2025-01-17 13:38:32 +01:00
MDL-78551 core_course: Add hooks api for course updates
This commit is contained in:
parent
d2c5d26190
commit
99b7dc147f
43
course/classes/hook/after_course_created.php
Normal file
43
course/classes/hook/after_course_created.php
Normal file
@ -0,0 +1,43 @@
|
||||
<?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\hook;
|
||||
|
||||
use stdClass;
|
||||
|
||||
/**
|
||||
* Hook after course creation.
|
||||
*
|
||||
* This hook will be dispatched after the course is created and events are fired.
|
||||
*
|
||||
* @package core_course
|
||||
* @copyright 2024 Safat Shahin <safat.shahin@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
#[\core\attribute\label('Allows plugins or features to perform actions after a course is created.')]
|
||||
#[\core\attribute\tags('course')]
|
||||
class after_course_created {
|
||||
|
||||
/**
|
||||
* Constructor for the hook.
|
||||
*
|
||||
* @param stdClass $course The course instance.
|
||||
*/
|
||||
public function __construct(
|
||||
public readonly stdClass $course,
|
||||
) {
|
||||
}
|
||||
}
|
45
course/classes/hook/after_course_updated.php
Normal file
45
course/classes/hook/after_course_updated.php
Normal file
@ -0,0 +1,45 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
namespace core_course\hook;
|
||||
|
||||
use stdClass;
|
||||
|
||||
/**
|
||||
* Hook after course updates.
|
||||
*
|
||||
* @package core_course
|
||||
* @copyright 2024 Safat Shahin <safat.shahin@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
#[\core\attribute\label('Allows plugins or features to perform actions after a course is updated.')]
|
||||
#[\core\attribute\tags('course')]
|
||||
class after_course_updated {
|
||||
|
||||
/**
|
||||
* Constructor for the hook.
|
||||
*
|
||||
* @param stdClass $course The course instance.
|
||||
* @param stdClass $oldcourse The old course instance.
|
||||
* @param bool $changeincoursecat Whether the course category has changed.
|
||||
*/
|
||||
public function __construct(
|
||||
public readonly stdClass $course,
|
||||
public readonly stdClass $oldcourse,
|
||||
public readonly bool $changeincoursecat = false,
|
||||
) {
|
||||
}
|
||||
}
|
59
course/classes/hook/before_course_delete.php
Normal file
59
course/classes/hook/before_course_delete.php
Normal file
@ -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_course\hook;
|
||||
|
||||
use stdClass;
|
||||
use Psr\EventDispatcher\StoppableEventInterface;
|
||||
|
||||
/**
|
||||
* Hook before course deletion.
|
||||
*
|
||||
* @package core_course
|
||||
* @copyright 2024 Safat Shahin <safat.shahin@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
#[\core\attribute\label('Allows plugins or features to perform actions before a course is deleted.')]
|
||||
#[\core\attribute\tags('course')]
|
||||
class before_course_delete implements
|
||||
StoppableEventInterface {
|
||||
|
||||
/**
|
||||
* @var bool Whether the propagation of this event has been stopped.
|
||||
*/
|
||||
protected bool $stopped = false;
|
||||
|
||||
/**
|
||||
* Constructor for the hook.
|
||||
*
|
||||
* @param stdClass $course The course instance.
|
||||
*/
|
||||
public function __construct(
|
||||
public readonly stdClass $course,
|
||||
) {
|
||||
}
|
||||
|
||||
public function isPropagationStopped(): bool {
|
||||
return $this->stopped;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop the propagation of this event.
|
||||
*/
|
||||
public function stop(): void {
|
||||
$this->stopped = true;
|
||||
}
|
||||
}
|
@ -435,6 +435,16 @@ class course_edit_form extends moodleform {
|
||||
$mform->addElement('hidden', 'id', null);
|
||||
$mform->setType('id', PARAM_INT);
|
||||
|
||||
// Communication api call to set the communication data in the form for handling actions for group feature changes.
|
||||
// We only need to set the data for courses already created.
|
||||
if (!empty($course->id)) {
|
||||
$communication = core_communication\helper::load_by_course(
|
||||
courseid: $course->id,
|
||||
context: $coursecontext,
|
||||
);
|
||||
$communication->set_data($course);
|
||||
}
|
||||
|
||||
// Prepare custom fields data.
|
||||
$handler->instance_form_before_set_data($course);
|
||||
// Finally set the current form data
|
||||
|
181
course/lib.php
181
course/lib.php
@ -2119,6 +2119,14 @@ function create_course($data, $editoroptions = NULL) {
|
||||
|
||||
$event->trigger();
|
||||
|
||||
$data->id = $newcourseid;
|
||||
|
||||
// Dispatch the hook for post course create actions.
|
||||
$hook = new \core_course\hook\after_course_created(
|
||||
course: $data,
|
||||
);
|
||||
\core\di::get(\core\hook\manager::class)->dispatch($hook);
|
||||
|
||||
// Setup the blocks
|
||||
blocks_add_default_course_blocks($course);
|
||||
|
||||
@ -2141,33 +2149,6 @@ function create_course($data, $editoroptions = NULL) {
|
||||
if (isset($data->tags)) {
|
||||
core_tag_tag::set_item_tags('core', 'course', $course->id, $context, $data->tags);
|
||||
}
|
||||
// Set up communication.
|
||||
if (core_communication\api::is_available()) {
|
||||
// Check for default provider config setting.
|
||||
$defaultprovider = get_config('moodlecourse', 'coursecommunicationprovider');
|
||||
$provider = (isset($data->selectedcommunication)) ? $data->selectedcommunication : $defaultprovider;
|
||||
|
||||
if (!empty($provider)) {
|
||||
// Prepare the communication api data.
|
||||
$courseimage = course_get_courseimage($course);
|
||||
$communicationroomname = !empty($data->communicationroomname) ? $data->communicationroomname : $data->fullname;
|
||||
|
||||
// Communication api call.
|
||||
$communication = \core_communication\api::load_by_instance(
|
||||
context: $context,
|
||||
component: 'core_course',
|
||||
instancetype: 'coursecommunication',
|
||||
instanceid: $course->id,
|
||||
provider: $provider,
|
||||
);
|
||||
$communication->create_and_configure_room(
|
||||
$communicationroomname,
|
||||
$courseimage ?: null,
|
||||
$data,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Save custom fields if there are any of them in the form.
|
||||
$handler = core_course\customfield\course_handler::create();
|
||||
// Make sure to set the handler's parent context first.
|
||||
@ -2291,139 +2272,6 @@ function update_course($data, $editoroptions = NULL) {
|
||||
if (isset($data->enablecompletion) && $data->enablecompletion == COMPLETION_DISABLED) {
|
||||
$data->showcompletionconditions = null;
|
||||
}
|
||||
|
||||
// Check if provider is selected.
|
||||
$provider = $data->selectedcommunication ?? null;
|
||||
// If the course moved to hidden category, set provider to none.
|
||||
if ($changesincoursecat && empty($data->visible)) {
|
||||
$provider = 'none';
|
||||
}
|
||||
|
||||
// Attempt to get the communication provider if it wasn't provided in the data.
|
||||
if (empty($provider) && core_communication\api::is_available()) {
|
||||
$provider = \core_communication\api::load_by_instance(
|
||||
context: $context,
|
||||
component: 'core_course',
|
||||
instancetype: 'coursecommunication',
|
||||
instanceid: $data->id,
|
||||
)->get_provider();
|
||||
}
|
||||
|
||||
// Communication api call.
|
||||
if (!empty($provider) && core_communication\api::is_available()) {
|
||||
// Prepare the communication api data.
|
||||
$courseimage = course_get_courseimage($data);
|
||||
|
||||
// This nasty logic is here because of hide course doesn't pass anything in the data object.
|
||||
if (!empty($data->communicationroomname)) {
|
||||
$communicationroomname = $data->communicationroomname;
|
||||
} else {
|
||||
$communicationroomname = $data->fullname ?? $oldcourse->fullname;
|
||||
}
|
||||
|
||||
// Update communication room membership of enrolled users.
|
||||
require_once($CFG->libdir . '/enrollib.php');
|
||||
$courseusers = enrol_get_course_users($data->id);
|
||||
$enrolledusers = [];
|
||||
|
||||
foreach ($courseusers as $user) {
|
||||
$enrolledusers[] = $user->id;
|
||||
}
|
||||
|
||||
// Existing communication provider.
|
||||
$communication = \core_communication\api::load_by_instance(
|
||||
context: $context,
|
||||
component: 'core_course',
|
||||
instancetype: 'coursecommunication',
|
||||
instanceid: $data->id,
|
||||
);
|
||||
$existingprovider = $communication->get_provider();
|
||||
$addusersrequired = false;
|
||||
$enablenewprovider = false;
|
||||
$instanceexists = true;
|
||||
|
||||
// Action required changes if provider has changed.
|
||||
if ($provider !== $existingprovider) {
|
||||
// Provider changed, flag new one to be enabled.
|
||||
$enablenewprovider = true;
|
||||
|
||||
// If provider set to none, remove all the members from previous provider.
|
||||
if ($provider === 'none' && $existingprovider !== '') {
|
||||
$communication->remove_members_from_room($enrolledusers);
|
||||
} else if (
|
||||
// If previous provider was not none and current provider is not none,
|
||||
// remove members from previous provider.
|
||||
$existingprovider !== '' &&
|
||||
$existingprovider !== 'none'
|
||||
) {
|
||||
$communication->remove_members_from_room($enrolledusers);
|
||||
$addusersrequired = true;
|
||||
} else if (
|
||||
// If previous provider was none and current provider is not none,
|
||||
// remove members from previous provider.
|
||||
($existingprovider === '' || $existingprovider === 'none')
|
||||
) {
|
||||
$addusersrequired = true;
|
||||
}
|
||||
|
||||
// Disable previous provider, if one was enabled.
|
||||
if ($existingprovider !== '' && $existingprovider !== 'none') {
|
||||
$communication->update_room(
|
||||
active: \core_communication\processor::PROVIDER_INACTIVE,
|
||||
);
|
||||
}
|
||||
|
||||
// Switch to the newly selected provider so it can be updated.
|
||||
if ($provider !== 'none') {
|
||||
$communication = \core_communication\api::load_by_instance(
|
||||
context: $context,
|
||||
component: 'core_course',
|
||||
instancetype: 'coursecommunication',
|
||||
instanceid: $data->id,
|
||||
provider: $provider,
|
||||
);
|
||||
|
||||
// Create it if it does not exist.
|
||||
if ($communication->get_provider() === '') {
|
||||
$communication->create_and_configure_room(
|
||||
communicationroomname: $communicationroomname,
|
||||
avatar: $courseimage,
|
||||
instance: $data
|
||||
);
|
||||
|
||||
$communication = \core_communication\api::load_by_instance(
|
||||
context: $context,
|
||||
component: 'core_course',
|
||||
instancetype: 'coursecommunication',
|
||||
instanceid: $data->id,
|
||||
provider: $provider,
|
||||
);
|
||||
|
||||
$addusersrequired = true;
|
||||
$instanceexists = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($provider !== 'none' && $instanceexists) {
|
||||
// Update the currently enabled provider's room data.
|
||||
// Newly created providers do not need to run this, the create process handles it.
|
||||
$communication->update_room(
|
||||
active: $enablenewprovider ? \core_communication\processor::PROVIDER_ACTIVE : null,
|
||||
communicationroomname: $communicationroomname,
|
||||
avatar: $courseimage,
|
||||
instance: $data,
|
||||
);
|
||||
}
|
||||
|
||||
// Complete room membership tasks if required.
|
||||
// Newly created providers complete the user mapping but do not queue the task
|
||||
// (it will be handled by the room creation task).
|
||||
if ($addusersrequired) {
|
||||
$communication->add_members_to_room($enrolledusers, $instanceexists);
|
||||
}
|
||||
}
|
||||
|
||||
// Update custom fields if there are any of them in the form.
|
||||
$handler = core_course\customfield\course_handler::create();
|
||||
$handler->instance_form_save($data);
|
||||
@ -2484,6 +2332,14 @@ function update_course($data, $editoroptions = NULL) {
|
||||
|
||||
$event->trigger();
|
||||
|
||||
// Dispatch the hook for post course update actions.
|
||||
$hook = new \core_course\hook\after_course_updated(
|
||||
course: $data,
|
||||
oldcourse: $oldcourse,
|
||||
changeincoursecat: $changesincoursecat,
|
||||
);
|
||||
\core\di::get(\core\hook\manager::class)->dispatch($hook);
|
||||
|
||||
if ($oldcourse->format !== $course->format) {
|
||||
// Remove all options stored for the previous format
|
||||
// We assume that new course format migrated everything it needed watching trigger
|
||||
@ -5093,7 +4949,10 @@ function course_get_communication_instance_data(int $courseid): array {
|
||||
*/
|
||||
function course_update_communication_instance_data(stdClass $data): void {
|
||||
$data->id = $data->instanceid; // For correct use in update_course.
|
||||
update_course($data);
|
||||
core_communication\helper::update_course_communication_instance(
|
||||
course: $data,
|
||||
changesincoursecat: false,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -7388,35 +7388,6 @@ class courselib_test extends advanced_testcase {
|
||||
$this->assertEquals($course->fullname, $heading);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the course_update_communication_instance_data() function.
|
||||
*
|
||||
* @covers ::course_update_communication_instance_data
|
||||
*/
|
||||
public function test_course_update_communication_instance_data(): void {
|
||||
$this->resetAfterTest();
|
||||
$course = $this->getDataGenerator()->create_course();
|
||||
|
||||
// Set some data to update with.
|
||||
$data = new stdClass();
|
||||
$data->instanceid = $course->id;
|
||||
$data->fullname = 'newname';
|
||||
|
||||
// These should not be the same yet.
|
||||
$this->assertNotEquals($course->fullname, $data->fullname);
|
||||
|
||||
// Use the callback function to update the course with the data.
|
||||
component_callback(
|
||||
'core_course',
|
||||
'update_communication_instance_data',
|
||||
[$data]
|
||||
);
|
||||
|
||||
// Get the course and check it was updated.
|
||||
$course = get_course($course->id);
|
||||
$this->assertEquals($course->fullname, $data->fullname);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test course_section_view() function
|
||||
*
|
||||
|
@ -307,14 +307,8 @@ $PAGE->set_heading($course->fullname);
|
||||
echo $OUTPUT->header();
|
||||
|
||||
// Show communication room status notification.
|
||||
if (core_communication\api::is_available() && has_capability('moodle/course:update', $context)) {
|
||||
$communication = \core_communication\api::load_by_instance(
|
||||
$context,
|
||||
'core_course',
|
||||
'coursecommunication',
|
||||
$course->id
|
||||
);
|
||||
$communication->show_communication_room_status_notification();
|
||||
if (has_capability('moodle/course:update', $context)) {
|
||||
core_communication\helper::get_course_communication_status_notification($course);
|
||||
}
|
||||
|
||||
if ($USER->editing == 1) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user