mirror of
https://github.com/element-hq/synapse.git
synced 2024-11-21 09:05:42 +03:00
Improve code formatting and fix a few typos in docs (#11221)
* Labeled a lot more code blocks with the appropriate type * Fixed a couple of minor typos (missing/extraneous commas) Signed-off-by: Sumner Evans <me@sumnerevans.com>
This commit is contained in:
parent
82d2168a15
commit
ece84f2c45
20 changed files with 229 additions and 164 deletions
1
changelog.d/11221.doc
Normal file
1
changelog.d/11221.doc
Normal file
|
@ -0,0 +1 @@
|
|||
Improve code formatting and fix a few typos in docs. Contributed by @sumnerevans at Beeper.
|
|
@ -15,12 +15,12 @@ in `homeserver.yaml`, to the list of authorized domains. If you have not set
|
|||
1. Agree to the terms of service and submit.
|
||||
1. Copy your site key and secret key and add them to your `homeserver.yaml`
|
||||
configuration file
|
||||
```
|
||||
```yaml
|
||||
recaptcha_public_key: YOUR_SITE_KEY
|
||||
recaptcha_private_key: YOUR_SECRET_KEY
|
||||
```
|
||||
1. Enable the CAPTCHA for new registrations
|
||||
```
|
||||
```yaml
|
||||
enable_registration_captcha: true
|
||||
```
|
||||
1. Go to the settings page for the CAPTCHA you just created
|
||||
|
|
|
@ -99,7 +99,7 @@ server admin: see [Admin API](../usage/administration/admin_api).
|
|||
|
||||
It returns a JSON body like the following:
|
||||
|
||||
```jsonc
|
||||
```json
|
||||
{
|
||||
"event_id": "$bNUFCwGzWca1meCGkjp-zwslF-GfVcXukvRLI1_FaVY",
|
||||
"event_json": {
|
||||
|
@ -132,7 +132,7 @@ It returns a JSON body like the following:
|
|||
},
|
||||
"type": "m.room.message",
|
||||
"unsigned": {
|
||||
"age_ts": 1592291711430,
|
||||
"age_ts": 1592291711430
|
||||
}
|
||||
},
|
||||
"id": <report_id>,
|
||||
|
|
|
@ -27,7 +27,7 @@ Room state data (such as joins, leaves, topic) is always preserved.
|
|||
|
||||
To delete local message events as well, set `delete_local_events` in the body:
|
||||
|
||||
```
|
||||
```json
|
||||
{
|
||||
"delete_local_events": true
|
||||
}
|
||||
|
|
|
@ -28,7 +28,7 @@ server admin: see [Admin API](../usage/administration/admin_api).
|
|||
|
||||
Response:
|
||||
|
||||
```
|
||||
```json
|
||||
{
|
||||
"room_id": "!636q39766251:server.com"
|
||||
}
|
||||
|
|
|
@ -87,7 +87,7 @@ GET /_synapse/admin/v1/rooms
|
|||
|
||||
A response body like the following is returned:
|
||||
|
||||
```jsonc
|
||||
```json
|
||||
{
|
||||
"rooms": [
|
||||
{
|
||||
|
@ -170,7 +170,7 @@ GET /_synapse/admin/v1/rooms?order_by=size
|
|||
|
||||
A response body like the following is returned:
|
||||
|
||||
```jsonc
|
||||
```json
|
||||
{
|
||||
"rooms": [
|
||||
{
|
||||
|
@ -208,7 +208,7 @@ A response body like the following is returned:
|
|||
}
|
||||
],
|
||||
"offset": 0,
|
||||
"total_rooms": 150
|
||||
"total_rooms": 150,
|
||||
"next_token": 100
|
||||
}
|
||||
```
|
||||
|
@ -224,7 +224,7 @@ GET /_synapse/admin/v1/rooms?order_by=size&from=100
|
|||
|
||||
A response body like the following is returned:
|
||||
|
||||
```jsonc
|
||||
```json
|
||||
{
|
||||
"rooms": [
|
||||
{
|
||||
|
|
|
@ -10,7 +10,9 @@ The necessary tools are detailed below.
|
|||
|
||||
First install them with:
|
||||
|
||||
pip install -e ".[lint,mypy]"
|
||||
```sh
|
||||
pip install -e ".[lint,mypy]"
|
||||
```
|
||||
|
||||
- **black**
|
||||
|
||||
|
@ -21,7 +23,9 @@ First install them with:
|
|||
Have `black` auto-format your code (it shouldn't change any
|
||||
functionality) with:
|
||||
|
||||
```sh
|
||||
black . --exclude="\.tox|build|env"
|
||||
```
|
||||
|
||||
- **flake8**
|
||||
|
||||
|
@ -30,7 +34,9 @@ First install them with:
|
|||
|
||||
Check all application and test code with:
|
||||
|
||||
```sh
|
||||
flake8 synapse tests
|
||||
```
|
||||
|
||||
- **isort**
|
||||
|
||||
|
@ -39,7 +45,9 @@ First install them with:
|
|||
|
||||
Auto-fix imports with:
|
||||
|
||||
```sh
|
||||
isort -rc synapse tests
|
||||
```
|
||||
|
||||
`-rc` means to recursively search the given directories.
|
||||
|
||||
|
@ -66,15 +74,19 @@ save as it takes a while and is very resource intensive.
|
|||
|
||||
Example:
|
||||
|
||||
```python
|
||||
from synapse.types import UserID
|
||||
...
|
||||
user_id = UserID(local, server)
|
||||
```
|
||||
|
||||
is preferred over:
|
||||
|
||||
```python
|
||||
from synapse import types
|
||||
...
|
||||
user_id = types.UserID(local, server)
|
||||
```
|
||||
|
||||
(or any other variant).
|
||||
|
||||
|
@ -134,21 +146,22 @@ Some guidelines follow:
|
|||
|
||||
Example:
|
||||
|
||||
## Frobnication ##
|
||||
```yaml
|
||||
## Frobnication ##
|
||||
|
||||
# The frobnicator will ensure that all requests are fully frobnicated.
|
||||
# To enable it, uncomment the following.
|
||||
#
|
||||
#frobnicator_enabled: true
|
||||
# The frobnicator will ensure that all requests are fully frobnicated.
|
||||
# To enable it, uncomment the following.
|
||||
#
|
||||
#frobnicator_enabled: true
|
||||
|
||||
# By default, the frobnicator will frobnicate with the default frobber.
|
||||
# The following will make it use an alternative frobber.
|
||||
#
|
||||
#frobincator_frobber: special_frobber
|
||||
# By default, the frobnicator will frobnicate with the default frobber.
|
||||
# The following will make it use an alternative frobber.
|
||||
#
|
||||
#frobincator_frobber: special_frobber
|
||||
|
||||
# Settings for the frobber
|
||||
#
|
||||
frobber:
|
||||
# Settings for the frobber
|
||||
#
|
||||
frobber:
|
||||
# frobbing speed. Defaults to 1.
|
||||
#
|
||||
#speed: 10
|
||||
|
@ -156,6 +169,7 @@ Example:
|
|||
# frobbing distance. Defaults to 1000.
|
||||
#
|
||||
#distance: 100
|
||||
```
|
||||
|
||||
Note that the sample configuration is generated from the synapse code
|
||||
and is maintained by a script, `scripts-dev/generate_sample_config`.
|
||||
|
|
|
@ -99,7 +99,7 @@ construct URIs where users can give their consent.
|
|||
see if an unauthenticated user is viewing the page. This is typically
|
||||
wrapped around the form that would be used to actually agree to the document:
|
||||
|
||||
```
|
||||
```html
|
||||
{% if not public_version %}
|
||||
<!-- The variables used here are only provided when the 'u' param is given to the homeserver -->
|
||||
<form method="post" action="consent">
|
||||
|
|
|
@ -8,23 +8,23 @@ easy to run CAS implementation built on top of Django.
|
|||
1. Create a new virtualenv: `python3 -m venv <your virtualenv>`
|
||||
2. Activate your virtualenv: `source /path/to/your/virtualenv/bin/activate`
|
||||
3. Install Django and django-mama-cas:
|
||||
```
|
||||
```sh
|
||||
python -m pip install "django<3" "django-mama-cas==2.4.0"
|
||||
```
|
||||
4. Create a Django project in the current directory:
|
||||
```
|
||||
```sh
|
||||
django-admin startproject cas_test .
|
||||
```
|
||||
5. Follow the [install directions](https://django-mama-cas.readthedocs.io/en/latest/installation.html#configuring) for django-mama-cas
|
||||
6. Setup the SQLite database: `python manage.py migrate`
|
||||
7. Create a user:
|
||||
```
|
||||
```sh
|
||||
python manage.py createsuperuser
|
||||
```
|
||||
1. Use whatever you want as the username and password.
|
||||
2. Leave the other fields blank.
|
||||
8. Use the built-in Django test server to serve the CAS endpoints on port 8000:
|
||||
```
|
||||
```sh
|
||||
python manage.py runserver
|
||||
```
|
||||
|
||||
|
|
|
@ -89,7 +89,9 @@ To do so, use `scripts-dev/make_full_schema.sh`. This will produce new
|
|||
|
||||
Ensure postgres is installed, then run:
|
||||
|
||||
./scripts-dev/make_full_schema.sh -p postgres_username -o output_dir/
|
||||
```sh
|
||||
./scripts-dev/make_full_schema.sh -p postgres_username -o output_dir/
|
||||
```
|
||||
|
||||
NB at the time of writing, this script predates the split into separate `state`/`main`
|
||||
databases so will require updates to handle that correctly.
|
||||
|
|
|
@ -69,7 +69,7 @@ A default policy can be defined as such, in the `retention` section of
|
|||
the configuration file:
|
||||
|
||||
```yaml
|
||||
default_policy:
|
||||
default_policy:
|
||||
min_lifetime: 1d
|
||||
max_lifetime: 1y
|
||||
```
|
||||
|
@ -95,7 +95,7 @@ depending on an event's room's policy. This can be done by setting the
|
|||
file. An example of such configuration could be:
|
||||
|
||||
```yaml
|
||||
purge_jobs:
|
||||
purge_jobs:
|
||||
- longest_max_lifetime: 3d
|
||||
interval: 12h
|
||||
- shortest_max_lifetime: 3d
|
||||
|
@ -141,8 +141,8 @@ purging old events in a room. These limits can be defined as such in the
|
|||
`retention` section of the configuration file:
|
||||
|
||||
```yaml
|
||||
allowed_lifetime_min: 1d
|
||||
allowed_lifetime_max: 1y
|
||||
allowed_lifetime_min: 1d
|
||||
allowed_lifetime_max: 1y
|
||||
```
|
||||
|
||||
The limits are considered when running purge jobs. If necessary, the
|
||||
|
|
|
@ -10,7 +10,7 @@ registered by using the Module API's `register_password_auth_provider_callbacks`
|
|||
|
||||
_First introduced in Synapse v1.46.0_
|
||||
|
||||
```
|
||||
```python
|
||||
auth_checkers: Dict[Tuple[str,Tuple], Callable]
|
||||
```
|
||||
|
||||
|
|
|
@ -29,16 +29,20 @@ connect to a postgres database.
|
|||
|
||||
Assuming your PostgreSQL database user is called `postgres`, first authenticate as the database user with:
|
||||
|
||||
su - postgres
|
||||
# Or, if your system uses sudo to get administrative rights
|
||||
sudo -u postgres bash
|
||||
```sh
|
||||
su - postgres
|
||||
# Or, if your system uses sudo to get administrative rights
|
||||
sudo -u postgres bash
|
||||
```
|
||||
|
||||
Then, create a postgres user and a database with:
|
||||
|
||||
# this will prompt for a password for the new user
|
||||
createuser --pwprompt synapse_user
|
||||
```sh
|
||||
# this will prompt for a password for the new user
|
||||
createuser --pwprompt synapse_user
|
||||
|
||||
createdb --encoding=UTF8 --locale=C --template=template0 --owner=synapse_user synapse
|
||||
createdb --encoding=UTF8 --locale=C --template=template0 --owner=synapse_user synapse
|
||||
```
|
||||
|
||||
The above will create a user called `synapse_user`, and a database called
|
||||
`synapse`.
|
||||
|
@ -145,20 +149,26 @@ Firstly, shut down the currently running synapse server and copy its
|
|||
database file (typically `homeserver.db`) to another location. Once the
|
||||
copy is complete, restart synapse. For instance:
|
||||
|
||||
./synctl stop
|
||||
cp homeserver.db homeserver.db.snapshot
|
||||
./synctl start
|
||||
```sh
|
||||
./synctl stop
|
||||
cp homeserver.db homeserver.db.snapshot
|
||||
./synctl start
|
||||
```
|
||||
|
||||
Copy the old config file into a new config file:
|
||||
|
||||
cp homeserver.yaml homeserver-postgres.yaml
|
||||
```sh
|
||||
cp homeserver.yaml homeserver-postgres.yaml
|
||||
```
|
||||
|
||||
Edit the database section as described in the section *Synapse config*
|
||||
above and with the SQLite snapshot located at `homeserver.db.snapshot`
|
||||
simply run:
|
||||
|
||||
synapse_port_db --sqlite-database homeserver.db.snapshot \
|
||||
```sh
|
||||
synapse_port_db --sqlite-database homeserver.db.snapshot \
|
||||
--postgres-config homeserver-postgres.yaml
|
||||
```
|
||||
|
||||
The flag `--curses` displays a coloured curses progress UI.
|
||||
|
||||
|
@ -170,16 +180,20 @@ To complete the conversion shut down the synapse server and run the port
|
|||
script one last time, e.g. if the SQLite database is at `homeserver.db`
|
||||
run:
|
||||
|
||||
synapse_port_db --sqlite-database homeserver.db \
|
||||
```sh
|
||||
synapse_port_db --sqlite-database homeserver.db \
|
||||
--postgres-config homeserver-postgres.yaml
|
||||
```
|
||||
|
||||
Once that has completed, change the synapse config to point at the
|
||||
PostgreSQL database configuration file `homeserver-postgres.yaml`:
|
||||
|
||||
./synctl stop
|
||||
mv homeserver.yaml homeserver-old-sqlite.yaml
|
||||
mv homeserver-postgres.yaml homeserver.yaml
|
||||
./synctl start
|
||||
```sh
|
||||
./synctl stop
|
||||
mv homeserver.yaml homeserver-old-sqlite.yaml
|
||||
mv homeserver-postgres.yaml homeserver.yaml
|
||||
./synctl start
|
||||
```
|
||||
|
||||
Synapse should now be running against PostgreSQL.
|
||||
|
||||
|
|
|
@ -52,7 +52,7 @@ to proxied traffic.)
|
|||
|
||||
### nginx
|
||||
|
||||
```
|
||||
```nginx
|
||||
server {
|
||||
listen 443 ssl http2;
|
||||
listen [::]:443 ssl http2;
|
||||
|
@ -141,7 +141,7 @@ matrix.example.com {
|
|||
|
||||
### Apache
|
||||
|
||||
```
|
||||
```apache
|
||||
<VirtualHost *:443>
|
||||
SSLEngine on
|
||||
ServerName matrix.example.com
|
||||
|
@ -170,7 +170,7 @@ matrix.example.com {
|
|||
|
||||
**NOTE 2**: It appears that Synapse is currently incompatible with the ModSecurity module for Apache (`mod_security2`). If you need it enabled for other services on your web server, you can disable it for Synapse's two VirtualHosts by including the following lines before each of the two `</VirtualHost>` above:
|
||||
|
||||
```
|
||||
```apache
|
||||
<IfModule security2_module>
|
||||
SecRuleEngine off
|
||||
</IfModule>
|
||||
|
|
|
@ -20,7 +20,9 @@ Finally, to actually run your worker-based synapse, you must pass synctl the `-a
|
|||
commandline option to tell it to operate on all the worker configurations found
|
||||
in the given directory, e.g.:
|
||||
|
||||
synctl -a $CONFIG/workers start
|
||||
```sh
|
||||
synctl -a $CONFIG/workers start
|
||||
```
|
||||
|
||||
Currently one should always restart all workers when restarting or upgrading
|
||||
synapse, unless you explicitly know it's safe not to. For instance, restarting
|
||||
|
@ -29,4 +31,6 @@ notifications.
|
|||
|
||||
To manipulate a specific worker, you pass the -w option to synctl:
|
||||
|
||||
synctl -w $CONFIG/workers/worker1.yaml restart
|
||||
```sh
|
||||
synctl -w $CONFIG/workers/worker1.yaml restart
|
||||
```
|
||||
|
|
|
@ -40,7 +40,9 @@ This will install and start a systemd service called `coturn`.
|
|||
|
||||
1. Configure it:
|
||||
|
||||
```sh
|
||||
./configure
|
||||
```
|
||||
|
||||
You may need to install `libevent2`: if so, you should do so in
|
||||
the way recommended by your operating system. You can ignore
|
||||
|
@ -49,22 +51,28 @@ This will install and start a systemd service called `coturn`.
|
|||
|
||||
1. Build and install it:
|
||||
|
||||
```sh
|
||||
make
|
||||
make install
|
||||
```
|
||||
|
||||
### Configuration
|
||||
|
||||
1. Create or edit the config file in `/etc/turnserver.conf`. The relevant
|
||||
lines, with example values, are:
|
||||
|
||||
```
|
||||
use-auth-secret
|
||||
static-auth-secret=[your secret key here]
|
||||
realm=turn.myserver.org
|
||||
```
|
||||
|
||||
See `turnserver.conf` for explanations of the options. One way to generate
|
||||
the `static-auth-secret` is with `pwgen`:
|
||||
|
||||
```sh
|
||||
pwgen -s 64 1
|
||||
```
|
||||
|
||||
A `realm` must be specified, but its value is somewhat arbitrary. (It is
|
||||
sent to clients as part of the authentication flow.) It is conventional to
|
||||
|
@ -73,7 +81,9 @@ This will install and start a systemd service called `coturn`.
|
|||
1. You will most likely want to configure coturn to write logs somewhere. The
|
||||
easiest way is normally to send them to the syslog:
|
||||
|
||||
```sh
|
||||
syslog
|
||||
```
|
||||
|
||||
(in which case, the logs will be available via `journalctl -u coturn` on a
|
||||
systemd system). Alternatively, coturn can be configured to write to a
|
||||
|
@ -83,6 +93,7 @@ This will install and start a systemd service called `coturn`.
|
|||
connect to arbitrary IP addresses and ports. The following configuration is
|
||||
suggested as a minimum starting point:
|
||||
|
||||
```
|
||||
# VoIP traffic is all UDP. There is no reason to let users connect to arbitrary TCP endpoints via the relay.
|
||||
no-tcp-relay
|
||||
|
||||
|
@ -98,16 +109,19 @@ This will install and start a systemd service called `coturn`.
|
|||
# consider whether you want to limit the quota of relayed streams per user (or total) to avoid risk of DoS.
|
||||
user-quota=12 # 4 streams per video call, so 12 streams = 3 simultaneous relayed calls per user.
|
||||
total-quota=1200
|
||||
```
|
||||
|
||||
1. Also consider supporting TLS/DTLS. To do this, add the following settings
|
||||
to `turnserver.conf`:
|
||||
|
||||
```
|
||||
# TLS certificates, including intermediate certs.
|
||||
# For Let's Encrypt certificates, use `fullchain.pem` here.
|
||||
cert=/path/to/fullchain.pem
|
||||
|
||||
# TLS private key file
|
||||
pkey=/path/to/privkey.pem
|
||||
```
|
||||
|
||||
In this case, replace the `turn:` schemes in the `turn_uri` settings below
|
||||
with `turns:`.
|
||||
|
@ -126,7 +140,9 @@ This will install and start a systemd service called `coturn`.
|
|||
If you want to try it anyway, you will at least need to tell coturn its
|
||||
external IP address:
|
||||
|
||||
```
|
||||
external-ip=192.88.99.1
|
||||
```
|
||||
|
||||
... and your NAT gateway must forward all of the relayed ports directly
|
||||
(eg, port 56789 on the external IP must be always be forwarded to port
|
||||
|
@ -186,7 +202,7 @@ After updating the homeserver configuration, you must restart synapse:
|
|||
./synctl restart
|
||||
```
|
||||
* If you use systemd:
|
||||
```
|
||||
```sh
|
||||
systemctl restart matrix-synapse.service
|
||||
```
|
||||
... and then reload any clients (or wait an hour for them to refresh their
|
||||
|
|
|
@ -1176,16 +1176,20 @@ For more information on configuring TLS certificates see the
|
|||
For users who have installed Synapse into a virtualenv, we recommend
|
||||
doing this by creating a new virtualenv. For example:
|
||||
|
||||
```sh
|
||||
virtualenv -p python3 ~/synapse/env3
|
||||
source ~/synapse/env3/bin/activate
|
||||
pip install matrix-synapse
|
||||
```
|
||||
|
||||
You can then start synapse as normal, having activated the new
|
||||
virtualenv:
|
||||
|
||||
```sh
|
||||
cd ~/synapse
|
||||
source env3/bin/activate
|
||||
synctl start
|
||||
```
|
||||
|
||||
Users who have installed from distribution packages should see the
|
||||
relevant package documentation. See below for notes on Debian
|
||||
|
@ -1197,6 +1201,7 @@ For more information on configuring TLS certificates see the
|
|||
`<server>.log.config` file. For example, if your `log.config`
|
||||
file contains:
|
||||
|
||||
```yaml
|
||||
handlers:
|
||||
file:
|
||||
class: logging.handlers.RotatingFileHandler
|
||||
|
@ -1209,9 +1214,11 @@ For more information on configuring TLS certificates see the
|
|||
class: logging.StreamHandler
|
||||
formatter: precise
|
||||
filters: [context]
|
||||
```
|
||||
|
||||
Then you should update this to be:
|
||||
|
||||
```yaml
|
||||
handlers:
|
||||
file:
|
||||
class: logging.handlers.RotatingFileHandler
|
||||
|
@ -1225,6 +1232,7 @@ For more information on configuring TLS certificates see the
|
|||
class: logging.StreamHandler
|
||||
formatter: precise
|
||||
filters: [context]
|
||||
```
|
||||
|
||||
There is no need to revert this change if downgrading to
|
||||
Python 2.
|
||||
|
@ -1310,15 +1318,18 @@ with the HS remotely has been removed.
|
|||
It has been replaced by specifying a list of application service
|
||||
registrations in `homeserver.yaml`:
|
||||
|
||||
app_service_config_files: ["registration-01.yaml", "registration-02.yaml"]
|
||||
```yaml
|
||||
app_service_config_files: ["registration-01.yaml", "registration-02.yaml"]
|
||||
```
|
||||
|
||||
Where `registration-01.yaml` looks like:
|
||||
|
||||
url: <String> # e.g. "https://my.application.service.com"
|
||||
as_token: <String>
|
||||
hs_token: <String>
|
||||
sender_localpart: <String> # This is a new field which denotes the user_id localpart when using the AS token
|
||||
namespaces:
|
||||
```yaml
|
||||
url: <String> # e.g. "https://my.application.service.com"
|
||||
as_token: <String>
|
||||
hs_token: <String>
|
||||
sender_localpart: <String> # This is a new field which denotes the user_id localpart when using the AS token
|
||||
namespaces:
|
||||
users:
|
||||
- exclusive: <Boolean>
|
||||
regex: <String> # e.g. "@prefix_.*"
|
||||
|
@ -1328,6 +1339,7 @@ Where `registration-01.yaml` looks like:
|
|||
rooms:
|
||||
- exclusive: <Boolean>
|
||||
regex: <String>
|
||||
```
|
||||
|
||||
# Upgrading to v0.8.0
|
||||
|
||||
|
|
|
@ -443,7 +443,7 @@ In the `media_repository` worker configuration file, configure the http listener
|
|||
expose the `media` resource. For example:
|
||||
|
||||
```yaml
|
||||
worker_listeners:
|
||||
worker_listeners:
|
||||
- type: http
|
||||
port: 8085
|
||||
resources:
|
||||
|
@ -455,7 +455,7 @@ Note that if running multiple media repositories they must be on the same server
|
|||
and you must configure a single instance to run the background tasks, e.g.:
|
||||
|
||||
```yaml
|
||||
media_instance_running_background_jobs: "media-repository-1"
|
||||
media_instance_running_background_jobs: "media-repository-1"
|
||||
```
|
||||
|
||||
Note that if a reverse proxy is used , then `/_matrix/media/` must be routed for both inbound client and federation requests (if they are handled separately).
|
||||
|
@ -492,7 +492,9 @@ must therefore be configured with the location of the main instance, via
|
|||
the `worker_main_http_uri` setting in the `frontend_proxy` worker configuration
|
||||
file. For example:
|
||||
|
||||
worker_main_http_uri: http://127.0.0.1:8008
|
||||
```yaml
|
||||
worker_main_http_uri: http://127.0.0.1:8008
|
||||
```
|
||||
|
||||
### Historical apps
|
||||
|
||||
|
|
Loading…
Reference in a new issue