first commit

This commit is contained in:
CHIEFSOFT\ameye
2024-09-30 18:11:26 -04:00
commit e592ca6823
27270 changed files with 5002257 additions and 0 deletions
@@ -0,0 +1,792 @@
<?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_matrix;
use communication_matrix\local\spec\features\matrix\{
create_room_v3 as create_room_feature,
get_room_members_v3 as get_room_members_feature,
remove_member_from_room_v3 as remove_member_from_room_feature,
update_room_avatar_v3 as update_room_avatar_feature,
update_room_name_v3 as update_room_name_feature,
update_room_topic_v3 as update_room_topic_feature,
upload_content_v3 as upload_content_feature,
media_create_v1 as media_create_feature,
};
use communication_matrix\local\spec\features\synapse\{
create_user_v2 as create_user_feature,
get_room_info_v1 as get_room_info_feature,
get_user_info_v2 as get_user_info_feature,
invite_member_to_room_v1 as invite_member_to_room_feature,
};
use core_communication\processor;
use stdClass;
use GuzzleHttp\Psr7\Response;
/**
* class communication_feature to handle matrix specific actions.
*
* @package communication_matrix
* @copyright 2023 Safat Shahin <safat.shahin@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\form_provider,
\core_communication\room_chat_provider,
\core_communication\room_user_provider,
\core_communication\synchronise_provider,
\core_communication\user_provider {
/** @var ?matrix_room $room The matrix room object to update room information */
private ?matrix_room $room = null;
/** @var string|null The URI of the home server */
protected ?string $homeserverurl = null;
/** @var string The URI of the Matrix web client */
protected string $webclienturl;
/** @var \communication_matrix\local\spec\v1p1|null The Matrix API processor */
protected ?matrix_client $matrixapi;
/**
* 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);
}
/**
* Reload the room information.
* This may be necessary after a room has been created or updated via the adhoc task.
* This is primarily intended for use in unit testing, but may have real world cases too.
*/
public function reload(): void {
$this->room = null;
$this->processor = processor::load_by_id($this->processor->get_id());
}
/**
* Constructor for communication provider to initialize necessary objects for api cals etc..
*
* @param processor $processor The communication processor object
*/
private function __construct(
private \core_communication\processor $processor,
) {
$this->homeserverurl = get_config('communication_matrix', 'matrixhomeserverurl');
$this->webclienturl = get_config('communication_matrix', 'matrixelementurl');
if ($processor::is_provider_available('communication_matrix')) {
// Generate the API instance.
$this->matrixapi = matrix_client::instance(
serverurl: $this->homeserverurl,
accesstoken: get_config('communication_matrix', 'matrixaccesstoken'),
);
}
}
/**
* Check whether the room configuration has been created yet.
*
* @return bool
*/
protected function room_exists(): bool {
return (bool) $this->get_room_configuration();
}
/**
* Whether the room exists on the remote server.
* This does not involve a remote call, but checks whether Moodle is aware of the room id.
* @return bool
*/
protected function remote_room_exists(): bool {
$room = $this->get_room_configuration();
return $room && ($room->get_room_id() !== null);
}
/**
* Get the stored room configuration.
* @return null|matrix_room
*/
public function get_room_configuration(): ?matrix_room {
$this->room = matrix_room::load_by_processor_id($this->processor->get_id());
return $this->room;
}
/**
* Return the current room id.
*
* @return string|null
*/
public function get_room_id(): ?string {
return $this->get_room_configuration()?->get_room_id();
}
/**
* Create members.
*
* @param array $userids The Moodle user ids to create
*/
public function create_members(array $userids): void {
$addedmembers = [];
// This API requiures the create_user feature.
$this->matrixapi->require_feature(create_user_feature::class);
foreach ($userids as $userid) {
$user = \core_user::get_user($userid);
$userfullname = fullname($user);
// Proceed if we have a user's full name and email to work with.
if (!empty($user->email) && !empty($userfullname)) {
$qualifiedmuid = matrix_user_manager::get_formatted_matrix_userid($user->username);
// First create user in matrix.
$response = $this->matrixapi->create_user(
userid: $qualifiedmuid,
displayname: $userfullname,
threepids: [(object) [
'medium' => 'email',
'address' => $user->email,
], ],
externalids: [],
);
$body = json_decode($response->getBody());
if (!empty($matrixuserid = $body->name)) {
// Then create matrix user id in moodle.
matrix_user_manager::set_matrix_userid_in_moodle($userid, $qualifiedmuid);
if ($this->add_registered_matrix_user_to_room($matrixuserid)) {
$addedmembers[] = $userid;
}
}
}
}
// Set the power level of the users.
if (!empty($addedmembers) && $this->is_power_levels_update_required($addedmembers)) {
$this->set_matrix_power_levels();
}
// Mark then users as synced for the added members.
$this->processor->mark_users_as_synced($addedmembers);
}
public function update_room_membership(array $userids): void {
// Filter out any users that are not room members yet.
$response = $this->matrixapi->get_room_members(
roomid: $this->get_room_id(),
);
$body = self::get_body($response);
if (isset($body->joined)) {
foreach ($userids as $key => $userid) {
$matrixuserid = matrix_user_manager::get_matrixid_from_moodle(
userid: $userid,
);
if (!array_key_exists($matrixuserid, (array) $body->joined)) {
unset($userids[$key]);
}
}
}
$this->set_matrix_power_levels();
// Mark the users as synced for the updated members.
$this->processor->mark_users_as_synced($userids);
}
/**
* Add members to a room.
*
* @param array $userids The user ids to add
*/
public function add_members_to_room(array $userids): void {
$unregisteredmembers = [];
$addedmembers = [];
foreach ($userids as $userid) {
$matrixuserid = matrix_user_manager::get_matrixid_from_moodle($userid);
if ($matrixuserid && $this->check_user_exists($matrixuserid)) {
if ($this->add_registered_matrix_user_to_room($matrixuserid)) {
$addedmembers[] = $userid;
}
} else {
$unregisteredmembers[] = $userid;
}
}
// Set the power level of the users.
if (!empty($addedmembers) && $this->is_power_levels_update_required($addedmembers)) {
$this->set_matrix_power_levels();
}
// Mark then users as synced for the added members.
$this->processor->mark_users_as_synced($addedmembers);
// Create Matrix users.
if (count($unregisteredmembers) > 0) {
$this->create_members($unregisteredmembers);
}
}
/**
* Adds the registered matrix user id to room.
*
* @param string $matrixuserid Registered matrix user id
*/
private function add_registered_matrix_user_to_room(string $matrixuserid): bool {
// Require the invite_member_to_room API feature.
$this->matrixapi->require_feature(invite_member_to_room_feature::class);
if (!$this->check_room_membership($matrixuserid)) {
$response = $this->matrixapi->invite_member_to_room(
roomid: $this->get_room_id(),
userid: $matrixuserid,
);
$body = self::get_body($response);
if (empty($body->room_id)) {
return false;
}
if ($body->room_id !== $this->get_room_id()) {
return false;
}
return true;
}
return false;
}
/**
* Remove members from a room.
*
* @param array $userids The Moodle user ids to remove
*/
public function remove_members_from_room(array $userids): void {
// This API requiures the remove_members_from_room feature.
$this->matrixapi->require_feature(remove_member_from_room_feature::class);
if ($this->get_room_id() === null) {
return;
}
// Remove the power level for the user first.
$this->set_matrix_power_levels($userids);
$membersremoved = [];
$currentpowerlevels = $this->get_current_powerlevel_data();
$currentuserpowerlevels = (array) $currentpowerlevels->users ?? [];
foreach ($userids as $userid) {
// Check user is member of room first.
$matrixuserid = matrix_user_manager::get_matrixid_from_moodle($userid);
if (!$matrixuserid) {
// Unable to find a matrix userid for this user.
continue;
}
if (array_key_exists($matrixuserid, $currentuserpowerlevels)) {
if ($currentuserpowerlevels[$matrixuserid] >= matrix_constants::POWER_LEVEL_MAXIMUM) {
// Skip removing the user if they are an admin.
continue;
}
}
if (
$this->check_user_exists($matrixuserid) &&
$this->check_room_membership($matrixuserid)
) {
$this->matrixapi->remove_member_from_room(
roomid: $this->get_room_id(),
userid: $matrixuserid,
);
$membersremoved[] = $userid;
}
}
$this->processor->delete_instance_user_mapping($membersremoved);
}
/**
* Check if a user exists in Matrix.
* Use if user existence is needed before doing something else.
*
* @param string $matrixuserid The Matrix user id to check
* @return bool
*/
public function check_user_exists(string $matrixuserid): bool {
// This API requires the get_user_info feature.
$this->matrixapi->require_feature(get_user_info_feature::class);
$response = $this->matrixapi->get_user_info(
userid: $matrixuserid,
);
$body = self::get_body($response);
return isset($body->name);
}
/**
* Check if a user is a member of a room.
* Use if membership confirmation is needed before doing something else.
*
* @param string $matrixuserid The Matrix user id to check
* @return bool
*/
public function check_room_membership(string $matrixuserid): bool {
// This API requires the get_room_members feature.
$this->matrixapi->require_feature(get_room_members_feature::class);
$response = $this->matrixapi->get_room_members(
roomid: $this->get_room_id(),
);
$body = self::get_body($response);
// Check user id is in the returned room member ids.
return isset($body->joined) && array_key_exists($matrixuserid, (array) $body->joined);
}
/**
* Create a room based on the data in the communication instance.
*
* @return bool
*/
public function create_chat_room(): bool {
if ($this->remote_room_exists()) {
// A room already exists. Update it instead.
return $this->update_chat_room();
}
// This method requires the create_room API feature.
$this->matrixapi->require_feature(create_room_feature::class);
$room = $this->get_room_configuration();
$response = $this->matrixapi->create_room(
name: $this->processor->get_room_name(),
visibility: 'private',
preset: 'private_chat',
initialstate: [],
options: [
'topic' => $room->get_topic(),
],
);
$response = self::get_body($response);
if (empty($response->room_id)) {
throw new \moodle_exception(
'Unable to determine ID of matrix room',
);
}
// Update our record of the matrix room_id.
$room->update_room_record(
roomid: $response->room_id,
);
// Update the room avatar.
$this->update_room_avatar();
return true;
}
public function update_chat_room(): bool {
if (!$this->remote_room_exists()) {
// No room exists. Create it instead.
return $this->create_chat_room();
}
$this->matrixapi->require_features([
get_room_info_feature::class,
update_room_name_feature::class,
update_room_topic_feature::class,
]);
// Get room data.
$response = $this->matrixapi->get_room_info(
roomid: $this->get_room_id(),
);
$remoteroomdata = self::get_body($response);
// Update the room name when it's updated from the form.
if ($remoteroomdata->name !== $this->processor->get_room_name()) {
$this->matrixapi->update_room_name(
roomid: $this->get_room_id(),
name: $this->processor->get_room_name(),
);
}
// Update the room topic if set.
$localroomdata = $this->get_room_configuration();
if ($remoteroomdata->topic !== $localroomdata->get_topic()) {
$this->matrixapi->update_room_topic(
roomid: $localroomdata->get_room_id(),
topic: $localroomdata->get_topic(),
);
}
// Update room avatar.
$this->update_room_avatar();
return true;
}
public function delete_chat_room(): bool {
$this->get_room_configuration()->delete_room_record();
$this->room = null;
return true;
}
/**
* Update the room avatar when an instance image is added or updated.
*/
public function update_room_avatar(): void {
// Both of the following features of the remote API are required.
$this->matrixapi->require_features([
upload_content_feature::class,
update_room_avatar_feature::class,
]);
// Check if we have an avatar that needs to be synced.
if ($this->processor->is_avatar_synced()) {
return;
}
$instanceimage = $this->processor->get_avatar();
$contenturi = null;
if ($this->matrixapi->implements_feature(media_create_feature::class)) {
// From version 1.7 we can fetch a mxc URI and use it before uploading the content.
if ($instanceimage) {
$response = $this->matrixapi->media_create();
$contenturi = self::get_body($response)->content_uri;
// Now update the room avatar.
$response = $this->matrixapi->update_room_avatar(
roomid: $this->get_room_id(),
avatarurl: $contenturi,
);
// And finally upload the content.
$this->matrixapi->upload_content($instanceimage);
} else {
$response = $this->matrixapi->update_room_avatar(
roomid: $this->get_room_id(),
avatarurl: null,
);
}
} else {
// Prior to v1.7 the only way to upload content was to upload the content, which returns a mxc URI to use.
if ($instanceimage) {
// First upload the content.
$response = $this->matrixapi->upload_content($instanceimage);
$body = self::get_body($response);
$contenturi = $body->content_uri;
}
// Now update the room avatar.
$response = $this->matrixapi->update_room_avatar(
roomid: $this->get_room_id(),
avatarurl: $contenturi,
);
}
// Indicate the avatar has been synced if it was successfully set with Matrix.
if ($response->getReasonPhrase() === 'OK') {
$this->processor->set_avatar_synced_flag(true);
}
}
public function get_chat_room_url(): ?string {
if (!$this->get_room_id()) {
// We don't have a room id for this record.
return null;
}
return sprintf(
"%s#/room/%s",
$this->webclienturl,
$this->get_room_id(),
);
}
public function save_form_data(\stdClass $instance): void {
$matrixroomtopic = $instance->matrixroomtopic ?? null;
$room = $this->get_room_configuration();
if ($room) {
$room->update_room_record(
topic: $matrixroomtopic,
);
} else {
$this->room = matrix_room::create_room_record(
processorid: $this->processor->get_id(),
topic: $matrixroomtopic,
);
}
}
public function set_form_data(\stdClass $instance): void {
if (!empty($instance->id) && !empty($this->processor->get_id())) {
if ($this->room_exists()) {
$instance->matrixroomtopic = $this->get_room_configuration()->get_topic();
}
}
}
public static function set_form_definition(\MoodleQuickForm $mform): void {
// Room description for the communication provider.
$mform->insertElementBefore($mform->createElement(
'text',
'matrixroomtopic',
get_string('matrixroomtopic', 'communication_matrix'),
'maxlength="255" size="20"'
), 'addcommunicationoptionshere');
$mform->addHelpButton('matrixroomtopic', 'matrixroomtopic', 'communication_matrix');
$mform->setType('matrixroomtopic', PARAM_TEXT);
}
/**
* Get the body of a response as a stdClass.
*
* @param Response $response
* @return stdClass
*/
public static function get_body(Response $response): stdClass {
$body = $response->getBody();
return json_decode($body, false, 512, JSON_THROW_ON_ERROR);
}
/**
* Set the matrix power level with the room.
*
* Users with a non-moodle power level are not typically removed unless specified in the $forceremoval param.
* Matrix Admin users are never removed.
*
* @param array $forceremoval The users to force removal from the room, even if they have a custom power level
*/
private function set_matrix_power_levels(
array $forceremoval = [],
): void {
// Get the current power levels.
$currentpowerlevels = $this->get_current_powerlevel_data();
$currentuserpowerlevels = (array) $currentpowerlevels->users ?? [];
// Get all the current users who need to be in the room.
$userlist = $this->processor->get_all_userids_for_instance();
// Translate the user ids to matrix user ids.
$userlist = array_combine(
array_map(
fn ($userid) => matrix_user_manager::get_matrixid_from_moodle($userid),
$userlist,
),
$userlist,
);
// Determine the power levels, and filter out anyone with the default level.
$newuserpowerlevels = array_filter(
array_map(
fn($userid) => $this->get_user_allowed_power_level($userid),
$userlist,
),
fn($level) => $level !== matrix_constants::POWER_LEVEL_DEFAULT,
);
// Keep current room admins, and users which don't use our MODERATOR power level without changing them.
$staticusers = $this->get_users_with_custom_power_level($currentuserpowerlevels);
foreach ($staticusers as $userid => $level) {
$newuserpowerlevels[$userid] = $level;
}
if (!empty($forceremoval)) {
// Remove the users from the power levels if they are not admins.
foreach ($forceremoval as $userid) {
$muid = matrix_user_manager::get_matrixid_from_moodle($userid);
if (isset($newuserpowerlevels[$muid]) && $newuserpowerlevels[$muid] < matrix_constants::POWER_LEVEL_MAXIMUM) {
unset($newuserpowerlevels[$muid]);
}
}
}
if (!$this->power_levels_changed($currentuserpowerlevels, $newuserpowerlevels)) {
// No changes to make.
return;
}
// Update the power levels for the room.
$this->matrixapi->update_room_power_levels(
roomid: $this->get_room_id(),
users: $newuserpowerlevels,
);
}
/**
* Filter the list of users provided to remove those with a moodle-related power level.
*
* @param array $users
* @return array
*/
private function get_users_with_custom_power_level(array $users): array {
return array_filter(
$users,
function ($level): bool {
switch ($level) {
case matrix_constants::POWER_LEVEL_DEFAULT:
case matrix_constants::POWER_LEVEL_MOODLE_SITE_ADMIN:
case matrix_constants::POWER_LEVEL_MOODLE_MODERATOR:
return false;
default:
return true;
}
},
);
}
/**
* Check whether power levels have changed compared with the proposed power levels.
*
* @param array $currentuserpowerlevels The current power levels
* @param array $newuserpowerlevels The new power levels proposed
* @return bool Whether there is any change to be made
*/
private function power_levels_changed(
array $currentuserpowerlevels,
array $newuserpowerlevels,
): bool {
if (count($newuserpowerlevels) !== count($currentuserpowerlevels)) {
// Different number of keys - there must be a difference then.
return true;
}
// Sort the power levels.
ksort($newuserpowerlevels, SORT_NUMERIC);
// Get the current power levels.
ksort($currentuserpowerlevels);
$diff = array_merge(
array_diff_assoc(
$newuserpowerlevels,
$currentuserpowerlevels,
),
array_diff_assoc(
$currentuserpowerlevels,
$newuserpowerlevels,
),
);
return count($diff) > 0;
}
/**
* Get the current power level for the room.
*
* @return stdClass
*/
private function get_current_powerlevel_data(): \stdClass {
$roomid = $this->get_room_id();
$response = $this->matrixapi->get_room_power_levels(
roomid: $roomid,
);
if ($response->getStatusCode() !== 200) {
throw new \moodle_exception(
'Unable to get power levels for room',
);
}
return $this->get_body($response);
}
/**
* Determine if a power level update is required.
* Matrix will always set a user to the default power level of 0 when a power level update is made.
* That is, unless we specify another level. As long as one person's level is greater than the default,
* we will need to set the power levels of all users greater than the default.
*
* @param array $userids The users to evaluate
* @return boolean Returns true if an update is required
*/
private function is_power_levels_update_required(array $userids): bool {
// Is the user's power level greater than the default?
foreach ($userids as $userid) {
if ($this->get_user_allowed_power_level($userid) > matrix_constants::POWER_LEVEL_DEFAULT) {
return true;
}
}
return false;
}
/**
* Get the allowed power level for the user id according to perms/site admin or default.
*
* @param int $userid
* @return int
*/
public function get_user_allowed_power_level(int $userid): int {
$powerlevel = matrix_constants::POWER_LEVEL_DEFAULT;
if (has_capability('communication/matrix:moderator', $this->processor->get_context(), $userid)) {
$powerlevel = matrix_constants::POWER_LEVEL_MOODLE_MODERATOR;
}
// If site admin, override all caps.
if (is_siteadmin($userid)) {
$powerlevel = matrix_constants::POWER_LEVEL_MOODLE_SITE_ADMIN;
}
return $powerlevel;
}
/*
* Check if matrix settings are configured
*
* @return boolean
*/
public static function is_configured(): bool {
// Matrix communication settings.
$matrixhomeserverurl = get_config('communication_matrix', 'matrixhomeserverurl');
$matrixaccesstoken = get_config('communication_matrix', 'matrixaccesstoken');
$matrixelementurl = get_config('communication_matrix', 'matrixelementurl');
if (
!empty($matrixhomeserverurl) &&
!empty($matrixaccesstoken) &&
(PHPUNIT_TEST || defined('BEHAT_SITE_RUNNING') || !empty($matrixelementurl))
) {
return true;
}
return false;
}
public function synchronise_room_members(): void {
$this->set_matrix_power_levels();
}
}
@@ -0,0 +1,189 @@
<?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_matrix\local;
use communication_matrix\matrix_client;
use GuzzleHttp\Psr7\Request;
use OutOfRangeException;
/**
* A command to be sent to the Matrix server.
*
* This class is a wrapper around the PSR-7 Request Interface implementation provided by Guzzle.
*
* It takes a set of common parameters and configurations and turns them into a Request that can be called against a live server.
*
* @package communication_matrix
* @copyright Andrew Nicols <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class command extends Request {
/** @var array $command The raw command data */
/** @var array|null $params The parameters passed into the command */
/** @var bool $sendasjson Whether to send params as JSON */
/** @var bool $requireauthorization Whether authorization is required for this request */
/** @var bool $ignorehttperrors Whether to ignore HTTP Errors */
/** @var array $query Any query parameters to set on the URL */
/** @var array|null Any parameters not used in the URI which are to be passed to the server via body or query params */
protected array $remainingparams = [];
/**
* Create a new Command.
*
* @param matrix_client $client The URL for this method
* @param string $method (GET|POST|PUT|DELETE)
* @param string $endpoint The URL
* @param array $params Any parameters to pass
* @param array $query Any query parameters to set on the URL
* @param bool $ignorehttperrors Whether to ignore HTTP Errors
* @param bool $requireauthorization Whether authorization is required for this request
* @param bool $sendasjson Whether to send params as JSON
*/
public function __construct(
protected matrix_client $client,
string $method,
string $endpoint,
protected array $params = [],
protected array $query = [],
protected bool $ignorehttperrors = false,
protected bool $requireauthorization = true,
protected bool $sendasjson = true,
) {
foreach ($params as $name => $value) {
if ($name[0] === ':') {
if (preg_match("/{$name}\\b/", $endpoint) !== 1) {
throw new OutOfRangeException("Parameter not found in URL '{$name}'");
}
$endpoint = preg_replace("/{$name}\\b/", urlencode($value), $endpoint);
unset($params[$name]);
}
}
// Store the modified params.
$this->remainingparams = $params;
if (str_contains($endpoint, '/:')) {
throw new OutOfRangeException("URL contains untranslated parameters '{$endpoint}'");
}
// Process the required headers.
$headers = [
'Content-Type' => 'application/json',
];
if ($this->require_authorization()) {
$headers['Authorization'] = 'Bearer ' . $this->client->get_token();
}
// Construct the final request.
parent::__construct(
$method,
$this->get_url($endpoint),
$headers,
);
}
/**
* Get the URL of the endpoint on the server.
*
* @param string $endpoint
* @return string
*/
protected function get_url(string $endpoint): string {
return sprintf(
"%s/%s",
$this->client->get_server_url(),
$endpoint,
);
}
/**
* Get all parameters, including those set in the URL.
*
* @return array
*/
public function get_all_params(): array {
return $this->params;
}
/**
* Get the parameters provided to the command which are not used in the URL.
*
* These are typically passed to the server as query or body parameters instead.
*
* @return array
*/
public function get_remaining_params(): array {
return $this->remainingparams;
}
/**
* Get the Guzzle options to pass into the request.
*
* @return array
*/
public function get_options(): array {
$options = [];
if (count($this->query)) {
$options['query'] = $this->query;
}
if ($this->should_send_params_as_json()) {
$options['json'] = $this->get_remaining_params();
}
if ($this->should_ignore_http_errors()) {
$options['http_errors'] = false;
}
return $options;
}
/**
* Whether authorization is required.
*
* Based on the 'authorization' attribute set in a raw command.
*
* @return bool
*/
public function require_authorization(): bool {
return $this->requireauthorization;
}
/**
* Whether to ignore http errors on the response.
*
* Based on the 'ignore_http_errors' attribute set in a raw command.
*
* @return bool
*/
public function should_ignore_http_errors(): bool {
return $this->ignorehttperrors;
}
/**
* Whether to send remaining parameters as JSON.
*
* @return bool
*/
public function should_send_params_as_json(): bool {
return $this->sendasjson;
}
}
@@ -0,0 +1,78 @@
<?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_matrix\local\spec\features\matrix;
use communication_matrix\local\command;
use GuzzleHttp\Psr7\Response;
/**
* Matrix API feature for room creation.
*
* https://spec.matrix.org/v1.1/client-server-api/#post_matrixclientv3createroom
*
* @package communication_matrix
* @copyright 2023 Andrew Lyons <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @codeCoverageIgnore
* This code does not warrant being tested. Testing offers no discernible benefit given its usage is tested.
*/
trait create_room_v3 {
/**
* Create a new room.
*
* @param string $name The room name
* @param null|string $visibility The room visibility
* @param null|string $preset The preset to use
* @param null|array $initialstate Initial state variables
* @param array $options Any additional options
* @return Response
*/
public function create_room(
string $name,
?string $visibility = null,
?string $preset = null,
?array $initialstate = null,
array $options = [],
): Response {
$params = [
'name' => $name,
];
if ($visibility !== null) {
$params['visibility'] = $visibility;
}
if ($preset !== null) {
$params['preset'] = $preset;
}
if ($initialstate !== null) {
$params['initial_state'] = $initialstate;
}
if (array_key_exists('topic', $options)) {
$params['topic'] = $options['topic'] ?? '';
}
return $this->execute(new command(
$this,
method: 'POST',
endpoint: '_matrix/client/v3/createRoom',
params: $params,
));
}
}
@@ -0,0 +1,52 @@
<?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_matrix\local\spec\features\matrix;
use communication_matrix\local\command;
use GuzzleHttp\Psr7\Response;
/**
* Matrix API feature to fetch a list of room members.
*
* https://spec.matrix.org/v1.1/client-server-api/#get_matrixclientv3roomsroomidjoined_members
*
* @package communication_matrix
* @copyright 2023 Andrew Lyons <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @codeCoverageIgnore
* This code does not warrant being tested. Testing offers no discernible benefit given its usage is tested.
*/
trait get_room_members_v3 {
/**
* Get a list of room members.
*
* @param string $roomid The room ID
* @return Response
*/
public function get_room_members(string $roomid): Response {
$params = [
':roomid' => $roomid,
];
return $this->execute(new command(
$this,
method: 'GET',
endpoint: '_matrix/client/v3/rooms/:roomid/joined_members',
params: $params,
));
}
}
@@ -0,0 +1,53 @@
<?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_matrix\local\spec\features\matrix;
use communication_matrix\local\command;
use GuzzleHttp\Psr7\Response;
/**
* Matrix API feature to fetch room power levels.
*
* https://spec.matrix.org/v1.1/client-server-api/#mroompower_levels
*
* @package communication_matrix
* @copyright 2024 David Woloszyn <david.woloszyn@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @codeCoverageIgnore
* This code does not warrant being tested. Testing offers no discernible benefit given its usage is tested.
*/
trait get_room_power_levels_v3 {
/**
* Get a list of room members and their power levels.
*
* @param string $roomid The room ID
* @return Response
*/
public function get_room_power_levels(string $roomid): Response {
$params = [
':roomid' => $roomid,
];
return $this->execute(new command(
$this,
method: 'GET',
endpoint: '_matrix/client/r0/rooms/:roomid/state/m.room.power_levels',
params: $params,
));
}
}
@@ -0,0 +1,91 @@
<?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_matrix\local\spec\features\matrix;
use communication_matrix\local\command;
use GuzzleHttp\Psr7\Response;
/**
* Matrix API feature to fetch room power levels using the sync API.
*
* https://spec.matrix.org/v1.1/client-server-api/#get_matrixclientv3sync
*
* @package communication_matrix
* @copyright 2023 Andrew Lyons <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @codeCoverageIgnore
* This code does not warrant being tested. Testing offers no discernible benefit given its usage is tested.
*/
trait get_room_powerlevels_from_sync_v3 {
/**
* Get a list of room members.
*
* @param string $roomid The room ID
* @return Response
*/
public function get_room_power_levels_from_sync(string $roomid): Response {
// Filter the event data according to the API:
// https://spec.matrix.org/v1.1/client-server-api/#filtering
// We have to filter out all of the object data that we do not want,
// and set a filter to only fetch the one room that we do want.
$filter = (object) [
"account_data" => (object) [
// We don't want any account info for this call.
"not_types" => ['*'],
],
"event_fields" => [
// We only care about type, and content. Not sender.
"type",
"content",
],
"event_format" => "client",
"presence" => (object) [
// We don't need any presence data.
"not_types" => ['*'],
],
"room" => (object) [
// We only want state information for power levels, not timeline and ephemeral data.
"rooms" => [
$roomid,
],
"state" => (object) [
"types" => [
"m.room.power_levels",
],
],
"ephemeral" => (object) [
"not_types" => ['*'],
],
"timeline" => (object) [
"not_types" => ['*'],
],
],
];
$query = [
'filter' => json_encode($filter),
];
return $this->execute(new command(
$this,
method: 'GET',
endpoint: '_matrix/client/v3/sync',
query: $query,
sendasjson: false,
));
}
}
@@ -0,0 +1,46 @@
<?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_matrix\local\spec\features\matrix;
use communication_matrix\local\command;
use GuzzleHttp\Psr7\Response;
/**
* Matrix API feature to create an mxc Media URI.
*
* https://spec.matrix.org/v1.1/client-server-api/#post_matrixmediav3upload
*
* @package communication_matrix
* @copyright 2023 Andrew Lyons <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @codeCoverageIgnore
* This code does not warrant being tested. Testing offers no discernible benefit given its usage is tested.
*/
trait media_create_v1 {
/**
* Create a media URI.
*
* @return Response
*/
public function media_create(): Response {
return $this->execute(new command(
$this,
method: 'POST',
endpoint: '_matrix/media/v1/create',
));
}
}
@@ -0,0 +1,57 @@
<?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_matrix\local\spec\features\matrix;
use communication_matrix\local\command;
use GuzzleHttp\Psr7\Response;
/**
* Matrix API feature to remove a member from a room.
*
* https://spec.matrix.org/v1.1/client-server-api/#post_matrixclientv3roomsroomidkick
*
* @package communication_matrix
* @copyright 2023 Andrew Lyons <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @codeCoverageIgnore
* This code does not warrant being tested. Testing offers no discernible benefit given its usage is tested.
*/
trait remove_member_from_room_v3 {
/**
* Remove a member from a room.
*
* @param string $roomid The roomid to remove from
* @param string $userid The member to remove
* @return Response
*/
public function remove_member_from_room(
string $roomid,
string $userid,
): Response {
$params = [
':roomid' => $roomid,
'user_id' => $userid,
];
return $this->execute(new command(
$this,
method: 'POST',
endpoint: '_matrix/client/v3/rooms/:roomid/kick',
params: $params,
));
}
}
@@ -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 communication_matrix\local\spec\features\matrix;
use communication_matrix\local\command;
use GuzzleHttp\Psr7\Response;
/**
* Matrix API feature to update a room avatar.
*
* https://spec.matrix.org/v1.1/client-server-api/#put_matrixclientv3roomsroomidstateeventtypestatekey
*
* @package communication_matrix
* @copyright 2023 Andrew Lyons <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @codeCoverageIgnore
* This code does not warrant being tested. Testing offers no discernible benefit given its usage is tested.
*/
trait update_room_avatar_v3 {
/**
* Set the avatar for a room to the specified URL.
*
* @param string $roomid The roomid to set for
* @param null|string $avatarurl The mxc URL to use
* @return Response
*/
public function update_room_avatar(
string $roomid,
?string $avatarurl,
): Response {
$params = [
':roomid' => $roomid,
'url' => $avatarurl,
];
return $this->execute(new command(
$this,
method: 'PUT',
endpoint: '_matrix/client/v3/rooms/:roomid/state/m.room.avatar',
ignorehttperrors: true,
params: $params,
));
}
}
@@ -0,0 +1,54 @@
<?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_matrix\local\spec\features\matrix;
use communication_matrix\local\command;
use GuzzleHttp\Psr7\Response;
/**
* Matrix API feature to update a room name.
*
* https://spec.matrix.org/v1.1/client-server-api/#put_matrixclientv3roomsroomidstateeventtypestatekey
*
* @package communication_matrix
* @copyright 2023 Andrew Lyons <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @codeCoverageIgnore
* This code does not warrant being tested. Testing offers no discernible benefit given its usage is tested.
*/
trait update_room_name_v3 {
/**
* Set the name for a room.
*
* @param string $roomid
* @param string $name
* @return Response
*/
public function update_room_name(string $roomid, string $name): Response {
$params = [
':roomid' => $roomid,
'name' => $name,
];
return $this->execute(new command(
$this,
method: 'PUT',
endpoint: '_matrix/client/v3/rooms/:roomid/state/m.room.name',
params: $params,
));
}
}
@@ -0,0 +1,77 @@
<?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_matrix\local\spec\features\matrix;
use communication_matrix\local\command;
use communication_matrix\matrix_constants;
use GuzzleHttp\Psr7\Response;
/**
* Matrix API feature to update a room power levels.
*
* Matrix rooms have a concept of power levels, which are used to determine what actions a user can perform in a room.
*
* https://spec.matrix.org/v1.1/client-server-api/#mroompower_levels
*
* @package communication_matrix
* @copyright 2023 Safat Shahin <safat.shahin@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @codeCoverageIgnore
* This code does not warrant being tested. Testing offers no discernible benefit given its usage is tested.
*/
trait update_room_power_levels_v3 {
/**
* Set the avatar for a room to the specified URL.
*
* @param string $roomid The roomid to set for
* @param array $users The users to set power levels for
* @param int $ban The level required to ban a user
* @param int $invite The level required to invite a user
* @param int $kick The level required to kick a user
* @param array $notifications The level required to send notifications
* @param int $redact The level required to redact events
* @return Response
*/
public function update_room_power_levels(
string $roomid,
array $users,
int $ban = matrix_constants::POWER_LEVEL_MAXIMUM,
int $invite = matrix_constants::POWER_LEVEL_MODERATOR,
int $kick = matrix_constants::POWER_LEVEL_MODERATOR,
array $notifications = [
'room' => matrix_constants::POWER_LEVEL_MODERATOR,
],
int $redact = matrix_constants::POWER_LEVEL_MODERATOR,
): Response {
$params = [
':roomid' => $roomid,
'ban' => $ban,
'invite' => $invite,
'kick' => $kick,
'notifications' => $notifications,
'redact' => $redact,
'users' => $users,
];
return $this->execute(new command(
$this,
method: 'PUT',
endpoint: '_matrix/client/v3/rooms/:roomid/state/m.room.power_levels',
params: $params,
));
}
}
@@ -0,0 +1,54 @@
<?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_matrix\local\spec\features\matrix;
use communication_matrix\local\command;
use GuzzleHttp\Psr7\Response;
/**
* Matrix API feature to update a room topic.
*
* https://spec.matrix.org/v1.1/client-server-api/#put_matrixclientv3roomsroomidstateeventtypestatekey
*
* @package communication_matrix
* @copyright 2023 Andrew Lyons <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @codeCoverageIgnore
* This code does not warrant being tested. Testing offers no discernible benefit given its usage is tested.
*/
trait update_room_topic_v3 {
/**
* Set the topic for a room.
*
* @param string $roomid
* @param string $topic
* @return Response
*/
public function update_room_topic(string $roomid, string $topic): Response {
$params = [
':roomid' => $roomid,
'topic' => $topic,
];
return $this->execute(new command(
$this,
method: 'PUT',
endpoint: '_matrix/client/v3/rooms/:roomid/state/m.room.topic',
params: $params,
));
}
}
@@ -0,0 +1,83 @@
<?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_matrix\local\spec\features\matrix;
use communication_matrix\local\command;
use GuzzleHttp\Psr7\Response;
use GuzzleHttp\Psr7\Utils;
/**
* Matrix API feature to upload content.
*
* https://spec.matrix.org/v1.1/client-server-api/#post_matrixmediav3upload
*
* @package communication_matrix
* @copyright 2023 Andrew Lyons <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @codeCoverageIgnore
* This code does not warrant being tested. Testing offers no discernible benefit given its usage is tested.
*/
trait upload_content_v3 {
/**
* Upload the content in the matrix/synapse server.
*
* @param null|\stored_file $content The content to be uploaded
* @param null|string $mediaid The mediaid to associate a file with. Supported for v1.7 API an above only.
* @return Response
*/
public function upload_content(
?\stored_file $content,
?string $mediaid = null,
): Response {
$query = [];
if ($content) {
$query['filename'] = $content->get_filename();
}
if ($mediaid !== null) {
// Specification of the mediaid requires version 1.7 or above of the upload API.
// See https://spec.matrix.org/v1.7/client-server-api/#put_matrixmediav3uploadservernamemediaid.
$this->requires_version('1.7');
$command = new command(
$this,
method: 'PUT',
endpoint: '_matrix/media/v3/upload/:mediaid',
sendasjson: false,
query: $query,
params: [
':mediaid' => $mediaid,
],
);
} else {
$command = new command(
$this,
method: 'POST',
endpoint: '_matrix/media/v3/upload',
sendasjson: false,
query: $query,
);
}
if ($content) {
// Add the content-type, and header.
$command = $command->withHeader('Content-Type', $content->get_mimetype());
$command = $command->withBody(Utils::streamFor($content->get_content()));
}
return $this->execute($command);
}
}
@@ -0,0 +1,65 @@
<?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_matrix\local\spec\features\synapse;
use communication_matrix\local\command;
use GuzzleHttp\Psr7\Response;
/**
* Synapse API feature for creating a user.
*
* https://matrix-org.github.io/synapse/latest/admin_api/user_admin_api.html#create-or-modify-account
*
* @package communication_matrix
* @copyright 2023 Andrew Lyons <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @codeCoverageIgnore
* This code does not warrant being tested. Testing offers no discernible benefit given its usage is tested.
*/
trait create_user_v2 {
/**
* Create a new user.
*
* @param string $userid The Matrix user id.
* @param string $displayname The visible name of the user
* @param array $threepids The third-party identifiers of the user.
* @param null|array $externalids
*/
public function create_user(
string $userid,
string $displayname,
array $threepids,
?array $externalids = null,
): Response {
$params = [
':userid' => $userid,
'displayname' => $displayname,
'threepids' => $threepids,
];
if ($externalids !== null) {
$params['externalids'] = $externalids;
}
return $this->execute(new command(
$this,
method: 'PUT',
endpoint: '_synapse/admin/v2/users/:userid',
params: $params,
));
}
}
@@ -0,0 +1,50 @@
<?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_matrix\local\spec\features\synapse;
use communication_matrix\local\command;
use GuzzleHttp\Psr7\Response;
/**
* Synapse API feature for fetching room info.
*
* https://matrix-org.github.io/synapse/latest/admin_api/rooms.html#room-details-api
*
* @package communication_matrix
* @copyright 2023 Andrew Lyons <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @codeCoverageIgnore
* This code does not warrant being tested. Testing offers no discernible benefit given its usage is tested.
*/
trait get_room_info_v1 {
/**
* Get room info.
*
* @param string $roomid
* @return Response
*/
public function get_room_info(string $roomid): Response {
return $this->execute(new command(
$this,
method: 'GET',
endpoint: '_synapse/admin/v1/rooms/:roomid',
params: [
':roomid' => $roomid,
],
));
}
}
@@ -0,0 +1,51 @@
<?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_matrix\local\spec\features\synapse;
use communication_matrix\local\command;
use GuzzleHttp\Psr7\Response;
/**
* Synapse API feature for fetching info about a user.
*
* https://matrix-org.github.io/synapse/latest/admin_api/user_admin_api.html#query-user-account
*
* @package communication_matrix
* @copyright 2023 Andrew Lyons <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @codeCoverageIgnore
* This code does not warrant being tested. Testing offers no discernible benefit given its usage is tested.
*/
trait get_user_info_v2 {
/**
* Get user info.
*
* @param string $userid
* @return Response
*/
public function get_user_info(string $userid): Response {
return $this->execute(new command(
$this,
method: 'GET',
endpoint: '_synapse/admin/v2/users/:userid',
ignorehttperrors: true,
params: [
':userid' => $userid,
],
));
}
}
@@ -0,0 +1,56 @@
<?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_matrix\local\spec\features\synapse;
use communication_matrix\local\command;
use GuzzleHttp\Psr7\Response;
/**
* Synapse API feature to invite a user into a room.
*
* https://matrix-org.github.io/synapse/latest/admin_api/room_membership.html#edit-room-membership-api
*
* @package communication_matrix
* @copyright 2023 Andrew Lyons <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @codeCoverageIgnore
* This code does not warrant being tested. Testing offers no discernible benefit given its usage is tested.
*/
trait invite_member_to_room_v1 {
/**
* Join a user to a room.
*
* Note: This joins the user, and does not invite them.
*
* @param string $roomid
* @param string $userid
* @return Response
*/
public function invite_member_to_room(string $roomid, string $userid): Response {
$params = [
':roomid' => $roomid,
'user_id' => $userid,
];
return $this->execute(new command(
$this,
method: 'POST',
endpoint: '_synapse/admin/v1/join/:roomid',
params: $params,
));
}
}
@@ -0,0 +1,48 @@
<?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_matrix\local\spec;
/**
* Matrix API to support version v1.1 of the Matrix specification.
*
* https://spec.matrix.org/v1.1/client-server-api/
*
* @package communication_matrix
* @copyright 2023 Andrew Lyons <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class v1p1 extends \communication_matrix\matrix_client {
// Use the standard matrix API for these features.
use features\matrix\create_room_v3;
use features\matrix\get_room_members_v3;
use features\matrix\remove_member_from_room_v3;
use features\matrix\update_room_avatar_v3;
use features\matrix\update_room_name_v3;
use features\matrix\update_room_topic_v3;
use features\matrix\upload_content_v3;
use features\matrix\update_room_power_levels_v3;
use features\matrix\get_room_powerlevels_from_sync_v3;
use features\matrix\get_room_power_levels_v3;
// We use the Synapse API here because it can invite users to a room without requiring them to accept the invite.
use features\synapse\invite_member_to_room_v1;
// User information and creation is a server-specific feature.
use features\synapse\get_user_info_v2;
use features\synapse\create_user_v2;
use features\synapse\get_room_info_v1;
}
@@ -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/>.
namespace communication_matrix\local\spec;
/**
* Matrix API to support version v1.2 of the Matrix specification.
*
* https://spec.matrix.org/v1.2/client-server-api/
* https://spec.matrix.org/v1.2/changelog/#api-changes
*
* @package communication_matrix
* @copyright 2023 Andrew Lyons <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class v1p2 extends v1p1 {
}
@@ -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/>.
namespace communication_matrix\local\spec;
/**
* Matrix API to support version v1.3 of the Matrix specification.
*
* https://spec.matrix.org/v1.3/client-server-api/
* https://spec.matrix.org/v1.3/changelog/#api-changes
*
* @package communication_matrix
* @copyright 2023 Andrew Lyons <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class v1p3 extends v1p2 {
}
@@ -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/>.
namespace communication_matrix\local\spec;
/**
* Matrix API to support version v1.4 of the Matrix specification.
*
* https://spec.matrix.org/v1.4/client-server-api/
* https://spec.matrix.org/v1.4/changelog/#api-changes
*
* @package communication_matrix
* @copyright 2023 Andrew Lyons <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class v1p4 extends v1p3 {
}
@@ -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/>.
namespace communication_matrix\local\spec;
/**
* Matrix API to support version v1.5 of the Matrix specification.
*
* https://spec.matrix.org/v1.5/client-server-api/
* https://spec.matrix.org/v1.5/changelog/#api-changes
*
* @package communication_matrix
* @copyright 2023 Andrew Lyons <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class v1p5 extends v1p4 {
}
@@ -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/>.
namespace communication_matrix\local\spec;
/**
* Matrix API to support version v1.6 of the Matrix specification.
*
* https://spec.matrix.org/v1.6/client-server-api/
* https://spec.matrix.org/v1.6/changelog/#api-changes
*
* @package communication_matrix
* @copyright 2023 Andrew Lyons <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class v1p6 extends v1p5 {
}
@@ -0,0 +1,34 @@
<?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_matrix\local\spec;
/**
* Matrix API to support version v1.7 of the Matrix specification.
*
* https://spec.matrix.org/v1.7/client-server-api/
* https://spec.matrix.org/v1.7/changelog/#api-changes
*
* @package communication_matrix
* @copyright 2023 Andrew Lyons <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class v1p7 extends v1p6 {
// Note: A new Content Upload API was introduced.
// See details in the spec:
// https://github.com/matrix-org/matrix-spec-proposals/pull/2246.
use features\matrix\media_create_v1;
}
@@ -0,0 +1,348 @@
<?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_matrix;
use communication_matrix\local\command;
use core\http_client;
use DirectoryIterator;
use Exception;
use GuzzleHttp\Psr7\Response;
/**
* The abstract class for a versioned API client for Matrix.
*
* Matrix uses a versioned API, and a handshake occurs between the Client (Moodle) and server, to determine the APIs available.
*
* This client represents a version-less API client.
* Versions are implemented by combining the various features into a versionedclass.
* See v1p1 for example.
*
* @package communication_matrix
* @copyright 2023 Andrew Lyons <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
abstract class matrix_client {
/** @var string $serverurl The URL of the home server */
/** @var string $accesstoken The access token of the matrix server */
/** @var http_client|null The client to use */
protected static http_client|null $client = null;
/**
* Matrix events constructor to get the room id and refresh token usage if required.
*
* @param string $serverurl The URL of the API server
* @param string $accesstoken The admin access token
*/
protected function __construct(
protected string $serverurl,
protected string $accesstoken,
) {
}
/**
* Return the versioned instance of the API.
*
* @param string $serverurl The URL of the API server
* @param string $accesstoken The admin access token to use
* @return matrix_client|null
*/
public static function instance(
string $serverurl,
string $accesstoken,
): ?matrix_client {
// Fetch the list of supported API versions.
$clientversions = self::get_supported_versions();
// Fetch the supported versions from the server.
$serversupports = self::query_server_supports($serverurl);
if ($serversupports === null) {
// Unable to fetch the server versions.
return null;
}
$serverversions = $serversupports->versions;
// Calculate the intersections and sort to determine the highest combined version.
$versions = array_intersect($clientversions, $serverversions);
if (count($versions) === 0) {
// No versions in common.
throw new \moodle_exception('No supported Matrix API versions found.');
}
asort($versions);
$version = array_key_last($versions);
$classname = \communication_matrix\local\spec::class . '\\' . $version;
return new $classname(
$serverurl,
$accesstoken,
);
}
/**
* Determine if the API supports a feature.
*
* If an Array is provided, this will return true if any of the specified features is implemented.
*
* @param string[]|string $feature The feature to check. This is in the form of a namespaced class.
* @return bool
*/
public function implements_feature(array|string $feature): bool {
if (is_array($feature)) {
foreach ($feature as $thisfeature) {
if ($this->implements_feature($thisfeature)) {
return true;
}
}
// None of the features are implemented in this API version.
return false;
}
return in_array($feature, $this->get_supported_features());
}
/**
* Get a list of the features supported by this client.
*
* @return string[]
*/
public function get_supported_features(): array {
$features = [];
$class = static::class;
do {
$features = array_merge($features, class_uses($class));
$class = get_parent_class($class);
} while ($class);
return $features;
}
/**
* Require that the API supports a feature.
*
* If an Array is provided, this is treated as a require any of the features.
*
* @param string[]|string $feature The feature to test
* @throws \moodle_exception
*/
public function require_feature(array|string $feature): void {
if (!$this->implements_feature($feature)) {
if (is_array($feature)) {
$features = implode(', ', $feature);
throw new \moodle_exception(
"None of the possible feature are implemented in this Matrix Client: '{$features}'"
);
}
throw new \moodle_exception("The requested feature is not implemented in this Matrix Client: '{$feature}'");
}
}
/**
* Require that the API supports a list of features.
*
* All features specified will be required.
*
* If an array is provided as one of the features, any of the items in the nested array will be required.
*
* @param string[]|array[] $features The list of features required
*
* Here is an example usage:
* <code>
* $matrixapi->require_features([
*
* \communication_matrix\local\spec\features\create_room::class,
* [
* \communication_matrix\local\spec\features\get_room_info_v1::class,
* \communication_matrix\local\spec\features\get_room_info_v2::class,
* ]
* ])
* </code>
*/
public function require_features(array $features): void {
array_walk($features, [$this, 'require_feature']);
}
/**
* Get the URL of the server.
*
* @return string
*/
public function get_server_url(): string {
return $this->serverurl;
}
/**
* Query the supported versions, and any unstable features, from the server.
*
* Servers must implement the client versions API described here:
* - https://spec.matrix.org/latest/client-server-api/#get_matrixclientversions
*
* @param string $serverurl The server base
* @return null|\stdClass The list of supported versions and a list of enabled unstable features
*/
protected static function query_server_supports(string $serverurl): ?\stdClass {
// Attempt to return from the cache first.
$cache = \cache::make('communication_matrix', 'serverversions');
$serverkey = sha1($serverurl);
if ($cache->get($serverkey)) {
return $cache->get($serverkey);
}
// Not in the cache - fetch and store in the cache.
try {
$client = static::get_http_client();
$response = $client->get("{$serverurl}/_matrix/client/versions");
$supportsdata = json_decode(
json: $response->getBody(),
associative: false,
flags: JSON_THROW_ON_ERROR,
);
$cache->set($serverkey, $supportsdata);
return $supportsdata;
} catch (\GuzzleHttp\Exception\TransferException $e) {
return null;
}
}
/**
* Get the list of supported versions based on the available classes.
*
* @return array
*/
public static function get_supported_versions(): array {
$versions = [];
$iterator = new DirectoryIterator(__DIR__ . '/local/spec');
foreach ($iterator as $fileinfo) {
if ($fileinfo->isDir()) {
continue;
}
// Get the classname from the filename.
$classname = substr($fileinfo->getFilename(), 0, -4);
if (!preg_match('/^v\d+p\d+$/', $classname)) {
// @codeCoverageIgnoreStart
// This file does not fit the format v[MAJOR]p[MINOR]].
continue;
// @codeCoverageIgnoreEnd
}
$versions[$classname] = "v" . self::get_version_from_classname($classname);
}
return $versions;
}
/**
* Get the current token in use.
*
* @return string
*/
public function get_token(): string {
return $this->accesstoken;
}
/**
* Helper to fetch the HTTP Client for the instance.
*
* @return \core\http_client
*/
protected function get_client(): \core\http_client {
return static::get_http_client();
}
/**
* Helper to fetch the HTTP Client.
*
* @return \core\http_client
*/
protected static function get_http_client(): \core\http_client {
if (static::$client !== null) {
return static::$client;
}
// @codeCoverageIgnoreStart
return new http_client();
// @codeCoverageIgnoreEnd
}
/**
* Execute the specified command.
*
* @param command $command
* @return Response
*/
protected function execute(
command $command,
): Response {
$client = $this->get_client();
return $client->send(
$command,
$command->get_options(),
);
}
/**
* Get the API version of the current instance.
*
* @return string
*/
public function get_version(): string {
$reflect = new \ReflectionClass(static::class);
$classname = $reflect->getShortName();
return self::get_version_from_classname($classname);
}
/**
* Normalise an API version from a classname.
*
* @param string $classname The short classname, omitting any namespace or file extension
* @return string The normalised version
*/
protected static function get_version_from_classname(string $classname): string {
$classname = str_replace('v', '', $classname);
$classname = str_replace('p', '.', $classname);
return $classname;
}
/**
* Check if the API version is at least the specified version.
*
* @param string $minversion The minimum API version required
* @return bool
*/
public function meets_version(string $minversion): bool {
$thisversion = $this->get_version();
return version_compare($thisversion, $minversion) >= 0;
}
/**
* Assert that the API version is at least the specified version.
*
* @param string $minversion The minimum API version required
* @throws Exception
*/
public function requires_version(string $minversion): void {
if ($this->meets_version($minversion)) {
return;
}
throw new \moodle_exception("Matrix API version {$minversion} or higher is required for this command.");
}
}
@@ -0,0 +1,51 @@
<?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_matrix;
/**
* class matrix_constants to have one location to store all constants related to matrix.
*
* @package communication_matrix
* @copyright 2023 Safat Shahin <safat.shahin@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class matrix_constants {
/**
* User default power level for matrix.
*/
public const POWER_LEVEL_DEFAULT = 0;
/**
* User moderator power level for matrix.
*/
public const POWER_LEVEL_MODERATOR = 50;
/**
* User moderator power level for matrix.
*/
public const POWER_LEVEL_MOODLE_MODERATOR = 51;
/**
* User power level for matrix associated to moodle site admins. It is a custom power level for site admins.
*/
public const POWER_LEVEL_MOODLE_SITE_ADMIN = 90;
/**
* User maximum power level for matrix. This is only associated to the token user to allow god mode actions.
*/
public const POWER_LEVEL_MAXIMUM = 100;
}
@@ -0,0 +1,146 @@
<?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_matrix;
use stdClass;
/**
* Class to manage the updates to the room information in db.
*
* @package communication_matrix
* @copyright 2023 Safat Shahin <safat.shahin@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class matrix_room {
private const TABLE = 'matrix_room';
/** @var \stdClass|null $record The matrix room record from db */
/**
* Load the matrix room record for the supplied processor.
* @param int $processorid
* @return null|self
*/
public static function load_by_processor_id(
int $processorid,
): ?self {
global $DB;
$record = $DB->get_record(self::TABLE, ['commid' => $processorid]);
if (!$record) {
return null;
}
return new self($record);
}
/**
* Matrix rooms constructor to load the matrix room information from matrix_room table.
*
* @param stdClass $record
*/
private function __construct(
private stdClass $record,
) {
}
/**
* Create matrix room data.
*
* @param int $processorid The id of the communication record
* @param string|null $topic The topic of the room for matrix
* @param string|null $roomid The id of the room from matrix
* @return self
*/
public static function create_room_record(
int $processorid,
?string $topic,
?string $roomid = null,
): self {
global $DB;
$roomrecord = (object) [
'commid' => $processorid,
'roomid' => $roomid,
'topic' => $topic,
];
$roomrecord->id = $DB->insert_record(self::TABLE, $roomrecord);
return self::load_by_processor_id($processorid);
}
/**
* Update matrix room data.
*
* @param string|null $roomid The id of the room from matrix
* @param string|null $topic The topic of the room for matrix
*/
public function update_room_record(
?string $roomid = null,
?string $topic = null,
): void {
global $DB;
if ($roomid !== null) {
$this->record->roomid = $roomid;
}
if ($topic !== null) {
$this->record->topic = $topic;
}
$DB->update_record(self::TABLE, $this->record);
}
/**
* Delete matrix room data.
*/
public function delete_room_record(): void {
global $DB;
$DB->delete_records(self::TABLE, ['commid' => $this->record->commid]);
unset($this->record);
}
/**
* Get the processor id.
*
* @return int
*/
public function get_processor_id(): int {
return $this->record->commid;
}
/**
* Get the matrix room id.
*
* @return string|null
*/
public function get_room_id(): ?string {
return $this->record->roomid;
}
/**
* Get the matrix room topic.
*
* @return string
*/
public function get_topic(): string {
return $this->record->topic ?? '';
}
}
@@ -0,0 +1,203 @@
<?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_matrix;
/**
* class matrix_user_manager to handle specific actions.
*
* @package communication_matrix
* @copyright 2023 Stevani Andolo <stevani.andolo@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class matrix_user_manager {
/**
* Prefix for Matrix usernames when they are detected as numeric.
*/
const MATRIX_USER_PREFIX = 'user';
/**
* Gets matrix user id from moodle.
*
* @param int $userid Moodle user id
* @return string|null
*/
public static function get_matrixid_from_moodle(
int $userid,
): ?string {
self::load_requirements();
$field = profile_user_record($userid);
$matrixprofilefield = get_config('communication_matrix', 'matrixuserid_field');
if ($matrixprofilefield === false) {
return null;
}
return $field->{$matrixprofilefield} ?? null;
}
/**
* Get a qualified matrix user id based on a Moodle username.
*
* @param string $username The moodle username to turn into a Matrix username
* @return string
*/
public static function get_formatted_matrix_userid(
string $username,
): string {
$username = preg_replace('/[@#$%^&*()+{}|<>?!,]/i', '.', $username);
$username = ltrim(rtrim($username, '.'), '.');
// Matrix/Synapse servers will not allow numeric usernames.
if (is_numeric($username)) {
$username = self::MATRIX_USER_PREFIX . $username;
}
$homeserver = self::get_formatted_matrix_home_server();
return "@{$username}:{$homeserver}";
}
/**
* Add user's Matrix user id.
*
* @param int $userid Moodle user id
* @param string $matrixuserid Matrix user id
*/
public static function set_matrix_userid_in_moodle(
int $userid,
string $matrixuserid,
): void {
$matrixprofilefield = self::get_profile_field_name();
$field = profile_get_custom_field_data_by_shortname($matrixprofilefield);
if ($field === null) {
return;
}
$userinfodata = (object) [
'id' => $userid,
'data' => $matrixuserid,
'fieldid' => $field->id,
"profile_field_{$matrixprofilefield}" => $matrixuserid,
];
profile_save_data($userinfodata);
}
/**
* Sets home server for user matrix id
*
* @return string
*/
public static function get_formatted_matrix_home_server(): string {
$homeserver = get_config('communication_matrix', 'matrixhomeserverurl');
if ($homeserver === false) {
throw new \moodle_exception('Unknown matrix homeserver url');
}
$homeserver = parse_url($homeserver)['host'];
if (str_starts_with($homeserver, 'www.')) {
$homeserver = str_replace('www.', '', $homeserver);
}
return $homeserver;
}
/**
* Insert "Communication" category and "matrixuserid" field.
*
* @return string
*/
public static function create_matrix_user_profile_fields(): string {
global $CFG, $DB;
require_once($CFG->dirroot . '/user/profile/definelib.php');
require_once($CFG->dirroot . '/user/profile/field/text/define.class.php');
// Check if communication category exists.
$categoryname = get_string('communication', 'core_communication');
$category = $DB->count_records('user_info_category', ['name' => $categoryname]);
if ($category < 1) {
$data = new \stdClass();
$data->sortorder = $DB->count_records('user_info_category') + 1;
$data->name = $categoryname;
$data->id = $DB->insert_record('user_info_category', $data);
$createdcategory = $DB->get_record('user_info_category', ['id' => $data->id]);
$categoryid = $createdcategory->id;
\core\event\user_info_category_created::create_from_category($createdcategory)->trigger();
} else {
$category = $DB->get_record('user_info_category', ['name' => $categoryname]);
$categoryid = $category->id;
}
set_config('communication_category_field', $categoryname, 'core_communication');
// Check if matrixuserid exists in user_info_field table.
$matrixuserid = $DB->count_records('user_info_field', [
'shortname' => 'matrixuserid',
'categoryid' => $categoryid,
]);
if ($matrixuserid < 1) {
$profileclass = new \profile_define_text();
$data = (object) [
'shortname' => 'matrixuserid',
'name' => get_string('matrixuserid', 'communication_matrix'),
'datatype' => 'text',
'categoryid' => $categoryid,
'forceunique' => 1,
'visible' => 0,
'locked' => 1,
'param1' => 30,
'param2' => 2048,
];
$profileclass->define_save($data);
set_config('matrixuserid_field', 'matrixuserid', 'communication_matrix');
return 'matrixuserid';
}
}
/**
* Get the profile field name, creating the profiel field if it does not exist.
*
* @return string
*/
protected static function get_profile_field_name(): string {
self::load_requirements();
$matrixprofilefield = get_config('communication_matrix', 'matrixuserid_field');
if ($matrixprofilefield === false) {
$matrixprofilefield = self::create_matrix_user_profile_fields();
}
return $matrixprofilefield;
}
/**
* Load requirements for profile field management.
*
* This is just a helper to keep loading legacy files isolated.
*/
protected static function load_requirements(): void {
global $CFG;
require_once("{$CFG->dirroot}/user/profile/lib.php");
}
}
@@ -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_matrix\privacy;
use core_privacy\local\metadata\null_provider;
/**
* Privacy Subsystem for communication_matrix implementing null_provider.
*
* @package communication_matrix
* @copyright 2023 Safat Shahin <safat.shahin@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @codeCoverageIgnore
*/
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';
}
}