Skip to content
Snippets Groups Projects
Verified Commit 9bdf1995 authored by Jonathan Weth's avatar Jonathan Weth :keyboard:
Browse files

Add first working group <-> room sync

parent 21b55a89
No related branches found
No related tags found
No related merge requests found
Pipeline #47950 failed
Showing
with 701 additions and 71 deletions
# All passwords are "admin"
- model: auth.user
pk: 2
fields:
password: pbkdf2_sha256$260000$FaCoGiW0LEmDlf5NozIIPw$FMtYfHkuRzOXXnD6xEnlJUa0j1HEwI1OpQcH5qkk+LE=
username: test1
is_active: true
- model: auth.user
pk: 3
fields:
password: pbkdf2_sha256$260000$FaCoGiW0LEmDlf5NozIIPw$FMtYfHkuRzOXXnD6xEnlJUa0j1HEwI1OpQcH5qkk+LE=
username: test2
is_active: true
- model: auth.user
pk: 4
fields:
password: pbkdf2_sha256$260000$FaCoGiW0LEmDlf5NozIIPw$FMtYfHkuRzOXXnD6xEnlJUa0j1HEwI1OpQcH5qkk+LE=
username: test3
is_active: true
- model: auth.user
pk: 5
fields:
password: pbkdf2_sha256$260000$FaCoGiW0LEmDlf5NozIIPw$FMtYfHkuRzOXXnD6xEnlJUa0j1HEwI1OpQcH5qkk+LE=
username: test4
is_active: true
- model: auth.user
pk: 6
fields:
password: pbkdf2_sha256$260000$FaCoGiW0LEmDlf5NozIIPw$FMtYfHkuRzOXXnD6xEnlJUa0j1HEwI1OpQcH5qkk+LE=
username: test5
is_active: true
- model: auth.user
pk: 7
fields:
password: pbkdf2_sha256$260000$FaCoGiW0LEmDlf5NozIIPw$FMtYfHkuRzOXXnD6xEnlJUa0j1HEwI1OpQcH5qkk+LE=
username: test6
is_active: true
- model: auth.user
pk: 8
fields:
password: pbkdf2_sha256$260000$FaCoGiW0LEmDlf5NozIIPw$FMtYfHkuRzOXXnD6xEnlJUa0j1HEwI1OpQcH5qkk+LE=
username: test7
is_active: true
- model: sites.site
pk: 1
fields:
domain: example.org
name: example.org
- model: core.person
pk: 2
fields:
site: 1
user: 2
is_active: true
first_name: Jane
last_name: Doe
short_name: DOE
email: jane.doe@example.com
- model: core.person
pk: 3
fields:
site: 1
user: 3
is_active: true
first_name: Aunt
last_name: Dawn
short_name: DAW
email: aunt.dawn@example.com
- model: core.person
pk: 4
fields:
site: 1
user: 4
is_active: true
first_name: Mad
last_name: Wilson
short_name: WIL
email: mad.wilson@example.com
- model: core.person
pk: 5
fields:
site: 1
user: 5
is_active: true
first_name: Lara
last_name: God
short_name: GOD
email: lara.god@example.com
- model: core.person
pk: 6
fields:
site: 1
user: 6
is_active: true
first_name: Student
last_name: A
email: student.a@example.com
- model: core.person
pk: 7
fields:
site: 1
user: 7
is_active: true
first_name: Student
last_name: B
email: student.b@example.com
- model: core.person
pk: 8
fields:
site: 1
user: 8
is_active: true
first_name: Student
last_name: C
email: student.C@example.com
# Classes: PK 100 and above
- model: core.group
pk: 101
fields:
site: 1
name: "8c"
short_name: "8c"
members: [ 6, 7, 8 ]
- model: core.group
pk: 102
fields:
site: 1
name: "5c"
short_name: "5c"
members: [ 6, 7, 8 ]
- model: core.group
pk: 103
fields:
site: 1
name: "6c"
short_name: "6c"
members: [ 6, 7, 8 ]
- model: core.group
pk: 104
fields:
site: 1
name: "5a"
short_name: "5a"
members: [ 6, 7, 8 ]
- model: core.group
pk: 105
fields:
site: 1
name: "7a"
short_name: "7a"
members: [ 6, 7, 8 ]
- model: core.group
pk: 106
fields:
site: 1
name: "6a"
short_name: "6a"
members: [ 6, 7, 8 ]
- model: core.group
pk: 107
fields:
site: 1
name: "9d"
short_name: "9d"
members: [ 6, 7, 8 ]
- model: core.group
pk: 1
fields:
site: 1
name: "8c:Mu"
short_name: "8c:Mu"
members: [ 6, 7, 8 ]
owners: [ 2 ]
parent_groups: [ 101 ]
- model: core.group
pk: 2
fields:
site: 1
name: "5c:Mu"
short_name: "5c:Mu"
members: [ 6, 7, 8 ]
owners: [ 2 ]
parent_groups: [ 102 ]
- model: core.group
pk: 3
fields:
site: 1
name: "6c:Mu"
short_name: "6c:Mu"
members: [ 6, 7, 8 ]
owners: [ 3 ]
parent_groups: [ 103 ]
- model: core.group
pk: 4
fields:
site: 1
name: "5a:De"
short_name: "5a:De"
members: [ 6, 7, 8 ]
owners: [ 4 ]
parent_groups: [ 104 ]
- model: core.group
pk: 5
fields:
site: 1
name: "7a:Mu"
short_name: "7a:Mu"
members: [ 6, 7, 8 ]
owners: [ 4 ]
parent_groups: [ 105 ]
- model: core.group
pk: 6
fields:
site: 1
name: "6a:Mu"
short_name: "6a:Mu"
members: [ 6, 7, 8 ]
owners: [ 5 ]
parent_groups: [ 106 ]
- model: core.group
pk: 7
fields:
site: 1
name: "9d:Mu"
short_name: "9d:Mu"
members: [ 6, 7, 8 ]
owners: [ 5 ]
parent_groups: [ 107 ]
- model: core.group
pk: 8
fields:
site: 1
name: "6a:De"
short_name: "6a:De"
members: [ 6, 7, 8 ]
owners: [ 5 ]
parent_groups: [ 106 ]
- model: core.group
pk: 9
fields:
site: 1
name: "Teachers"
short_name: "Teachers"
members: [ 2, 3, 4, 5 ]
- model: core.group
pk: 10
fields:
site: 1
name: "Students"
short_name: "Students"
members: [ 6, 7, 8 ]
from aleksis.core.filters import GroupFilter
class GroupMatrixRoomFilter(GroupFilter):
pass
from django.utils.translation import gettext as _
from aleksis.apps.matrix.tasks import use_groups_in_matrix
from aleksis.core.forms import ActionForm
def use_in_matrix_action(modeladmin, request, queryset):
"""Use selected groups in Matrix."""
use_groups_in_matrix.delay(list(queryset.values_list("pk", flat=True)))
use_in_matrix_action.short_description = _("Use in Matrix")
class GroupMatrixRoomActionForm(ActionForm):
def get_actions(self):
return [use_in_matrix_action]
import time
from json import JSONDecodeError
from typing import Any, Dict, Optional
from urllib.parse import urljoin
import requests
from aleksis.core.util.core_helpers import get_site_preferences
......@@ -17,3 +22,24 @@ def get_headers():
return {
"Authorization": "Bearer " + get_site_preferences()["matrix__access_token"],
}
def do_matrix_request(method: str, url: str, body: Optional[dict] = None) -> Dict[str, Any]:
"""Do a HTTP request to the Matrix Client Server API."""
while True:
res = requests.request(method=method, url=build_url(url), headers=get_headers(), json=body)
if res.status_code != requests.codes.ok:
try:
data = res.json()
except JSONDecodeError:
raise MatrixException(res.text)
# If rate limit exceeded, wait and retry
if data.get("errcode", "") == "M_LIMIT_EXCEEDED":
time.sleep(data["retry_after_ms"] / 1000)
else:
raise MatrixException(data)
else:
break
return res.json()
from django.utils.translation import gettext_lazy as _
MENUS = {
"NAV_MENU_CORE": [
{
"name": _("Matrix"),
"url": "#",
"icon": "chat",
"root": True,
"validators": [
(
"aleksis.core.util.predicates.permission_validator",
"matrix.show_menu_rule",
),
],
"submenu": [
{
"name": _("Groups and Rooms"),
"url": "matrix_rooms",
"icon": "group_work",
"validators": [
(
"aleksis.core.util.predicates.permission_validator",
"matrix.view_matrixrooms_rule",
),
],
},
],
}
]
}
......@@ -24,13 +24,14 @@ class Migration(migrations.Migration):
('extended_data', models.JSONField(default=dict, editable=False)),
('room_id', models.CharField(max_length=255, unique=True, verbose_name='Room ID')),
('alias', models.CharField(blank=True, max_length=255, unique=True, verbose_name='Alias')),
('group', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='matrix_spaces', to='core.group', verbose_name='Group')),
('group', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='matrix_room', to='core.group', verbose_name='Group')),
('polymorphic_ctype', models.ForeignKey(editable=False, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='polymorphic_matrix.matrixroom_set+', to='contenttypes.contenttype')),
('site', models.ForeignKey(default=1, editable=False, on_delete=django.db.models.deletion.CASCADE, to='sites.site')),
],
options={
'verbose_name': 'Matrix room',
'verbose_name_plural': 'Matrix rooms',
'permissions': (('use_group_in_matrix', 'Can use group in Matrix'),),
},
managers=[
('objects', aleksis.core.managers.PolymorphicCurrentSiteManager()),
......
from typing import Optional, Union
from django.utils.translation import gettext_lazy as _
from celery.result import AsyncResult
from aleksis.apps.matrix.models import MatrixRoom
from aleksis.apps.matrix.tasks import use_group_in_matrix
from aleksis.core.models import Group
@Group.method
def use_in_matrix(self, sync=False) -> Union[MatrixRoom, AsyncResult]:
"""Create and sync a room for this group in Matrix."""
if sync:
return self._use_in_matrix()
else:
return use_group_in_matrix.delay(self.pk)
@Group.method
def _use_in_matrix(self):
"""Create and sync a room for this group in Matrix."""
room = MatrixRoom.from_group(self)
room.sync()
return room
@Group.property_
def matrix_alias(self) -> Optional[str]:
"""Return the alias of the group's room in Matrix."""
if hasattr(self, "matrix_room"):
return self.matrix_room.alias
return None
@Group.property_
def matrix_room_id(self) -> Optional[str]:
"""Return the ID of the group's room in Matrix."""
if hasattr(self, "matrix_room"):
return self.matrix_room.room_id
return None
Group.add_permission("view_matrixroom", _("Can view matrix room of a group"))
import re
from typing import Any, Dict, Optional, Union
from typing import Any, Dict, List, Optional, Union
from django.db import models
from django.db.models import Q
from django.template.defaultfilters import slugify
from django.utils.translation import gettext_lazy as _
import requests
from aleksis.apps.matrix.matrix import do_matrix_request
from aleksis.core.mixins import ExtensibleModel, ExtensiblePolymorphicModel
from aleksis.core.models import Group, Person
from aleksis.core.util.core_helpers import get_site_preferences
......@@ -56,19 +55,17 @@ class MatrixRoom(ExtensiblePolymorphicModel):
room_id = models.CharField(max_length=255, verbose_name=_("Room ID"), unique=True)
alias = models.CharField(max_length=255, verbose_name=_("Alias"), unique=True, blank=True)
group = models.ForeignKey(
group = models.OneToOneField(
Group,
on_delete=models.CASCADE,
verbose_name=_("Group"),
null=True,
blank=True,
related_name="matrix_spaces",
related_name="matrix_room",
)
@classmethod
def from_group(self, group: Group):
"""Create a Matrix room from a group."""
from .matrix import MatrixException, build_url, get_headers
from .matrix import MatrixException, do_matrix_request
try:
room = MatrixRoom.objects.get(group=group)
......@@ -77,16 +74,27 @@ class MatrixRoom(ExtensiblePolymorphicModel):
if room.room_id:
# Existing room, check if still accessible
r = requests.get(
build_url(f"directory/list/room/{room.room_id}"), headers=get_headers()
)
if not r.status_code == requests.codes.ok:
raise MatrixException()
r = do_matrix_request("GET", f"directory/list/room/{room.room_id}")
else:
# Room does not exist, create it
alias = slugify(group.short_name or group.name)
r = self._create_group(group.name, alias)
while r.json().get("errcode") == "M_ROOM_IN_USE":
profiles_to_invite = list(
self.get_profiles_for_group(group).values_list("matrix_id", flat=True)
)
alias_found = False
while not alias_found:
try:
r = self._create_room(group.name, alias, profiles_to_invite)
alias_found = True
except MatrixException as e:
print(e.args, get_site_preferences()["matrix__disambiguate_room_aliases"])
if (
not get_site_preferences()["matrix__disambiguate_room_aliases"]
or e.args[0].get("errcode") != "M_ROOM_IN_USE"
):
raise MatrixException(*e.args)
match = re.match(r"^(.*)-(\d+)$", alias)
if match:
# Counter found, increase
......@@ -96,75 +104,72 @@ class MatrixRoom(ExtensiblePolymorphicModel):
else:
# Counter not found, add one
alias = f"{alias}-2"
r = self._create_group(group.name, alias)
if r.status_code == requests.codes.ok:
room.room_id = r.json()["room_id"]
room.alias = r.json()["room_alias"]
room.save()
else:
raise MatrixException(r.text)
room.room_id = r["room_id"]
room.alias = r["room_alias"]
room.save()
return room
@classmethod
def _create_group(self, name, alias):
from .matrix import build_url, get_headers
def _create_room(self, name, alias, invite: List[str]):
from .matrix import do_matrix_request
body = {"preset": "private_chat", "name": name, "room_alias_name": alias}
r = requests.post(build_url("createRoom"), headers=get_headers(), json=body)
body = {"preset": "private_chat", "name": name, "room_alias_name": alias, "invite": invite}
r = do_matrix_request("POST", "createRoom", body=body)
return r
@property
def power_levels(self) -> Dict[str, int]:
"""Return the power levels for this room."""
from aleksis.apps.matrix.matrix import MatrixException, build_url, get_headers
from aleksis.apps.matrix.matrix import do_matrix_request
r = requests.get(build_url(f"rooms/{self.room_id}/state"), headers=get_headers())
if r.status_code != requests.codes.ok:
raise MatrixException(r.text)
r = do_matrix_request("GET", f"rooms/{self.room_id}/state")
event = list(filter(lambda x: x["type"] == "m.room.power_levels", r.json()))
event = list(filter(lambda x: x["type"] == "m.room.power_levels", r))
user_levels = event[0]["content"]["users"]
return user_levels
@property
def members(self) -> List[str]:
from aleksis.apps.matrix.matrix import do_matrix_request
r = do_matrix_request(
"GET", f"rooms/{self.room_id}/members", body={"membership": ["join", "invite"]}
)
return [m["state_key"] for m in r["chunk"]]
def _invite(self, profile: MatrixProfile) -> Dict[str, Any]:
"""Invite a user to this room."""
from aleksis.apps.matrix.matrix import MatrixException, build_url, get_headers
from aleksis.apps.matrix.matrix import do_matrix_request
r = requests.post(
build_url(f"rooms/{self.room_id}/invite"),
headers=get_headers(),
json={"user_id": profile.matrix_id},
r = do_matrix_request(
"POST",
f"rooms/{self.room_id}/invite",
body={"user_id": profile.matrix_id},
)
if not r.status_code == requests.codes.ok:
raise MatrixException(r.text)
return r.json()
return r
def _set_power_levels(self, power_levels: Dict[str, int]) -> Dict[str, Any]:
"""Set the power levels for this room."""
from aleksis.apps.matrix.matrix import MatrixException, build_url, get_headers
r = requests.put(
build_url(f"rooms/{self.room_id}/state/m.room.power_levels/"),
headers=get_headers(),
json={"users": power_levels},
r = do_matrix_request(
"PUT",
f"rooms/{self.room_id}/state/m.room.power_levels/",
body={"users": power_levels},
)
print(r.text, r.status_code)
if not r.status_code == requests.codes.ok:
raise MatrixException(r.text)
return r.json()
return r
def sync_profiles(self):
"""Sync profiles for this room."""
@classmethod
def get_profiles_for_group(cls, group: Group):
"""Get all profile objects for the members/owners of a group."""
existing_profiles = MatrixProfile.objects.filter(
Q(person__member_of=self.group) | Q(person__owner_of=self.group)
Q(person__member_of=group) | Q(person__owner_of=group)
)
profiles_to_create = []
for person in (
Person.objects.filter(user__isnull=False)
.filter(Q(member_of=self.group) | Q(owner_of=self.group))
.filter(Q(member_of=group) | Q(owner_of=group))
.exclude(matrix_profile__in=existing_profiles)
.distinct()
):
......@@ -174,13 +179,23 @@ class MatrixRoom(ExtensiblePolymorphicModel):
MatrixProfile.objects.bulk_create(profiles_to_create)
all_profiles = MatrixProfile.objects.filter(
Q(person__in=self.group.members.all()) | Q(person__in=self.group.owners.all())
Q(person__in=group.members.all()) | Q(person__in=group.owners.all())
)
user_levels = self.power_levels
return all_profiles
def get_profiles(self):
"""Get all profile objects for the members/owners of this group."""
return self.get_profiles_for_group(self.group)
def sync_profiles(self):
"""Sync profiles for this room."""
all_profiles = self.get_profiles()
members = self.members
# Invite all users who are not in the room yet
for profile in all_profiles:
if profile.matrix_id not in user_levels:
if profile.matrix_id not in members:
# Now invite
self._invite(profile)
......@@ -195,9 +210,14 @@ class MatrixRoom(ExtensiblePolymorphicModel):
user_levels[profile.matrix_id] = 0
self._set_power_levels(user_levels)
def sync(self):
"""Sync this room."""
self.sync_profiles()
class Meta:
verbose_name = _("Matrix room")
verbose_name_plural = _("Matrix rooms")
permissions = (("use_group_in_matrix", "Can use group in Matrix"),)
class MatrixSpace(MatrixRoom):
......
......@@ -47,7 +47,6 @@ class DeviceID(StringPreference):
name = "device_id"
verbose_name = _("Device ID")
default = ""
field_kwargs = {"editable": False}
@site_preferences_registry.register
......
# View groups
import rules
from aleksis.core.models import Group
from aleksis.core.rules import view_group_predicate, view_groups_predicate
from aleksis.core.util.predicates import has_any_object, has_global_perm, has_object_perm
view_matrix_rooms_predicate = view_groups_predicate & (
has_global_perm("matrix.view_matrixroom") | has_any_object("core.view_matrixroom", Group)
)
rules.add_perm("matrix.view_matrixrooms_rule", view_matrix_rooms_predicate)
view_matrix_room_predicate = view_group_predicate & (
has_global_perm("matrix.view_matrixroom") | has_object_perm("core.view_matrixroom")
)
rules.add_perm("matrix.view_matrixroom_rule", view_matrix_room_predicate)
use_room_for_matrix_predicate = view_matrix_room_predicate & (
has_global_perm("matrix.use_group_in_matrix")
)
rules.add_perm("matrix.use_group_in_matrix_rule", use_room_for_matrix_predicate)
show_menu_predicate = view_matrix_rooms_predicate
rules.add_perm("matrix.show_menu_rule", show_menu_predicate)
from django_tables2 import Column, Table
from aleksis.core.util.tables import SelectColumn
class GroupsMatrixRoomsTable(Table):
"""Table to list groups together with their Matrix rooms."""
class Meta:
attrs = {"class": "highlight"}
selected = SelectColumn()
name = Column()
short_name = Column()
matrix_alias = Column()
matrix_room_id = Column()
from typing import Sequence
from aleksis.apps.matrix.models import MatrixRoom
from aleksis.core.celery import app
from aleksis.core.models import Group
@app.task
def sync_room(pk: int):
room = MatrixRoom.objects.get(pk=pk)
room.sync()
@app.task
def use_groups_in_matrix(pks: Sequence[int]):
groups = Group.objects.filter(pk__in=pks)
for group in groups:
group._use_in_matrix()
@app.task
def use_group_in_matrix(pk: int):
group = Group.objects.get(pk=pk)
group._use_in_matrix()
{# -*- engine:django -*- #}
{% extends "core/base.html" %}
{% load i18n material_form static %}
{% load render_table from django_tables2 %}
{% block browser_title %}{% blocktrans %}Groups and Matrix Rooms{% endblocktrans %}{% endblock %}
{% block page_title %}{% blocktrans %}Groups and Matrix Rooms{% endblocktrans %}{% endblock %}
{% block content %}
<!-- <a class="btn green waves-effect waves-light" href="{% url 'create_group' %}">-->
<!-- <i class="material-icons left">add</i>-->
<!-- {% trans "Create group" %}-->
<!-- </a>-->
<div class="card">
<div class="card-content">
<div class="card-title">{% trans "Filter groups" %}</div>
<form method="get">
{% form form=filter.form %}{% endform %}
{% trans "Search" as caption %}
{% include "core/partials/save_button.html" with caption=caption icon="search" %}
<button type="reset" class="btn red waves-effect waves-light">
<i class="material-icons left">clear</i>
{% trans "Clear" %}
</button>
</form>
</div>
</div>
<div class="card">
<div class="card-content">
<form action="" method="post">
{% csrf_token %}
<div class="row">
<div class="col s12 {% if action_form %}m4 l4 xl6{% endif %}">
<div class="card-title">{% trans "Selected groups" %}</div>
</div>
{% if action_form %}
<div class="col s12 m8 l8 xl6">
<div class="col s12 m8">
{% form form=action_form %}{% endform %}
</div>
<div class="col s12 m4">
<button type="submit" class="btn waves-effect waves-primary">
{% trans "Execute" %}
<i class="material-icons right">send</i>
</button>
</div>
</div>
{% endif %}
</div>
{% render_table table %}
</form>
</div>
</div>
<script src="{% static "js/multi_select.js" %}"></script>
{% endblock %}
import time
from datetime import date
from django.contrib.auth.models import User
import pytest
import requests
from celery.result import AsyncResult
from aleksis.apps.matrix.matrix import do_matrix_request
from aleksis.apps.matrix.models import MatrixProfile, MatrixRoom
from aleksis.core.models import Group, Person, SchoolTerm
from aleksis.core.util.core_helpers import get_site_preferences
......@@ -40,7 +43,12 @@ def matrix_bot_user(synapse):
get_site_preferences()["matrix__device_id"] = user["device_id"]
get_site_preferences()["matrix__access_token"] = user["access_token"]
yield r.json()
yield user
def test_matrix_bot_user(matrix_bot_user):
print(matrix_bot_user)
assert True
def test_create_room_for_group(matrix_bot_user):
......@@ -56,8 +64,8 @@ def test_create_room_for_group(matrix_bot_user):
# On second get, it should be the same matrix room
assert MatrixRoom.from_group(g) == room
r = requests.get(build_url(f"rooms/{room.room_id}/aliases"), headers=get_headers())
aliases = r.json()["aliases"]
r = do_matrix_request("GET", f"rooms/{room.room_id}/aliases")
aliases = r["aliases"]
assert "#test-room:matrix.aleksis.example.org" in aliases
......@@ -77,6 +85,9 @@ def test_room_alias_collision_same_name(matrix_bot_user):
g2 = Group.objects.create(name="test-room")
g3 = Group.objects.create(name="Test-Room")
g4 = Group.objects.create(name="test room")
get_site_preferences()["matrix__disambiguate_room_aliases"] = True
room = MatrixRoom.from_group(g1)
assert room.alias == "#test-room:matrix.aleksis.example.org"
......@@ -93,6 +104,8 @@ def test_room_alias_collision_same_name(matrix_bot_user):
def test_room_alias_collision_school_term(matrix_bot_user):
get_site_preferences()["matrix__disambiguate_room_aliases"] = True
school_term_a = SchoolTerm.objects.create(
name="Test Term A", date_start=date(2020, 1, 1), date_end=date(2020, 12, 31)
)
......@@ -146,14 +159,13 @@ def test_sync_room_members(matrix_bot_user):
assert p5.matrix_profile.matrix_id == "@test5:matrix.aleksis.example.org"
# Check members
r = requests.get(
build_url(f"rooms/{room.room_id}/members"),
headers=get_headers(),
json={"membership": ["join", "invite"]},
r = do_matrix_request(
"GET",
f"rooms/{room.room_id}/members",
body={"membership": ["join", "invite"]},
)
assert r.status_code == requests.codes.ok
matrix_ids = [x["state_key"] for x in r.json()["chunk"]]
matrix_ids = [x["state_key"] for x in r["chunk"]]
assert p1.matrix_profile.matrix_id in matrix_ids
assert p2.matrix_profile.matrix_id in matrix_ids
assert p3.matrix_profile.matrix_id in matrix_ids
......@@ -161,9 +173,8 @@ def test_sync_room_members(matrix_bot_user):
assert p5.matrix_profile.matrix_id in matrix_ids
# Get power levels
r = requests.get(build_url(f"rooms/{room.room_id}/state"), headers=get_headers())
assert r.status_code == requests.codes.ok
for event in r.json():
r = do_matrix_request("GET", f"rooms/{room.room_id}/state")
for event in r:
if not event["type"] == "m.room.power_levels":
continue
current_power_levels = event["content"]["users"]
......@@ -221,3 +232,53 @@ def test_sync_room_members_without_homeserver(matrix_bot_user):
assert not hasattr(p1, "matrix_profile")
assert p2.matrix_profile
assert p2.matrix_profile.matrix_id == "@test2:matrix.aleksis.example.org"
def test_use_room_sync(matrix_bot_user):
from aleksis.apps.matrix.matrix import build_url, get_headers
get_site_preferences()["matrix__homeserver_ids"] = "matrix.aleksis.example.org"
g = Group.objects.create(name="Test Room")
u1 = User.objects.create_user("test1", "test1@example.org", "test1")
p1 = Person.objects.create(first_name="Test", last_name="Person", user=u1)
g.members.add(p1)
r = g.use_in_matrix(sync=True)
assert isinstance(r, MatrixRoom)
assert MatrixProfile.objects.all().count() == 1
assert p1.matrix_profile
assert p1.matrix_profile.matrix_id == "@test1:matrix.aleksis.example.org"
from django.test import TransactionTestCase, override_settings
@pytest.mark.usefixtures("celery_worker", "matrix_bot_user")
@override_settings(CELERY_BROKER_URL="memory://localhost//")
@override_settings(HAYSTACK_SIGNAL_PROCESSOR="")
class MatrixCeleryTest(TransactionTestCase):
serialized_rollback = True
def test_use_room_async(self):
get_site_preferences()["matrix__homeserver_ids"] = "matrix.aleksis.example.org"
g = Group.objects.create(name="Test Room")
u1 = User.objects.create_user("test1", "test1@example.org", "test1")
p1 = Person.objects.create(first_name="Test", last_name="Person", user=u1)
g.members.add(p1)
r = g.use_in_matrix(sync=False)
assert isinstance(r, AsyncResult)
time.sleep(3)
assert MatrixProfile.objects.all().count() == 1
assert p1.matrix_profile
assert p1.matrix_profile.matrix_id == "@test1:matrix.aleksis.example.org"
from django.urls import path
from . import views
urlpatterns = [
path("rooms/", views.MatrixRoomListView.as_view(), name="matrix_rooms"),
]
from django_filters.views import FilterView
from django_tables2 import SingleTableMixin
from guardian.shortcuts import get_objects_for_user
from rules.contrib.views import PermissionRequiredMixin
from aleksis.apps.matrix.filters import GroupMatrixRoomFilter
from aleksis.apps.matrix.forms import GroupMatrixRoomActionForm
from aleksis.apps.matrix.tables import GroupsMatrixRoomsTable
from aleksis.core.models import Group
class MatrixRoomListView(PermissionRequiredMixin, SingleTableMixin, FilterView):
model = Group
template_name = "matrix/room/list.html"
permission_required = "matrix.view_matrixrooms_rule"
table_class = GroupsMatrixRoomsTable
filterset_class = GroupMatrixRoomFilter
def get_queryset(self):
return get_objects_for_user(
self.request.user, ["core.view_group", "core.view_matrixroom"], Group
)
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
self.action_form = GroupMatrixRoomActionForm(
self.request, self.request.POST or None, queryset=self.get_queryset()
)
context["action_form"] = self.action_form
return context
def post(self, request, *args, **kwargs):
r = super().get(request, *args, **kwargs)
if self.action_form.is_valid() and request.user.has_perm("matrix.use_group_in_matrix_rule"):
self.action_form.execute()
return r
......@@ -4,6 +4,8 @@ import pytest
import yaml
from xprocess import ProcessStarter
pytest_plugins = ("celery.contrib.pytest",)
@pytest.fixture
def synapse(xprocess, tmp_path):
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment