MDL-77576 core_course: Implement communication api

This commit implements the core communication api in the
core course to create or update matrix roomand its members
according to course settings. This commit will add the
communicaiton room in the course so that users can access
communication room from course.

Originally implemented as MDL-76702, MDL-76705 and MDL-76703.

Co-Authored-By: David Woloszyn <[email protected]>
Co-Authored-By: Safat Shahin <[email protected]>
This commit is contained in:
Safat
2023-05-11 13:35:16 +10:00
co-authored by David Woloszyn
parent 9a060a6d8d
commit ad5c10016a
11 changed files with 236 additions and 6 deletions
+10
View File
@@ -403,6 +403,16 @@ class course_edit_form extends moodleform {
$handler->set_parent_context($categorycontext); // For course handler only.
$handler->instance_form_definition($mform, empty($course->id) ? 0 : $course->id);
// Add communication plugins to the form.
if (core_communication\api::is_available()) {
$communication = \core_communication\api::load_by_instance(
'core_course',
'coursecommunication',
empty($course->id) ? 0 : $course->id);
$communication->form_definition($mform);
$communication->set_data($course);
}
// When two elements we need a group.
$buttonarray = array();
$classarray = array('class' => 'form-submit');
+78
View File
@@ -24,6 +24,7 @@
defined('MOODLE_INTERNAL') || die;
use core_course\external\course_summary_exporter;
use core_courseformat\base as course_format;
require_once($CFG->libdir.'/completionlib.php');
@@ -2313,6 +2314,18 @@ function create_course($data, $editoroptions = NULL) {
core_tag_tag::set_item_tags('core', 'course', $course->id, context_course::instance($course->id), $data->tags);
}
// Communication api implementation in course.
if (isset($data->selectedcommunication) && core_communication\api::is_available()) {
// Prepare the communication api date.
$courseimage = course_summary_exporter::get_course_image($course);
$communicationroomname = !empty($data->communicationroomname) ? $data->communicationroomname : $data->fullname;
$selectedcommunication = $data->selectedcommunication;
// Communication api call.
$communication = \core_communication\api::load_by_instance('core_course', 'coursecommunication', $course->id);
$communication->create_and_configure_room($selectedcommunication, $communicationroomname, $courseimage);
}
// 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.
@@ -2434,6 +2447,71 @@ function update_course($data, $editoroptions = NULL) {
$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';
}
// Communication api call.
if (!empty($provider) && core_communication\api::is_available()) {
// Prepare the communication api data.
$courseimage = course_summary_exporter::get_course_image($data);
if (!$courseimage) {
$courseimage = null;
}
// 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;
}
$communication = \core_communication\api::load_by_instance(
'core_course',
'coursecommunication',
$data->id
);
$addafterupdate = false;
if ($provider !== $communication->get_provider()) {
// If provider set to none, remove all the members.
if ($provider === 'none') {
$communication->remove_members_from_room($enrolledusers);
} else if (
// If previous provider was not none and current provider is not none, but a different provider, remove members.
$communication->get_provider() !== '' &&
$communication->get_provider() !== 'none' &&
$provider !== $communication->get_provider()
) {
$communication->remove_members_from_room($enrolledusers);
$addafterupdate = true;
} else if (
// If previous provider was none and current provider is not none, but a different provider, remove members.
($communication->get_provider() === '' || $communication->get_provider() === 'none') &&
$provider !== $communication->get_provider()
) {
$addafterupdate = true;
}
}
$communication->update_room($provider, $communicationroomname, $courseimage);
if ($addafterupdate) {
$communication->add_members_to_room($enrolledusers, false);
}
}
// Update custom fields if there are any of them in the form.
$handler = core_course\customfield\course_handler::create();
$handler->instance_form_save($data);
+1
View File
@@ -51,6 +51,7 @@ $string['aria:recommendedtab'] = 'Recommended activities';
$string['aria:modulefavourite'] = 'Star {$a} activity';
$string['browsecourseadminindex'] = 'Browse the course administration with this index.';
$string['browsesettingindex'] = 'Browse settings with this index.';
$string['communicationroomlink'] = 'Chat to course participants';
$string['completion_automatic:done'] = 'Done:';
$string['completion_automatic:failed'] = 'Failed:';
$string['completion_automatic:todo'] = 'To do:';
@@ -414,6 +414,7 @@ class icon_system_fontawesome extends icon_system_font {
'core:t/locktime' => 'fa-lock',
'core:t/markasread' => 'fa-check',
'core:t/messages' => 'fa-comments',
'core:t/messages-o' => 'fa-comments-o',
'core:t/message' => 'fa-comment-o',
'core:t/more' => 'fa-caret-down',
'core:t/move' => 'fa-arrows-v',
+34 -1
View File
@@ -4145,6 +4145,19 @@ function delete_user(stdClass $user) {
// Keep a copy of user context, we need it for event.
$usercontext = context_user::instance($user->id);
// Remove user from communication rooms immediately.
if (core_communication\api::is_enabled()) {
foreach (enrol_get_users_courses($user->id) as $course) {
$communication = \core_communication\communication_processor::load_by_instance(
'core_course',
'coursecommunication',
$course->id
);
$communication->get_room_user_provider()->remove_members_from_room([$user->id]);
$communication->delete_instance_user_mapping([$user->id]);
}
}
// Delete all grades - backup is kept in grade_grades_history table.
grade_user_delete($user->id);
@@ -5031,7 +5044,7 @@ function set_login_session_preferences() {
* failed, but you have no way of knowing which.
*/
function delete_course($courseorid, $showfeedback = true) {
global $DB;
global $DB, $CFG;
if (is_object($courseorid)) {
$courseid = $courseorid->id;
@@ -5071,6 +5084,26 @@ function delete_course($courseorid, $showfeedback = true) {
// Delete the course and related context instance.
context_helper::delete_instance(CONTEXT_COURSE, $courseid);
// Communication provider delete associated information.
$communication = \core_communication\api::load_by_instance(
'core_course',
'coursecommunication',
$course->id
);
// Update communication room membership of enrolled users.
require_once($CFG->libdir . '/enrollib.php');
$courseusers = enrol_get_course_users($courseid);
$enrolledusers = [];
foreach ($courseusers as $user) {
$enrolledusers[] = $user->id;
}
$communication->remove_members_from_room($enrolledusers);
$communication->delete_room();
$DB->delete_records("course", array("id" => $courseid));
$DB->delete_records("course_format_options", array("courseid" => $courseid));
+48
View File
@@ -4285,6 +4285,54 @@ EOD;
return !empty($this->services_support_link()) || !empty($this->page_doc_link()) || !empty($this->supportemail());
}
/**
* Helper function to decide whether to show the communication link or not.
*
* @return bool
*/
public function has_communication_links(): bool {
if (!core_communication\api::is_available()) {
return false;
}
return !empty($this->communication_link());
}
/**
* Returns the communication link, complete with html.
*
* @return string
*/
public function communication_link(): string {
if (during_initial_install()) {
return '';
}
$link = $this->communication_url() ?? '';
$commicon = $this->pix_icon('t/messages-o', '', 'moodle', ['class' => 'fa fa-comments']);
$newwindowicon = $this->pix_icon('i/externallink', get_string('opensinnewwindow'), 'moodle', ['class' => 'ml-1']);
$content = $commicon . get_string('communicationroomlink', 'course') . $newwindowicon;
$html = html_writer::tag('a', $content, ['target' => '_blank', 'href' => $link]);
return !empty($link) ? $html : '';
}
/**
* Returns the communication url for a given instance if it exists.
*
* @return string
*/
public function communication_url(): string {
global $COURSE;
$url = '';
if ($COURSE->id !== SITEID) {
$comm = \core_communication\api::load_by_instance('core_course', 'coursecommunication', $COURSE->id);
$url = $comm->get_communication_room_url();
}
return !empty($url) ? $url : '';
}
/**
* Returns the page heading menu.
*
+14 -1
View File
@@ -2309,10 +2309,22 @@ $footer-link-color: $bg-inverse-link-color !default;
@include box-shadow($popover-box-shadow);
}
.btn-footer-communication {
display: none;
position: fixed;
bottom: 5rem;
right: 2rem;
@include box-shadow($popover-box-shadow);
}
.hasstickyfooter .btn-footer-popover {
bottom: calc(1rem + #{$stickyfooter-height});
}
.hasstickyfooter .btn-footer-communication {
bottom: calc(4rem + #{$stickyfooter-height});
}
.popover.footer {
.popover-body {
padding: 0;
@@ -2339,7 +2351,8 @@ $footer-link-color: $bg-inverse-link-color !default;
.jsenabled #page-footer .footer-content-popover {
display: none;
}
.jsenabled .btn-footer-popover {
.jsenabled .btn-footer-popover,
.jsenabled .btn-footer-communication {
display: block;
z-index: $zindex-dropdown;
}
+13 -1
View File
@@ -25138,10 +25138,21 @@ h3.sectionname .inplaceeditable.inplaceeditingon .editinstructions {
right: 2rem;
}
.btn-footer-communication {
display: none;
position: fixed;
bottom: 5rem;
right: 2rem;
}
.hasstickyfooter .btn-footer-popover {
bottom: calc(1rem + max(96px, 0.9375rem * 3));
}
.hasstickyfooter .btn-footer-communication {
bottom: calc(4rem + max(96px, 0.9375rem * 3));
}
.popover.footer .popover-body {
padding: 0;
}
@@ -25164,7 +25175,8 @@ h3.sectionname .inplaceeditable.inplaceeditingon .editinstructions {
.jsenabled #page-footer .footer-content-popover {
display: none;
}
.jsenabled .btn-footer-popover {
.jsenabled .btn-footer-popover,
.jsenabled .btn-footer-communication {
display: block;
z-index: 1000;
}
+14 -1
View File
@@ -27,18 +27,31 @@
"has_popover_links": true,
"services_support": "Services and support",
"login_info": "You are logged in as cute kitten",
"moodle_release": "90210"
"moodle_release": "90210",
"has_communication_links": true,
"communication_url": "https://element:8081/#/room/#yourroom:synapse",
"communication_link": "<a href=\"#\">Communication room</a>"
}
}
}}
<footer id="page-footer" class="footer-popover bg-white">
<div data-region="footer-container-popover">
{{#output.has_communication_links}}
<button onclick="window.open('{{output.communication_url}}', '_blank')" class="btn btn-icon bg-primary text-white icon-no-margin btn-footer-communication" aria-label="{{#str}}communicationroomlink, course{{/str}}">
{{#pix}}t/messages-o, core{{/pix}}
</button>
{{/output.has_communication_links}}
<button class="btn btn-icon bg-secondary icon-no-margin btn-footer-popover" data-action="footer-popover" aria-label="{{#str}}showfooter, theme_boost{{/str}}">
{{#pix}}e/question, core{{/pix}}
</button>
</div>
<div class="footer-content-popover container" data-region="footer-content-popover">
{{#output.has_communication_links}}
<div class="footer-section p-3 border-bottom footer-link-communication">
<div class="footer-support-link">{{{ output.communication_link }}}</div>
</div>
{{/output.has_communication_links}}
{{# output.has_popover_links }}
<div class="footer-section p-3 border-bottom">
{{# output.page_doc_link }}
+13 -1
View File
@@ -25138,10 +25138,21 @@ h3.sectionname .inplaceeditable.inplaceeditingon .editinstructions {
right: 2rem;
}
.btn-footer-communication {
display: none;
position: fixed;
bottom: 5rem;
right: 2rem;
}
.hasstickyfooter .btn-footer-popover {
bottom: calc(1rem + max(96px, 0.9375rem * 3));
}
.hasstickyfooter .btn-footer-communication {
bottom: calc(4rem + max(96px, 0.9375rem * 3));
}
.popover.footer .popover-body {
padding: 0;
}
@@ -25164,7 +25175,8 @@ h3.sectionname .inplaceeditable.inplaceeditingon .editinstructions {
.jsenabled #page-footer .footer-content-popover {
display: none;
}
.jsenabled .btn-footer-popover {
.jsenabled .btn-footer-popover,
.jsenabled .btn-footer-communication {
display: block;
z-index: 1000;
}
+10 -1
View File
@@ -33,8 +33,17 @@
<footer id="page-footer" class="footer-dark bg-dark text-light">
<div class="container footer-dark-inner">
<div id="course-footer">{{{ output.course_footer }}}</div>
{{#output.has_communication_links}}
<button onclick="window.open('{{output.communication_url}}', '_blank')" class="btn btn-icon bg-primary text-white icon-no-margin btn-footer-communication" aria-label="{{#str}}communicationroomlink, course{{/str}}">
{{#pix}}t/messages-o, core{{/pix}}
</button>
{{/output.has_communication_links}}
<div class="pb-3">
{{#output.has_communication_links}}
<div class="footer-section footer-link-communication">
<div class="footer-support-link">{{{ output.communication_link }}}</div>
</div>
{{/output.has_communication_links}}
{{# output.page_doc_link }}
<div class="footer-support-link">{{{ output.page_doc_link }}}</div>
{{/ output.page_doc_link }}