MDL-78619 communication_customlink: Implement custom link provider

This commit is contained in:
Michael Hawkins 2023-07-19 01:54:39 +08:00
parent bfbb314e2f
commit a3fa3e3fc1
No known key found for this signature in database
GPG Key ID: 81FCB1395B8AA779
9 changed files with 564 additions and 0 deletions

View File

@ -0,0 +1,168 @@
<?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 communication_customlink;
use core_communication\processor;
/**
* class communication_feature to handle custom link specific actions.
*
* @package communication_customlink
* @copyright 2023 Michael Hawkins <michaelh@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class communication_feature implements
\core_communication\communication_provider,
\core_communication\room_chat_provider,
\core_communication\form_provider {
/** @var string The database table storing custom link specific data */
protected const CUSTOMLINK_TABLE = 'communication_customlink';
/** @var \cache_application $cache The application cache for this provider. */
protected \cache_application $cache;
/**
* Load the communication provider for the communication API.
*
* @param processor $communication The communication processor object.
* @return communication_feature The communication provider object.
*/
public static function load_for_instance(processor $communication): self {
return new self($communication);
}
/**
* Constructor for communication provider.
*
* @param processor $communication The communication processor object.
*/
private function __construct(
private \core_communication\processor $communication,
) {
$this->cache = \cache::make('communication_customlink', 'customlink');
}
/**
* Create room - room existence managed externally, always return true.
*
* @return boolean
*/
public function create_chat_room(): bool {
return true;
}
/**
* Update room - room existence managed externally, always return true.
*
* @return boolean
*/
public function update_chat_room(): bool {
return true;
}
/**
* Delete room - room existence managed externally, always return true.
*
* @return boolean
*/
public function delete_chat_room(): bool {
return true;
}
/**
* Fetch the URL for this custom link provider.
*
* @return string|null The custom URL, or null if not found.
*/
public function get_chat_room_url(): ?string {
global $DB;
$commid = $this->communication->get_id();
$cachekey = "link_url_{$commid}";
// Attempt to fetch the room URL from the cache.
if ($url = $this->cache->get($cachekey)) {
return $url;
}
// If not found in the cache, fetch the URL from the database.
$url = $DB->get_field(
self::CUSTOMLINK_TABLE,
'url',
['commid' => $commid],
);
// Cache the URL.
$this->cache->set($cachekey, $url);
return $url;
}
public function save_form_data(\stdClass $instance): void {
global $DB;
$commid = $this->communication->get_id();
$cachekey = "link_url_{$commid}";
$newrecord = new \stdClass();
$newrecord->url = $instance->customlinkurl ?? null;
$existingrecord = $DB->get_record(
self::CUSTOMLINK_TABLE,
['commid' => $commid],
'id, url'
);
if (!$existingrecord) {
// Create the record if it does not exist.
$newrecord->commid = $commid;
$DB->insert_record(self::CUSTOMLINK_TABLE, $newrecord);
} else if ($newrecord->url !== $existingrecord->url) {
// Update record if the URL has changed.
$newrecord->id = $existingrecord->id;
$DB->update_record(self::CUSTOMLINK_TABLE, $newrecord);
} else {
// No change made.
return;
}
// Cache the new URL.
$this->cache->set($cachekey, $newrecord->url);
}
public function set_form_data(\stdClass $instance): void {
if (!empty($instance->id) && !empty($this->communication->get_id())) {
$instance->customlinkurl = $this->get_chat_room_url();
}
}
public static function set_form_definition(\MoodleQuickForm $mform): void {
// Custom link description for the communication provider.
$mform->insertElementBefore($mform->createElement('text', 'customlinkurl',
get_string('customlinkurl', 'communication_customlink'),
'maxlength="255" size="40"'), 'addcommunicationoptionshere');
$mform->addHelpButton('customlinkurl', 'customlinkurl', 'communication_customlink');
$mform->setType('customlinkurl', PARAM_URL);
$mform->addRule('customlinkurl', get_string('required'), 'required', null, 'client');
$mform->addRule('customlinkurl', get_string('maximumchars', '', 255), 'maxlength', 255);
$mform->insertElementBefore($mform->createElement('static', 'customlinkurlinfo', '',
get_string('customlinkurlinfo', 'communication_customlink'),
'addcommunicationoptionshere'), 'addcommunicationoptionshere');
}
}

View File

@ -0,0 +1,39 @@
<?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 communication_customlink\privacy;
use core_privacy\local\metadata\null_provider;
/**
* Privacy Subsystem for communication_customlink implementing null_provider.
*
* @package communication_customlink
* @copyright 2023 Michael Hawkins <michaelh@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class provider implements null_provider {
/**
* Get the language string identifier with the component's language
* file to explain why this plugin stores no data.
*
* @return string
*/
public static function get_reason(): string {
return 'privacy:metadata';
}
}

View File

@ -0,0 +1,35 @@
<?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/>.
/**
* Defined caches used internally by the provider.
*
* @package communication_customlink
* @copyright 2023 Michael Hawkins <michaelh@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
declare(strict_types=1);
defined('MOODLE_INTERNAL') || die();
$definitions = [
'customlink' => [
'mode' => cache_store::MODE_APPLICATION,
'simplekeys' => true,
'simpledata' => true,
],
];

View File

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8" ?>
<XMLDB PATH="communication/provider/customlink/db" VERSION="20230826" COMMENT="Stores the link associated with a custom link communication instance."
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="../../../../lib/xmldb/xmldb.xsd"
>
<TABLES>
<TABLE NAME="communication_customlink" COMMENT="Stores the link associated with a custom link communication instance.">
<FIELDS>
<FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="true"/>
<FIELD NAME="commid" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false" COMMENT="ID of the communication record"/>
<FIELD NAME="url" TYPE="char" LENGTH="255" NOTNULL="false" SEQUENCE="false" COMMENT="URL being linked to by the provider"/>
</FIELDS>
<KEYS>
<KEY NAME="primary" TYPE="primary" FIELDS="id"/>
<KEY NAME="fk_commid" TYPE="foreign" FIELDS="commid" REFTABLE="communication" REFFIELDS="id" COMMENT="Foreign key for communication reference"/>
</KEYS>
</TABLE>
</TABLES>
</XMLDB>

View File

@ -0,0 +1,30 @@
<?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/>.
/**
* Strings for component communication_customlink, language 'en'.
*
* @package communication_customlink
* @copyright 2023 Michael Hawkins <michaelh@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
$string['cachedef_customlink'] = 'Custom link data';
$string['customlinkurl'] = 'Custom link URL';
$string['customlinkurl_help'] = 'Provide a link to an existing room from any communication service you would like to make available to participants - such as Microsoft Teams, Slack or Matrix.';
$string['customlinkurlinfo'] = 'The URL of an existing room already set up for this course.';
$string['pluginname'] = 'Custom link';
$string['privacy:metadata'] = 'Custom link communication plugin does not store any personal data.';

View File

@ -0,0 +1,86 @@
@communication @communication_customlink @javascript
Feature: Communication custom link
In order to facilitate easy access to an existing communication platform
As a teacher
I need to be able to make a custom communication link available in my course
Background:
Given the following "users" exist:
| username | firstname | lastname | email |
| teacher1 | Teacher | 1 | teacher1@example.com |
| student1 | Student | 1 | student1@example.com |
And the following "courses" exist:
| fullname | shortname |
| Course 1 | C1 |
And the following "course enrolments" exist:
| user | course | role |
| teacher1 | C1 | editingteacher |
| student1 | C1 | student |
And the following config values are set as admin:
| enablecommunicationsubsystem | 1 |
Scenario: As a teacher I can configure a custom communication provider for my course
Given I am on the "Course 1" "Course" page logged in as "teacher1"
And "Chat to course participants" "button" should not be visible
When I navigate to "Communication" in current page administration
And the "Communication service" select box should contain "Custom link"
And I should not see "Custom link URL"
And I select "Custom link" from the "Communication service" singleselect
And I should see "Custom link URL"
And I set the following fields to these values:
| communicationroomname | Test URL |
| customlinkurl | #wwwroot#/communication/provider/customlink/tests/behat/fixtures/custom_link_test_page.php |
And I press "Save changes"
Then "Chat to course participants" "button" should be visible
And I click on "Chat to course participants" "button"
# Check the link hits the expected destination.
And I switch to a second window
And I should see "Example messaging service - teacher1" in the "region-main" "region"
And I close all opened windows
# Ensure any communication subsystem tasks have no impact on availability.
And I run all adhoc tasks
And I am on the "Course 1" course page
And "Chat to course participants" "button" should be visible
And I click on "Chat to course participants" "button"
And I switch to a second window
And I should see "Example messaging service - teacher1" in the "region-main" "region"
And I close all opened windows
And I log out
# Confirm student also has access to the custom link.
And I am on the "Course 1" "Course" page logged in as "student1"
And "Chat to course participants" "button" should be visible
And I click on "Chat to course participants" "button"
And I switch to a second window
And I should see "Example messaging service - student1" in the "region-main" "region"
Scenario: As a teacher I can disable and re-enable a custom communication provider for my course
Given I am on the "Course 1" "Course" page logged in as "teacher1"
And "Chat to course participants" "button" should not be visible
When I navigate to "Communication" in current page administration
And I select "Custom link" from the "Communication service" singleselect
And I set the following fields to these values:
| communicationroomname | Test URL |
| customlinkurl | #wwwroot#/communication/provider/customlink/tests/behat/fixtures/custom_link_test_page.php |
And I press "Save changes"
And "Chat to course participants" "button" should be visible
And I run all adhoc tasks
And I navigate to "Communication" in current page administration
And I select "None" from the "Communication service" singleselect
And I press "Save changes"
And "Chat to course participants" "button" should not be visible
And I run all adhoc tasks
And I am on the "Course 1" course page
And "Chat to course participants" "button" should not be visible
And I navigate to "Communication" in current page administration
And I select "Custom link" from the "Communication service" singleselect
And I set the following fields to these values:
| communicationroomname | Test URL |
| customlinkurl | #wwwroot#/communication/provider/customlink/tests/behat/fixtures/custom_link_test_page.php |
And I press "Save changes"
And "Chat to course participants" "button" should be visible
And I run all adhoc tasks
And I am on the "Course 1" course page
And "Chat to course participants" "button" should be visible
And I click on "Chat to course participants" "button"
And I switch to a second window
And I should see "Example messaging service - teacher1" in the "region-main" "region"

View File

@ -0,0 +1,40 @@
<?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/>.
/**
* A page which can be used to represent a messaging service while testing the custom link communication provider.
*
* The current Moodle user's username is listed in the heading to make it easier to confirm the page has been
* opened by the expected user.
*
* @package communication_customlink
* @copyright 2023 Michael Hawkins <michaelh@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
require_once(__DIR__ . '/../../../../../../config.php');
defined('BEHAT_SITE_RUNNING') || die();
global $OUTPUT, $PAGE, $USER;
$PAGE->set_url('/communication/provider/customlink/tests/behat/fixtures/custom_link_test_page.php');
require_login();
$PAGE->set_context(core\context\system::instance());
echo $OUTPUT->header();
echo "<h2>Example messaging service - {$USER->username}</h2>";
echo "<p>Imagine this is a wonderful messaging service being accessed directly from a link in Moodle!</p>";
echo $OUTPUT->footer();

View File

@ -0,0 +1,117 @@
<?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 communication_customlink;
use core_communication\processor;
use core_communication\communication_test_helper_trait;
defined('MOODLE_INTERNAL') || die();
require_once(__DIR__ . '/../../../tests/communication_test_helper_trait.php');
/**
* Class communication_feature_test to test the custom link features implemented using the core interfaces.
*
* @package communication_customlink
* @category test
* @copyright 2023 Michael Hawkins <michaelh@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @coversDefaultClass \communication_customlink\communication_feature
*/
class communication_feature_test extends \advanced_testcase {
use communication_test_helper_trait;
public function setUp(): void {
parent::setUp();
$this->resetAfterTest();
$this->setup_communication_configs();
}
/**
* Test create, update and delete chat room.
*
* @covers ::load_for_instance
*/
public function test_load_for_instance(): void {
$communicationprocessor = $this->get_test_communication_processor();
$instance = communication_feature::load_for_instance($communicationprocessor);
$this->assertInstanceOf('communication_customlink\communication_feature', $instance);
}
/**
* Test create, update and delete chat room.
*
* @covers ::create_chat_room
* @covers ::update_chat_room
* @covers ::delete_chat_room
*/
public function test_create_update_delete_chat_room(): void {
$communicationprocessor = $this->get_test_communication_processor();
// Create, update and delete room should always return true because this provider contains
// a link to a room, but does not manage the existence of the room.
$createroomresult = $communicationprocessor->get_room_provider()->create_chat_room();
$updateroomresult = $communicationprocessor->get_room_provider()->update_chat_room();
$deleteroomresult = $communicationprocessor->get_room_provider()->delete_chat_room();
$this->assertTrue($createroomresult);
$this->assertTrue($updateroomresult);
$this->assertTrue($deleteroomresult);
}
/**
* Test save form data with provider's custom field and fetching with get_chat_room_url().
*
* @covers ::save_form_data
* @covers ::get_chat_room_url
*/
public function test_save_form_data(): void {
$communicationprocessor = $this->get_test_communication_processor();
$customlinkurl = 'https://moodle.org/message/index.php';
$formdatainstance = (object) ['customlinkurl' => $customlinkurl];
// Test the custom link URL is saved and can be retrieved as expected.
$communicationprocessor->get_form_provider()->save_form_data($formdatainstance);
$fetchedurl = $communicationprocessor->get_room_provider()->get_chat_room_url();
$this->assertEquals($customlinkurl, $fetchedurl);
}
/**
* Create a test custom link communication processor object.
*
* @return processor
*/
protected function get_test_communication_processor(): processor {
$course = $this->getDataGenerator()->create_course();
$instanceid = $course->id;
$component = 'core_course';
$instancetype = 'coursecommunication';
$selectedcommunication = 'communication_customlink';
$communicationroomname = 'communicationroom';
$communicationprocessor = processor::create_instance(
$selectedcommunication,
$instanceid,
$component,
$instancetype,
$communicationroomname,
);
return $communicationprocessor;
}
}

View File

@ -0,0 +1,30 @@
<?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/>.
/**
* Version information for communication_customlink.
*
* @package communication_customlink
* @copyright 2023 Michael Hawkins <michaelh@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
$plugin->component = 'communication_customlink';
$plugin->version = 2023082600;
$plugin->requires = 2023082600;
$plugin->maturity = MATURITY_ALPHA;