diff --git a/communication/amd/build/providerchooser.min.js b/communication/amd/build/providerchooser.min.js index b62029acef428..1504f7e6ab590 100644 --- a/communication/amd/build/providerchooser.min.js +++ b/communication/amd/build/providerchooser.min.js @@ -7,6 +7,6 @@ define("core_communication/providerchooser",["exports"],(function(_exports){Obje * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later * @since 4.2 */ -const Selectors_fields={selector:'[data-communicationchooser-field="selector"]',updateButton:'[data-communicationchooser-field="updateButton"]'};_exports.init=()=>{document.querySelector(Selectors_fields.selector).addEventListener("change",(e=>{const form=e.target.closest("form"),updateButton=form.querySelector(Selectors_fields.updateButton),fieldset=updateButton.closest("fieldset"),url=new URL(form.action);url.hash=fieldset.id,form.action=url.toString(),updateButton.click()}))}})); +const Selectors_fields={selector:'[data-communicationchooser-field="selector"]',updateButton:'[data-communicationchooser-field="updateButton"]'};_exports.init=()=>{document.querySelector(Selectors_fields.selector).addEventListener("change",(e=>{const form=e.target.closest("form"),updateButton=form.querySelector(Selectors_fields.updateButton),url=new URL(form.action);form.action=url.toString(),updateButton.click()}))}})); //# sourceMappingURL=providerchooser.min.js.map \ No newline at end of file diff --git a/communication/amd/build/providerchooser.min.js.map b/communication/amd/build/providerchooser.min.js.map index 2dd6bb97d4d5e..2869a533188b3 100644 --- a/communication/amd/build/providerchooser.min.js.map +++ b/communication/amd/build/providerchooser.min.js.map @@ -1 +1 @@ -{"version":3,"file":"providerchooser.min.js","sources":["../src/providerchooser.js"],"sourcesContent":["\n// This file is part of Moodle - http://moodle.org/ //\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * Communication provider selection handler.\n *\n * @module core_communication/communicationchooser\n * @copyright 2023 Safat Shahin \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n * @since 4.2\n */\n\nconst Selectors = {\n fields: {\n selector: '[data-communicationchooser-field=\"selector\"]',\n updateButton: '[data-communicationchooser-field=\"updateButton\"]',\n },\n};\n\n/**\n * Initialise the format chooser.\n */\nexport const init = () => {\n document.querySelector(Selectors.fields.selector).addEventListener('change', e => {\n const form = e.target.closest('form');\n const updateButton = form.querySelector(Selectors.fields.updateButton);\n const fieldset = updateButton.closest('fieldset');\n\n const url = new URL(form.action);\n url.hash = fieldset.id;\n\n form.action = url.toString();\n updateButton.click();\n });\n};\n"],"names":["Selectors","selector","updateButton","document","querySelector","addEventListener","e","form","target","closest","fieldset","url","URL","action","hash","id","toString","click"],"mappings":";;;;;;;;;MAwBMA,iBACM,CACJC,SAAU,+CACVC,aAAc,kEAOF,KAChBC,SAASC,cAAcJ,iBAAiBC,UAAUI,iBAAiB,UAAUC,UACnEC,KAAOD,EAAEE,OAAOC,QAAQ,QACxBP,aAAeK,KAAKH,cAAcJ,iBAAiBE,cACnDQ,SAAWR,aAAaO,QAAQ,YAEhCE,IAAM,IAAIC,IAAIL,KAAKM,QACzBF,IAAIG,KAAOJ,SAASK,GAEpBR,KAAKM,OAASF,IAAIK,WAClBd,aAAae"} \ No newline at end of file +{"version":3,"file":"providerchooser.min.js","sources":["../src/providerchooser.js"],"sourcesContent":["\n// This file is part of Moodle - http://moodle.org/ //\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * Communication provider selection handler.\n *\n * @module core_communication/communicationchooser\n * @copyright 2023 Safat Shahin \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n * @since 4.2\n */\n\nconst Selectors = {\n fields: {\n selector: '[data-communicationchooser-field=\"selector\"]',\n updateButton: '[data-communicationchooser-field=\"updateButton\"]',\n },\n};\n\n/**\n * Initialise the format chooser.\n */\nexport const init = () => {\n document.querySelector(Selectors.fields.selector).addEventListener('change', e => {\n const form = e.target.closest('form');\n const updateButton = form.querySelector(Selectors.fields.updateButton);\n const url = new URL(form.action);\n\n form.action = url.toString();\n updateButton.click();\n });\n};\n"],"names":["Selectors","selector","updateButton","document","querySelector","addEventListener","e","form","target","closest","url","URL","action","toString","click"],"mappings":";;;;;;;;;MAwBMA,iBACM,CACJC,SAAU,+CACVC,aAAc,kEAOF,KAChBC,SAASC,cAAcJ,iBAAiBC,UAAUI,iBAAiB,UAAUC,UACnEC,KAAOD,EAAEE,OAAOC,QAAQ,QACxBP,aAAeK,KAAKH,cAAcJ,iBAAiBE,cACnDQ,IAAM,IAAIC,IAAIJ,KAAKK,QAEzBL,KAAKK,OAASF,IAAIG,WAClBX,aAAaY"} \ No newline at end of file diff --git a/communication/amd/src/providerchooser.js b/communication/amd/src/providerchooser.js index ccfbe69b8907a..1e0b35d880d0d 100644 --- a/communication/amd/src/providerchooser.js +++ b/communication/amd/src/providerchooser.js @@ -36,10 +36,7 @@ export const init = () => { document.querySelector(Selectors.fields.selector).addEventListener('change', e => { const form = e.target.closest('form'); const updateButton = form.querySelector(Selectors.fields.updateButton); - const fieldset = updateButton.closest('fieldset'); - const url = new URL(form.action); - url.hash = fieldset.id; form.action = url.toString(); updateButton.click(); diff --git a/communication/classes/api.php b/communication/classes/api.php index 93dbdff90528d..1698f719313a4 100644 --- a/communication/classes/api.php +++ b/communication/classes/api.php @@ -148,8 +148,6 @@ public function form_definition( $PAGE->requires->js_call_amd('core_communication/providerchooser', 'init'); - $mform->addElement('header', 'communication', get_string('communication', 'communication')); - // List the communication providers. $mform->addElement( 'select', diff --git a/communication/classes/form/configure_form.php b/communication/classes/form/configure_form.php new file mode 100644 index 0000000000000..e3cc7f6a4e784 --- /dev/null +++ b/communication/classes/form/configure_form.php @@ -0,0 +1,87 @@ +. + +/** + * Configure communication for a given instance - the form definition. + * + * @package core_communication + * @copyright 2023 David Woloszyn + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace core_communication\form; + +defined('MOODLE_INTERNAL') || die(); + +require_once($CFG->libdir.'/formslib.php'); + +/** + * Defines the configure communication form. + */ +class configure_form extends \moodleform { + + /** + * @var \core_communication\api $communication The communication api object. + */ + protected $communication; + + /** + * Defines the form fields. + */ + public function definition() { + $mform = $this->_form; + $instanceid = $this->_customdata['instanceid']; + $instancetype = $this->_customdata['instancetype']; + $component = $this->_customdata['component']; + $instance = $this->_customdata['instance']; + + // Add communication plugins to the form. + $this->communication = \core_communication\api::load_by_instance( + $component, + $instancetype, + $instanceid + ); + $this->communication->form_definition($mform); + $this->communication->set_data($instance); + + // Form buttons. + $buttonarray = []; + $buttonarray[] = $mform->createElement('submit', 'saveandreturn', get_string('savechanges')); + $buttonarray[] = $mform->createElement('cancel'); + $mform->addGroup($buttonarray, 'buttonar', '', [' '], false); + $mform->closeHeaderBefore('buttonar'); + + // Hidden elements. + $mform->addElement('hidden', 'instanceid', $instanceid); + $mform->setType('instanceid', PARAM_INT); + $mform->addElement('hidden', 'instancetype', $instancetype); + $mform->setType('instancetype', PARAM_TEXT); + $mform->addElement('hidden', 'component', $component); + $mform->setType('component', PARAM_TEXT); + + // Finally set the current form data. + $this->set_data($instance); + } + + /** + * Fill in the communication page data depending on provider selected. + */ + public function definition_after_data() { + $mform = $this->_form; + // Add communication plugins to the form with respect to the provider. + $this->communication->form_definition_for_provider($mform); + } +} diff --git a/communication/configure.php b/communication/configure.php new file mode 100644 index 0000000000000..36724e082f84c --- /dev/null +++ b/communication/configure.php @@ -0,0 +1,86 @@ +. + +/** + * Configure communication for a given instance. + * + * @package core_communication + * @copyright 2023 David Woloszyn + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +require_once('../config.php'); +require_once('lib.php'); + +require_login(); + +$instanceid = required_param('instanceid', PARAM_INT); +$instancetype = required_param('instancetype', PARAM_TEXT); +$component = required_param('component', PARAM_COMPONENT); + +$instanceinfo = [ + 'instanceid' => $instanceid, + 'instancetype' => $instancetype, + 'component' => $component, +]; + +// Requires communication to be enabled. +if (!core_communication\api::is_available()) { + throw new \moodle_exception('communicationdisabled', 'communication'); +} + +// Attempt to load the communication instance with the provided params. +$communication = \core_communication\api::load_by_instance($component, $instancetype, $instanceid); + +// No communication, no way this form can be used. +if (!$communication) { + throw new \moodle_exception('nocommunicationinstance', 'communication'); +} + +// Set variables according to the component callback and use them on the page. +list($instance, $context, $heading, $returnurl) = component_callback( + $component, + 'get_communication_instance_data', + [$instanceid] +); + +// Set up the page. +$PAGE->set_context($context); +$PAGE->set_url('/communication/configure.php', $instanceinfo); +$PAGE->set_title(get_string('communication', 'communication')); +$PAGE->set_heading($heading); +$PAGE->add_body_class('limitedwidth'); + +// Append the instance data before passing to form object. +$instanceinfo['instance'] = $instance; +// Get our form definitions. +$form = new \core_communication\form\configure_form(null, $instanceinfo); + +if ($form->is_cancelled()) { + + redirect($returnurl); + +} else if ($data = $form->get_data()) { + + component_callback($component, 'update_communication_instance_data', [$data]); + redirect($returnurl); +} + +// Display the page contents. +echo $OUTPUT->header(); +echo $OUTPUT->heading(get_string('communication', 'communication'), 2); +$form->display(); +echo $OUTPUT->footer(); diff --git a/communication/provider/matrix/tests/behat/matrix_form_fields.feature b/communication/provider/matrix/tests/behat/matrix_form_fields.feature index f403db6fd6a08..78ea9fdbedc52 100644 --- a/communication/provider/matrix/tests/behat/matrix_form_fields.feature +++ b/communication/provider/matrix/tests/behat/matrix_form_fields.feature @@ -19,35 +19,30 @@ Feature: Communication matrix form field Given a Matrix mock server is configured And I log in as "teacher1" And I am on "Test course" course homepage - When I navigate to "Settings" in current page administration - And I click on "Communication" "link" + When I navigate to "Communication" in current page administration And I set the field "id_selectedcommunication" to "Matrix" And I wait to be redirected And I should see "Room name" And I set the field "id_communicationroomname" to "Sampleroomname" - And I press "Save and display" - And I navigate to "Settings" in current page administration - And I expand all fieldsets + And I press "Save changes" + And I navigate to "Communication" in current page administration Then the field "id_communicationroomname" matches value "Sampleroomname" Scenario: I can add room topic for matrix room Given a Matrix mock server is configured And I log in as "teacher1" And I am on "Test course" course homepage - When I navigate to "Settings" in current page administration - And I click on "Communication" "link" + When I navigate to "Communication" in current page administration And I set the field "id_selectedcommunication" to "Matrix" And I wait to be redirected And I should see "Room name" And I should see "Room topic" And I set the field "id_communicationroomname" to "Sampleroomname" And I set the field "id_matrixroomtopic" to "Sampleroomtopic" - And I press "Save and display" - And I navigate to "Settings" in current page administration - And I click on "Communication" "link" + And I press "Save changes" + And I navigate to "Communication" in current page administration Then the field "id_communicationroomname" matches value "Sampleroomname" And I press "Cancel" And I run all adhoc tasks - And I navigate to "Settings" in current page administration - And I click on "Communication" "link" + And I navigate to "Communication" in current page administration And the field "id_matrixroomtopic" matches value "Sampleroomtopic" diff --git a/communication/provider/matrix/tests/behat/matrix_room_link.feature b/communication/provider/matrix/tests/behat/matrix_room_link.feature index b20a64fe4e58e..0b968a7321019 100644 --- a/communication/provider/matrix/tests/behat/matrix_room_link.feature +++ b/communication/provider/matrix/tests/behat/matrix_room_link.feature @@ -37,10 +37,11 @@ Feature: Communication matrix And ".footer-link-communication" "css_element" should be visible Scenario: I cannot see the matrix room link when communication provider is disabled - Given I am on the "Test course" "Course editing" page logged in as "teacher1" + Given I am on the "Test course" "Course" page logged in as "teacher1" + When I navigate to "Communication" in current page administration And I set the following fields to these values: | selectedcommunication | none | - And I press "Save and display" + And I press "Save changes" And I run all adhoc tasks And I reload the page Then ".btn-footer-communication" "css_element" should not be visible diff --git a/communication/tests/api_test.php b/communication/tests/api_test.php index f8400cdac5b75..0c9c3251d6597 100644 --- a/communication/tests/api_test.php +++ b/communication/tests/api_test.php @@ -191,7 +191,7 @@ public function test_create_and_configure_room(): void { */ public function test_create_and_configure_room_without_communication_provider_selected(): void { // Get the course by disabling communication so that we can create it manually calling the api. - $course = $this->getDataGenerator()->create_course(); + $course = $this->get_course('Sampleroom', 'none'); // Test the tasks added. $adhoctask = \core\task\manager::get_adhoc_tasks('\\core_communication\\task\\create_and_configure_room_task'); diff --git a/communication/tests/behat/communication_configuration.feature b/communication/tests/behat/communication_configuration.feature new file mode 100644 index 0000000000000..a311c19519f86 --- /dev/null +++ b/communication/tests/behat/communication_configuration.feature @@ -0,0 +1,41 @@ +@communication +Feature: Access the communication configuration page + As an editing teacher + See dynamic form fields based on selected provider + + Background: Set up teachers and course for the communication confifiguration page + Given I enable communication experimental feature + And the following "users" exist: + | username | firstname | lastname | email | + | teacher1 | Teacher | 1 | teacher1@example.com | + | teacher2 | Teacher | 2 | teacher2@example.com | + And the following "courses" exist: + | fullname | shortname | category | selectedcommunication | + | Test course | Test course | 0 | none | + And the following "course enrolments" exist: + | user | course | role | + | teacher1 | Test course | editingteacher | + | teacher2 | Test course | teacher | + + Scenario: A teacher with the correct capability can access the communication configuration page + Given I am on the "Test course" "Course" page logged in as "teacher1" + When I navigate to "Communication" in current page administration + Then I should see "Communication" + + Scenario: A teacher without the correct capability cannot access the communication configuration page + Given I am on the "Test course" "Course" page logged in as "teacher2" + Then "Communication" "link" should not exist in current page administration + + Scenario: I cannot see the communication link when communication provider is disabled + Given I disable communication experimental feature + And I am on the "Test course" "Course" page logged in as "teacher1" + Then "Communication" "link" should not exist in current page administration + + @javascript + Scenario: The communication form fields toggle dynamically when valid provider is set + Given I am on the "Test course" "Course" page logged in as "teacher1" + When I navigate to "Communication" in current page administration + And I set the following fields to these values: + | selectedcommunication | communication_matrix | + Then I should see "Room name" + And I should see "Room topic" diff --git a/course/edit_form.php b/course/edit_form.php index 73d630e158104..c5f2b44611e8f 100644 --- a/course/edit_form.php +++ b/course/edit_form.php @@ -401,28 +401,6 @@ function definition() { $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()) { - - $instanceconfig = core_communication\processor::PROVIDER_NONE; - // For new courses. - if (empty($course->id)) { - $instanceid = 0; - if (!empty($courseconfig->coursecommunicationprovider)) { - $instanceconfig = $courseconfig->coursecommunicationprovider; - } - } else { - // For existing courses. - $instanceid = $course->id; - } - - $communication = \core_communication\api::load_by_instance( - 'core_course', - 'coursecommunication', - $instanceid); - $communication->form_definition($mform, $instanceconfig); - $communication->set_data($course); - } // When two elements we need a group. $buttonarray = array(); @@ -494,15 +472,6 @@ function definition_after_data() { $handler = core_course\customfield\course_handler::create(); $handler->instance_form_definition_after_data($mform, empty($courseid) ? 0 : $courseid); - // 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_for_provider($mform); - } } /** diff --git a/course/lib.php b/course/lib.php index 31e451e0a1ab9..5f6bb58817a44 100644 --- a/course/lib.php +++ b/course/lib.php @@ -2322,17 +2322,24 @@ function create_course($data, $editoroptions = NULL) { if (isset($data->tags)) { 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, $data); + // 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_summary_exporter::get_course_image($course); + $communicationroomname = !empty($data->communicationroomname) ? $data->communicationroomname : $data->fullname; + // Communication api call. + $communication = \core_communication\api::load_by_instance( + 'core_course', + 'coursecommunication', + $course->id + ); + $communication->create_and_configure_room($provider, $communicationroomname, $courseimage, $data); + } } // Save custom fields if there are any of them in the form. @@ -2463,6 +2470,11 @@ function update_course($data, $editoroptions = NULL) { $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('core_course', 'coursecommunication', $data->id)->get_provider(); + } + // Communication api call. if (!empty($provider) && core_communication\api::is_available()) { // Prepare the communication api data. @@ -4007,6 +4019,7 @@ function course_get_user_navigation_options($context, $course = null) { 'participants' => false, 'search' => false, 'tags' => false, + 'communication' => false, ]; $options->blogs = !empty($CFG->enableblogs) && @@ -4078,6 +4091,10 @@ function course_get_user_navigation_options($context, $course = null) { $options->grades = $grades; } + if (\core_communication\api::is_available()) { + $options->communication = has_capability('moodle/course:configurecoursecommunication', $context); + } + if (\core_competency\api::is_enabled()) { $capabilities = array('moodle/competency:coursecompetencyview', 'moodle/competency:coursecompetencymanage'); $options->competencies = has_any_capability($capabilities, $context); @@ -5133,3 +5150,32 @@ function course_output_fragment_new_base_form($args) { return $o; } + +/** + * Get course specific data for configuring a communication instance. + * + * @param integer $courseid The course id. + * @return array Returns course data, context and heading. + */ +function course_get_communication_instance_data(int $courseid): array { + // Do some checks and prepare instance specific data. + $course = get_course($courseid); + require_login($course); + $context = context_course::instance($course->id); + require_capability('moodle/course:configurecoursecommunication', $context); + + $heading = $course->fullname; + $returnurl = new moodle_url('/course/view.php', ['id' => $courseid]); + + return [$course, $context, $heading, $returnurl]; +} + +/** + * Update a course using communication configuration data. + * + * @param stdClass $data The data to update the course with. + */ +function course_update_communication_instance_data(stdClass $data): void { + $data->id = $data->instanceid; // For correct use in update_course. + update_course($data); +} diff --git a/course/tests/behat/course_communication_defaults.feature b/course/tests/behat/course_communication_defaults.feature index c7fc5982fb2f5..d314bef4f09da 100644 --- a/course/tests/behat/course_communication_defaults.feature +++ b/course/tests/behat/course_communication_defaults.feature @@ -1,4 +1,4 @@ -@core @core_course +@core @core_course @communication Feature: Course communication In order to create a new communication room in for course As an admin @@ -7,22 +7,23 @@ Feature: Course communication Background: Given the following "courses" exist: - | fullname | shortname | - | Test course | C | + | fullname | shortname | + | Course 1 | C1 | And I enable communication experimental feature And I log in as "admin" Scenario: I should have matrix plugin by default for new courses Given I go to the courses management page - And I should see the "Categories" management page And I click on category "Category 1" in the management interface - And I should see the "Course categories and courses" management page And I follow "Create new course" - When I click on "Communication" "link" + And I set the following fields to these values: + | Course full name | Course 2 | + | Course short name | C2 | + And I press "Save and display" + When I navigate to "Communication" in current page administration Then the field "Communication service" matches value "Matrix" Scenario: I should have communication disabled by default for existing courses - Given I am on "Test course" course homepage - When I navigate to "Settings" in current page administration - And I click on "Communication" "link" + Given I am on "Course 1" course homepage + When I navigate to "Communication" in current page administration Then the field "Communication service" matches value "None" diff --git a/course/tests/courselib_test.php b/course/tests/courselib_test.php index f972ed5c346f4..51a4b3db179fd 100644 --- a/course/tests/courselib_test.php +++ b/course/tests/courselib_test.php @@ -7271,4 +7271,67 @@ public function test_set_downloadcontent() { // The download course content value has changed, it should return true in this case. $this->assertTrue(set_downloadcontent($page->cmid, DOWNLOAD_COURSE_CONTENT_ENABLED)); } + + /** + * Test the course_get_communication_instance_data() function. + * + * @covers ::course_get_communication_instance_data + */ + public function test_course_get_communication_instance_data(): void { + $this->resetAfterTest(); + $course = $this->getDataGenerator()->create_course(); + + // Set admin user as a valid enrolment will be checked in the callback function. + $this->setAdminUser(); + + // Use the callback function and return the data. + list($instance, $context, $heading, $returnurl) = component_callback( + 'core_course', + 'get_communication_instance_data', + [$course->id] + ); + + // Check the url is as expected. + $expectedreturnurl = new moodle_url('/course/view.php', ['id' => $course->id]); + $this->assertEquals($expectedreturnurl, $returnurl); + + // Check the context is as expected. + $expectedcontext = context_course::instance($course->id); + $this->assertEquals($expectedcontext, $context); + + // Check the instance id is as expected. + $this->assertEquals($course->id, $instance->id); + + // Check the heading is as expected. + $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); + } } diff --git a/course/tests/externallib_test.php b/course/tests/externallib_test.php index b026cbfd7c461..83404ad7b2539 100644 --- a/course/tests/externallib_test.php +++ b/course/tests/externallib_test.php @@ -2636,7 +2636,7 @@ public function test_get_user_navigation_options() { foreach ($course['options'] as $option) { $navoptions->{$option['name']} = $option['available']; } - $this->assertCount(8, $course['options']); + $this->assertCount(9, $course['options']); if ($course['id'] == SITEID) { $this->assertTrue($navoptions->blogs); $this->assertFalse($navoptions->notes); @@ -2646,6 +2646,7 @@ public function test_get_user_navigation_options() { $this->assertFalse($navoptions->grades); $this->assertFalse($navoptions->search); $this->assertTrue($navoptions->competencies); + $this->assertFalse($navoptions->communication); } else { $this->assertTrue($navoptions->blogs); $this->assertFalse($navoptions->notes); @@ -2655,6 +2656,7 @@ public function test_get_user_navigation_options() { $this->assertTrue($navoptions->grades); $this->assertFalse($navoptions->search); $this->assertTrue($navoptions->competencies); + $this->assertFalse($navoptions->communication); } } } diff --git a/lang/en/communication.php b/lang/en/communication.php index 46fa746b930cb..bdadec765ac17 100644 --- a/lang/en/communication.php +++ b/lang/en/communication.php @@ -23,12 +23,14 @@ */ $string['communication'] = 'Communication'; +$string['communicationdisabled'] = 'Communication is disabled.'; $string['communicationprovidernotfound'] = 'The \'{$a}\' communication provider doesn\'t exist or is not recognised.'; $string['communicationroomname'] = 'Room name'; $string['communicationroomname_help'] = 'The name that participants see when they visit the room. If you leave this blank, a default room name will be automatically set.'; $string['communicationroompending'] = 'Your {$a} room will be ready soon.'; $string['communicationroomready'] = 'Your {$a} room is ready!'; $string['managecommunicationproviders'] = 'Manage communication providers'; +$string['nocommunicationinstance'] = 'No communication instance found.'; $string['nocommunicationprovider'] = 'No communication provider found.'; $string['nocommunicationselected'] = 'None'; $string['privacy:metadata:communication_user'] = 'The communication user table stores the user id and communication id for communication provider mapping.'; diff --git a/lang/en/role.php b/lang/en/role.php index 5f9b0ff7f0021..117a489e991e1 100644 --- a/lang/en/role.php +++ b/lang/en/role.php @@ -164,6 +164,7 @@ $string['contextrolenotallowed'] = 'Role {$a} not allowed in this context.'; $string['course:activityvisibility'] = 'Hide/show activities'; $string['course:bulkmessaging'] = 'Send a message to many people'; +$string['course:configurecoursecommunication'] = 'Configure course communication settings'; $string['course:create'] = 'Create courses'; $string['course:creategroupconversations'] = 'Create group conversations'; $string['course:delete'] = 'Delete courses'; diff --git a/lib/classes/navigation/views/secondary.php b/lib/classes/navigation/views/secondary.php index 9abc2b270f8a9..b533fe5881886 100644 --- a/lib/classes/navigation/views/secondary.php +++ b/lib/classes/navigation/views/secondary.php @@ -89,6 +89,7 @@ protected function get_default_course_mapping(): array { 'grades' => 2, 'badgesview' => 7, 'competencies' => 8, + 'communication' => 14, ], self::TYPE_CUSTOM => [ 'contentbank' => 5, diff --git a/lib/db/access.php b/lib/db/access.php index 37bd3aa509baa..3a326b40ea451 100644 --- a/lib/db/access.php +++ b/lib/db/access.php @@ -2691,4 +2691,14 @@ 'manager' => CAP_ALLOW, ] ], + + // Allow users to configure course communication rooms. + 'moodle/course:configurecoursecommunication' => [ + 'captype' => 'write', + 'contextlevel' => CONTEXT_COURSE, + 'archetypes' => [ + 'editingteacher' => CAP_ALLOW, + 'manager' => CAP_ALLOW, + ] + ], ); diff --git a/lib/navigationlib.php b/lib/navigationlib.php index c95f7ebcbb242..11522aa9c9b4f 100644 --- a/lib/navigationlib.php +++ b/lib/navigationlib.php @@ -2976,6 +2976,14 @@ public function add_course_essentials($coursenode, stdClass $course) { } } + // Add link for configuring communication. + if ($navoptions->communication) { + $url = new moodle_url('/communication/configure.php', ['instanceid' => $course->id, + 'instancetype' => 'coursecommunication', 'component' => 'core_course']); + $coursenode->add(get_string('communication', 'communication'), $url, + navigation_node::TYPE_SETTING, null, 'communication'); + } + return true; } /** diff --git a/version.php b/version.php index c6de877291901..55147bd1c999f 100644 --- a/version.php +++ b/version.php @@ -29,7 +29,7 @@ defined('MOODLE_INTERNAL') || die(); -$version = 2023072800.00; // YYYYMMDD = weekly release date of this DEV branch. +$version = 2023072800.01; // YYYYMMDD = weekly release date of this DEV branch. // RR = release increments - 00 in DEV branches. // .XX = incremental changes. $release = '4.3dev (Build: 20230728)'; // Human-friendly version name