Skip to content

Commit

Permalink
Merge pull request #8 from davewoloszyn/MDL-79460
Browse files Browse the repository at this point in the history
MDL-79460: Add support for powerlevels and route for sync
  • Loading branch information
andrewnicols authored Oct 27, 2023
2 parents fbbdd64 + 8affc63 commit 5254b2c
Show file tree
Hide file tree
Showing 6 changed files with 276 additions and 1 deletion.
66 changes: 65 additions & 1 deletion application/src/Controller/MatrixController.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@
use App\Entity\RoomMember;
use App\Entity\Token;
use App\Entity\User;
use App\Service\Filter;
use App\Traits\GeneralTrait;
use App\Traits\MatrixSynapseTrait;
use stdClass;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\Routing\Annotation\Route;
Expand Down Expand Up @@ -273,6 +273,51 @@ public function createRoom(string $serverID, Request $request):JsonResponse {
return new JsonResponse((object) $response, 200);
}

/**
* Sync Matrix room.
* Sync room according to the API:
* https://spec.matrix.org/v1.8/client-server-api/#get_matrixclientv3sync
*
* @Route("/v3/sync")
* @param string $serverID
* @param Request $request
* @return JsonResponse
*/
public function sync(string $serverID, Request $request):JsonResponse {
// 1. Check call auth.
// 2. Check HTTP method is accepted.
$accessCheck = $this->authHttpCheck(['GET'], $request);
if (!$accessCheck['status']) {
return $accessCheck['message'];
}
// Params are being queried via a filter in the request.
$filter = Filter::fromRequest($request);

$roomSearch = [
'serverid' => $serverID,
];

if ($roomIds = $filter->getRoomFilter()->getRoomIds()) {
$roomSearch['roomid'] = $roomIds;
}

// Get a list of rooms.
$entityManager = $this->getDoctrine()->getManager();
$roomList = $entityManager->getRepository(Room::class)->findBy($roomSearch);

// Get room state data for each room.
$roomData = [];
foreach ($roomList as $room) {
$roomData[$room->getRoomId()] = $room->getRoomState($request);
}

return new JsonResponse((object) [
'rooms' => [
'join' => $roomData,
],
]);
}

/**
* Create Matrix room.
*
Expand Down Expand Up @@ -402,6 +447,25 @@ public function roomState(string $serverID, string $roomID, string $eventType, R
if (!$check['status']) {
return $check['message'];
}
// Update power levels for each room member.
foreach ($payload->users as $userid => $level) {
if (!empty($level)) {
$user = $entityManager->getRepository(User::class)->findOneBy([
'serverid' => $serverID,
'userid' => $userid,
]);
if (!empty($user)) {
$membership = $entityManager->getRepository(RoomMember::class)->findOneBy([
'serverid' => $serverID,
'room' => $room,
'user' => $user,
]);
if(!empty($membership)) {
$membership->setPowerLevel($level);
}
}
}
}
} else {
// Unknown state.
return new JsonResponse((object) [
Expand Down
58 changes: 58 additions & 0 deletions application/src/Entity/Room.php
Original file line number Diff line number Diff line change
Expand Up @@ -200,4 +200,62 @@ public function getMembers(): Collection
{
return $this->members;
}

public function getRoomState(): array
{
// We will probably need to pass a filter in the future.
// At the moment we only know how to serve a set of power levels.
$roomData = [
'state' => [
'events' => [],
],
];

$roomData['state']['events'][] = $this->getPowerLevelState();

return $roomData;
}

protected function getPowerLevelState(): array
{
// Power level values.
$powerLevelDefault = 0;
$powerLevelModerator = 50;
$powerLevelMaximum = 100;

// Get the members of the room who have a powerlevel set.
$members = $this->getMembers()->filter(function (RoomMember $member) {
return $member->getPowerLevel() !== null;
});

// Build the power level state.
$memberInfo = $members->map(fn(RoomMember $member) => [
$member->getUser()->getUserid() => $member->getPowerLevel(),
])->toArray();

return [
'type' => 'm.room.power_levels',
'content' => [
'users' => (object) $memberInfo,
'users_default' => $powerLevelDefault,
'events' => [
'm.room.name' => $powerLevelModerator,
'm.room.power_levels' => $powerLevelMaximum,
'm.room.history_visibility' => $powerLevelMaximum,
'm.room.canonical_alias' => $powerLevelModerator,
'm.room.avatar' => $powerLevelModerator,
'm.room.tombstone' => $powerLevelMaximum,
'm.room.server_acl' => $powerLevelMaximum,
'm.room.encryption' => $powerLevelMaximum,
],
'events_default' => $powerLevelDefault,
'state_default' => $powerLevelModerator,
'ban' => $powerLevelModerator,
'kick' => $powerLevelModerator,
'redact' => $powerLevelModerator,
'invite' => $powerLevelDefault,
'historical' => $powerLevelMaximum,
],
];
}
}
17 changes: 17 additions & 0 deletions application/src/Entity/RoomMember.php
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,11 @@ class RoomMember
*/
private $state;

/**
* @ORM\Column(type="integer", nullable=true)
*/
private $powerlevel;

public function getId(): ?int
{
return $this->id;
Expand Down Expand Up @@ -154,4 +159,16 @@ public function setState(string $state = null): self

return $this;
}

public function getPowerLevel(): ?int
{
return $this->powerlevel;
}

public function setPowerLevel(int $powerlevel): self
{
$this->powerlevel = $powerlevel;

return $this;
}
}
55 changes: 55 additions & 0 deletions application/src/Service/Filter.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
<?php

namespace App\Service;

use App\Service\Filter\RoomFilter;
use Symfony\Component\HttpFoundation\Request;


class Filter
{
private array $roomIds = [];
private RoomFilter $roomFilter;

public static function fromRequest(Request $request): static
{
$filter = new static();

$filterData = $request->query->get('filter');
if (!$filterData) {
return $filter;
}

// Filter the event data according to the API:
// https://spec.matrix.org/v1.1/client-server-api/#filtering
// Filters are made up of different filter types.
// Detailed here: https://spec.matrix.org/v1.1/client-server-api/#post_matrixclientv3useruseridfilter
$filterData = json_decode($request->query->get('filter'));

if (property_exists($filterData, 'room')) {
$filter->setRoomFilter(RoomFilter::fromObject($filterData->room));
}

return $filter;
}

public function setRoomFilter(RoomFilter $roomFilter): static
{
$this->roomFilter = $roomFilter;
return $this;
}

public function getRequestedRoomIds(): array
{
return $this->roomIds;
}

public function getRoomFilter(): RoomFilter
{
if (empty($this->roomFilter)) {
$this->roomFilter = new RoomFilter();
}

return $this->roomFilter;
}
}
49 changes: 49 additions & 0 deletions application/src/Service/RoomFilter.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
<?php

namespace App\Service\Filter;

class RoomFilter
{
private array $roomIds = [];
private StateFilter $roomStateFilter;

public static function fromObject(\stdClass $filterData): static
{
$filter = new static();

// Check for a list of rooms.
if (property_exists($filterData, 'rooms')) {
$rooms = (array) $filterData->rooms;
$filter->setRoomIds($rooms);
}

// Check for a room state filter.
if (property_exists($filterData, 'state')) {
$filter->setRoomState(StateFilter::fromObject($filterData->state));
}

return $filter;
}

public function setRoomIds(array $ids): static
{
$this->roomIds = $ids;
return $this;
}

public function setRoomState(StateFilter $state): static
{
$this->roomStateFilter = $state;
return $this;
}

public function getRoomIds(): array
{
return $this->roomIds;
}

public function getRoomState(): StateFilter
{
return $this->roomStateFilter;
}
}
32 changes: 32 additions & 0 deletions application/src/Service/StateFilter.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?php

namespace App\Service\Filter;

class StateFilter
{
private array $types = [];

public static function fromObject(\stdClass $filterData): static
{
$filter = new static();

// We currently only support types.
if (property_exists($filterData, 'types')) {
$types = (array) $filterData->types;
$filter->setTypes($types);
}

return $filter;
}

public function setTypes(array $types): static
{
$this->types = $types;
return $this;
}

public function getTypes(): array
{
return $this->types;
}
}

0 comments on commit 5254b2c

Please sign in to comment.