Merge branch 'master' into rav/is_token_grammar
This commit is contained in:
commit
a544b68298
27 changed files with 706 additions and 91 deletions
79
api/client-server/read_markers.yaml
Normal file
79
api/client-server/read_markers.yaml
Normal file
|
@ -0,0 +1,79 @@
|
||||||
|
# Copyright 2018 New Vector Ltd
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
swagger: '2.0'
|
||||||
|
info:
|
||||||
|
title: "Matrix Client-Server Read Marker API"
|
||||||
|
version: "1.0.0"
|
||||||
|
host: localhost:8008
|
||||||
|
schemes:
|
||||||
|
- https
|
||||||
|
- http
|
||||||
|
basePath: /_matrix/client/%CLIENT_MAJOR_VERSION%
|
||||||
|
consumes:
|
||||||
|
- application/json
|
||||||
|
produces:
|
||||||
|
- application/json
|
||||||
|
securityDefinitions:
|
||||||
|
$ref: definitions/security.yaml
|
||||||
|
paths:
|
||||||
|
"/rooms/{roomId}/read_markers":
|
||||||
|
post:
|
||||||
|
summary: Set the position of the read marker for a room.
|
||||||
|
description: |-
|
||||||
|
Sets the position of the read marker for a given room, and optionally
|
||||||
|
the read receipt's location.
|
||||||
|
operationId: setReadMarker
|
||||||
|
security:
|
||||||
|
- accessToken: []
|
||||||
|
parameters:
|
||||||
|
- in: path
|
||||||
|
type: string
|
||||||
|
name: roomId
|
||||||
|
description: The room ID to set the read marker in for the user.
|
||||||
|
required: true
|
||||||
|
x-example: "!somewhere:domain.com"
|
||||||
|
- in: body
|
||||||
|
name: body
|
||||||
|
description: The read marker and optional read receipt locations.
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
"m.fully_read":
|
||||||
|
type: string
|
||||||
|
description: |-
|
||||||
|
The event ID the read marker should be located at. The
|
||||||
|
event MUST belong to the room.
|
||||||
|
example: "$somewhere:domain.com"
|
||||||
|
"m.read":
|
||||||
|
type: string
|
||||||
|
description: |-
|
||||||
|
The event ID to set the read receipt location at. This is
|
||||||
|
equivalent to calling ``/receipt/m.read/$elsewhere:domain.com``
|
||||||
|
and is provided here to save that extra call.
|
||||||
|
example: "$elsewhere:domain.com"
|
||||||
|
required: ['m.fully_read']
|
||||||
|
responses:
|
||||||
|
200:
|
||||||
|
description: |-
|
||||||
|
The read marker, and read receipt if provided, have been updated.
|
||||||
|
schema:
|
||||||
|
type: object
|
||||||
|
properties: {}
|
||||||
|
429:
|
||||||
|
description: This request was rate-limited.
|
||||||
|
schema:
|
||||||
|
"$ref": "definitions/errors/rate_limited.yaml"
|
||||||
|
tags:
|
||||||
|
- Read Markers
|
1
changelogs/client_server/newsfragments/1420.feature
Normal file
1
changelogs/client_server/newsfragments/1420.feature
Normal file
|
@ -0,0 +1 @@
|
||||||
|
Encrypt file attachments
|
1
changelogs/client_server/newsfragments/1617.feature
Normal file
1
changelogs/client_server/newsfragments/1617.feature
Normal file
|
@ -0,0 +1 @@
|
||||||
|
Add "rich replies" - a way for users to better represent the conversation thread they are referencing in their messages.
|
1
changelogs/client_server/newsfragments/1635.feature
Normal file
1
changelogs/client_server/newsfragments/1635.feature
Normal file
|
@ -0,0 +1 @@
|
||||||
|
Add support for read markers.
|
1
changelogs/client_server/newsfragments/1635.new
Normal file
1
changelogs/client_server/newsfragments/1635.new
Normal file
|
@ -0,0 +1 @@
|
||||||
|
POST ``/rooms/{roomId}/read_markers``
|
|
@ -0,0 +1 @@
|
||||||
|
Clarify and generalise the language used for describing pagination.
|
8
event-schemas/examples/m.fully_read
Normal file
8
event-schemas/examples/m.fully_read
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
{
|
||||||
|
"$ref": "core/event.json",
|
||||||
|
"type": "m.fully_read",
|
||||||
|
"room_id": "!somewhere:domain.com",
|
||||||
|
"content": {
|
||||||
|
"event_id": "$someplace:domain.com"
|
||||||
|
}
|
||||||
|
}
|
|
@ -18,8 +18,16 @@ properties:
|
||||||
description: Size of the image in bytes.
|
description: Size of the image in bytes.
|
||||||
type: integer
|
type: integer
|
||||||
thumbnail_url:
|
thumbnail_url:
|
||||||
description: The URL to a thumbnail of the image.
|
description: |-
|
||||||
|
The URL to a thumbnail of the image. Only present if the
|
||||||
|
thumbnail is unencrypted.
|
||||||
type: string
|
type: string
|
||||||
|
thumbnail_file:
|
||||||
|
description: |-
|
||||||
|
Information on the encrypted thumbnail file, as specified in
|
||||||
|
|encrypted_files|_. Only present if the thumbnail is encrypted.
|
||||||
|
title: EncryptedFile
|
||||||
|
type: object
|
||||||
thumbnail_info:
|
thumbnail_info:
|
||||||
allOf:
|
allOf:
|
||||||
- $ref: thumbnail_info.yaml
|
- $ref: thumbnail_info.yaml
|
||||||
|
|
29
event-schemas/schema/m.fully_read
Normal file
29
event-schemas/schema/m.fully_read
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"title": "Read Marker Location Event",
|
||||||
|
"description": "The current location of the user's read marker in a room. This event appears in the user's room account data for the room the marker is applicable for.",
|
||||||
|
"allOf": [{
|
||||||
|
"$ref": "core-event-schema/event.yaml"
|
||||||
|
}],
|
||||||
|
"properties": {
|
||||||
|
"content": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"event_id": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The event the user's read marker is located at in the room."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["event_id"]
|
||||||
|
},
|
||||||
|
"type": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": ["m.fully_read"]
|
||||||
|
},
|
||||||
|
"room_id": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The room ID the read marker applies to."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["type", "room_id", "content"]
|
||||||
|
}
|
|
@ -27,12 +27,17 @@ properties:
|
||||||
- m.audio
|
- m.audio
|
||||||
type: string
|
type: string
|
||||||
url:
|
url:
|
||||||
description: The URL to the audio clip.
|
description: Required if the file is not encrypted. The URL to the audio clip.
|
||||||
type: string
|
type: string
|
||||||
|
file:
|
||||||
|
description: |-
|
||||||
|
Required if the file is encrypted. Information on the encrypted
|
||||||
|
file, as specified in |encrypted_files|_.
|
||||||
|
title: EncryptedFile
|
||||||
|
type: object
|
||||||
required:
|
required:
|
||||||
- msgtype
|
- msgtype
|
||||||
- body
|
- body
|
||||||
- url
|
|
||||||
type: object
|
type: object
|
||||||
type:
|
type:
|
||||||
enum:
|
enum:
|
||||||
|
|
|
@ -21,8 +21,16 @@ properties:
|
||||||
description: The size of the file in bytes.
|
description: The size of the file in bytes.
|
||||||
type: integer
|
type: integer
|
||||||
thumbnail_url:
|
thumbnail_url:
|
||||||
description: The URL to the thumbnail of the file.
|
description: |-
|
||||||
|
The URL to the thumbnail of the file. Only present if the
|
||||||
|
thumbnail is unencrypted.
|
||||||
type: string
|
type: string
|
||||||
|
thumbnail_file:
|
||||||
|
description: |-
|
||||||
|
Information on the encrypted thumbnail file, as specified in
|
||||||
|
|encrypted_files|_. Only present if the thumbnail is encrypted.
|
||||||
|
title: EncryptedFile
|
||||||
|
type: object
|
||||||
thumbnail_info:
|
thumbnail_info:
|
||||||
allOf:
|
allOf:
|
||||||
- $ref: core-event-schema/msgtype_infos/thumbnail_info.yaml
|
- $ref: core-event-schema/msgtype_infos/thumbnail_info.yaml
|
||||||
|
@ -34,12 +42,17 @@ properties:
|
||||||
- m.file
|
- m.file
|
||||||
type: string
|
type: string
|
||||||
url:
|
url:
|
||||||
description: The URL to the file.
|
description: Required if the file is unencrypted. The URL to the file.
|
||||||
type: string
|
type: string
|
||||||
|
file:
|
||||||
|
description: |-
|
||||||
|
Required if the file is encrypted. Information on the encrypted
|
||||||
|
file, as specified in |encrypted_files|_.
|
||||||
|
title: EncryptedFile
|
||||||
|
type: object
|
||||||
required:
|
required:
|
||||||
- msgtype
|
- msgtype
|
||||||
- body
|
- body
|
||||||
- url
|
|
||||||
- filename
|
- filename
|
||||||
type: object
|
type: object
|
||||||
type:
|
type:
|
||||||
|
|
|
@ -17,12 +17,17 @@ properties:
|
||||||
- m.image
|
- m.image
|
||||||
type: string
|
type: string
|
||||||
url:
|
url:
|
||||||
description: The URL to the image.
|
description: Required if the file is unencrypted. The URL to the image.
|
||||||
type: string
|
type: string
|
||||||
|
file:
|
||||||
|
description: |-
|
||||||
|
Required if the file is encrypted. Information on the encrypted
|
||||||
|
file, as specified in |encrypted_files|_.
|
||||||
|
title: EncryptedFile
|
||||||
|
type: object
|
||||||
required:
|
required:
|
||||||
- msgtype
|
- msgtype
|
||||||
- body
|
- body
|
||||||
- url
|
|
||||||
type: object
|
type: object
|
||||||
type:
|
type:
|
||||||
enum:
|
enum:
|
||||||
|
|
|
@ -19,8 +19,16 @@ properties:
|
||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
thumbnail_url:
|
thumbnail_url:
|
||||||
description: The URL to a thumbnail of the location being represented.
|
description: |-
|
||||||
|
The URL to the thumbnail of the file. Only present if the
|
||||||
|
thumbnail is unencrypted.
|
||||||
type: string
|
type: string
|
||||||
|
thumbnail_file:
|
||||||
|
description: |-
|
||||||
|
Information on the encrypted thumbnail file, as specified in
|
||||||
|
|encrypted_files|_. Only present if the thumbnail is encrypted.
|
||||||
|
title: EncryptedFile
|
||||||
|
type: object
|
||||||
thumbnail_info:
|
thumbnail_info:
|
||||||
allOf:
|
allOf:
|
||||||
- $ref: core-event-schema/msgtype_infos/thumbnail_info.yaml
|
- $ref: core-event-schema/msgtype_infos/thumbnail_info.yaml
|
||||||
|
|
|
@ -27,8 +27,16 @@ properties:
|
||||||
description: The size of the video in bytes.
|
description: The size of the video in bytes.
|
||||||
type: integer
|
type: integer
|
||||||
thumbnail_url:
|
thumbnail_url:
|
||||||
description: The URL to an image thumbnail of the video clip.
|
description: |-
|
||||||
|
The URL to an image thumbnail of the video clip. Only present if the
|
||||||
|
thumbnail is unencrypted.
|
||||||
type: string
|
type: string
|
||||||
|
thumbnail_file:
|
||||||
|
description: |-
|
||||||
|
Information on the encrypted thumbnail file, as specified in
|
||||||
|
|encrypted_files|_. Only present if the thumbnail is encrypted.
|
||||||
|
title: EncryptedFile
|
||||||
|
type: object
|
||||||
thumbnail_info:
|
thumbnail_info:
|
||||||
allOf:
|
allOf:
|
||||||
- $ref: core-event-schema/msgtype_infos/thumbnail_info.yaml
|
- $ref: core-event-schema/msgtype_infos/thumbnail_info.yaml
|
||||||
|
@ -40,12 +48,17 @@ properties:
|
||||||
- m.video
|
- m.video
|
||||||
type: string
|
type: string
|
||||||
url:
|
url:
|
||||||
description: The URL to the video clip.
|
description: Required if the file is unencrypted. The URL to the video clip.
|
||||||
type: string
|
type: string
|
||||||
|
file:
|
||||||
|
description: |-
|
||||||
|
Required if the file is encrypted. Information on the encrypted
|
||||||
|
file, as specified in |encrypted_files|_.
|
||||||
|
title: EncryptedFile
|
||||||
|
type: object
|
||||||
required:
|
required:
|
||||||
- msgtype
|
- msgtype
|
||||||
- body
|
- body
|
||||||
- url
|
|
||||||
type: object
|
type: object
|
||||||
type:
|
type:
|
||||||
enum:
|
enum:
|
||||||
|
|
|
@ -318,6 +318,46 @@ table.citation td {
|
||||||
border-bottom: none;
|
border-bottom: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
table.colwidths-auto caption {
|
||||||
|
font-family: 'Inconsolata', monospace;
|
||||||
|
font-weight: 800;
|
||||||
|
font-size: 120%;
|
||||||
|
padding: 5px;
|
||||||
|
text-align: left;
|
||||||
|
margin-bottom: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
ol, li {
|
||||||
|
margin: 0px 0px 0px 30px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
p.httpheaders {
|
||||||
|
font-weight: 800;
|
||||||
|
font-size: 120%;
|
||||||
|
padding: 5px;
|
||||||
|
text-align: left;
|
||||||
|
margin-bottom: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
table.colwidths-auto {
|
||||||
|
width:100%;
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
table.colwidths-auto tr td:nth-child(1) {
|
||||||
|
width: 15%;
|
||||||
|
}
|
||||||
|
|
||||||
|
table.colwidths-auto tr td:nth-child(2) {
|
||||||
|
width: 15%;
|
||||||
|
font-family: 'Inconsolata', monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
table.colwidths-auto tr td:nth-child(3) {
|
||||||
|
width: 70%;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* -- other body styles ----------------------------------------------------- */
|
/* -- other body styles ----------------------------------------------------- */
|
||||||
|
|
||||||
ol.arabic {
|
ol.arabic {
|
||||||
|
|
|
@ -273,6 +273,7 @@ table {
|
||||||
|
|
||||||
td[colspan]:not([colspan="1"]) {
|
td[colspan]:not([colspan="1"]) {
|
||||||
background: #eeeeee;
|
background: #eeeeee;
|
||||||
|
text-transform: capitalize;
|
||||||
}
|
}
|
||||||
|
|
||||||
thead {
|
thead {
|
||||||
|
|
|
@ -6,8 +6,7 @@
|
||||||
{{common_event.desc}}
|
{{common_event.desc}}
|
||||||
|
|
||||||
{% for table in common_event.tables %}
|
{% for table in common_event.tables %}
|
||||||
{{"``"+table.title+"``" if table.title else "" }}
|
|
||||||
|
|
||||||
{{ tables.paramtable(table.rows, ["Key", "Type", "Description"]) }}
|
{{ tables.paramtable(table.rows, ["Key", "Type", "Description"], (table.title or "")) }}
|
||||||
|
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
|
@ -5,16 +5,15 @@
|
||||||
|
|
||||||
{% if (event.typeof | length) %}
|
{% if (event.typeof | length) %}
|
||||||
*{{event.typeof}}*
|
*{{event.typeof}}*
|
||||||
{{event.typeof_info}}
|
{{event.typeof_info | indent_block(4)}}
|
||||||
|
|
||||||
{% endif -%}
|
{% endif -%}
|
||||||
|
|
||||||
{{event.desc}}
|
{{event.desc}}
|
||||||
|
|
||||||
{% for table in event.content_fields %}
|
{% for table in event.content_fields %}
|
||||||
{{"``"+table.title+"``" if table.title else "" }}
|
|
||||||
|
|
||||||
{{ tables.paramtable(table.rows, [(table.title or "Content") ~ " Key", "Type", "Description"]) }}
|
{{ tables.paramtable(table.rows, [(table.title or "Content") ~ " Key", "Type", "Description"], (table.title or "")) }}
|
||||||
|
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
Example{% if examples | length > 1 %}s{% endif %}:
|
Example{% if examples | length > 1 %}s{% endif %}:
|
||||||
|
|
|
@ -13,13 +13,16 @@
|
||||||
{{":Rate-limited: Yes." if endpoint.rate_limited else "" }}
|
{{":Rate-limited: Yes." if endpoint.rate_limited else "" }}
|
||||||
{{":Requires auth: Yes." if endpoint.requires_auth else "" }}
|
{{":Requires auth: Yes." if endpoint.requires_auth else "" }}
|
||||||
|
|
||||||
|
.. class:: httpheaders
|
||||||
|
|
||||||
Request format:
|
Request format:
|
||||||
|
|
||||||
{% if (endpoint.req_param_by_loc | length) %}
|
{% if (endpoint.req_param_by_loc | length) %}
|
||||||
{{ tables.split_paramtable(endpoint.req_param_by_loc) }}
|
{{ tables.split_paramtable(endpoint.req_param_by_loc) }}
|
||||||
{% if (endpoint.req_body_tables) %}
|
{% if (endpoint.req_body_tables) %}
|
||||||
{% for table in endpoint.req_body_tables -%}
|
{% for table in endpoint.req_body_tables -%}
|
||||||
{{"``"+table.title+"``" if table.title else "" }}
|
{{ tables.paramtable(table.rows, caption=(table.title or "")) }}
|
||||||
{{ tables.paramtable(table.rows) }}
|
|
||||||
{% endfor -%}
|
{% endfor -%}
|
||||||
{% endif -%}
|
{% endif -%}
|
||||||
|
|
||||||
|
@ -28,22 +31,30 @@ Request format:
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% if endpoint.res_headers is not none -%}
|
{% if endpoint.res_headers is not none -%}
|
||||||
|
|
||||||
|
.. class:: httpheaders
|
||||||
|
|
||||||
Response headers:
|
Response headers:
|
||||||
|
|
||||||
{{ tables.paramtable(endpoint.res_headers.rows) }}
|
{{ tables.paramtable(endpoint.res_headers.rows) }}
|
||||||
{% endif -%}
|
{% endif -%}
|
||||||
|
|
||||||
{% if endpoint.res_tables|length > 0 -%}
|
{% if endpoint.res_tables|length > 0 -%}
|
||||||
|
|
||||||
|
.. class:: httpheaders
|
||||||
|
|
||||||
Response format:
|
Response format:
|
||||||
|
|
||||||
{% for table in endpoint.res_tables -%}
|
{% for table in endpoint.res_tables -%}
|
||||||
{{"``"+table.title+"``" if table.title else "" }}
|
|
||||||
|
|
||||||
{{ tables.paramtable(table.rows) }}
|
{{ tables.paramtable(table.rows, caption=(table.title or "")) }}
|
||||||
|
|
||||||
|
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{% endif -%}
|
{% endif -%}
|
||||||
|
|
||||||
|
.. class:: httpheaders
|
||||||
|
|
||||||
Example request:
|
Example request:
|
||||||
|
|
||||||
.. code:: http
|
.. code:: http
|
||||||
|
@ -51,6 +62,9 @@ Example request:
|
||||||
{{endpoint.example.req | indent_block(2)}}
|
{{endpoint.example.req | indent_block(2)}}
|
||||||
|
|
||||||
{% if endpoint.responses|length > 0 -%}
|
{% if endpoint.responses|length > 0 -%}
|
||||||
|
|
||||||
|
.. class:: httpheaders
|
||||||
|
|
||||||
Response{{"s" if endpoint.responses|length > 1 else "" }}:
|
Response{{"s" if endpoint.responses|length > 1 else "" }}:
|
||||||
|
|
||||||
{% endif -%}
|
{% endif -%}
|
||||||
|
@ -63,6 +77,8 @@ Response{{"s" if endpoint.responses|length > 1 else "" }}:
|
||||||
|
|
||||||
{% if res["example"] -%}
|
{% if res["example"] -%}
|
||||||
|
|
||||||
|
.. class:: httpheaders
|
||||||
|
|
||||||
Example
|
Example
|
||||||
|
|
||||||
.. code:: json
|
.. code:: json
|
||||||
|
|
|
@ -4,9 +4,8 @@
|
||||||
{{(4 + event.msgtype | length) * title_kind}}
|
{{(4 + event.msgtype | length) * title_kind}}
|
||||||
{{event.desc | wrap(80)}}
|
{{event.desc | wrap(80)}}
|
||||||
{% for table in event.content_fields -%}
|
{% for table in event.content_fields -%}
|
||||||
{{"``"+table.title+"``" if table.title else "" }}
|
|
||||||
|
|
||||||
{{ tables.paramtable(table.rows, [(table.title or "Content") ~ " Key", "Type", "Description"]) }}
|
{{ tables.paramtable(table.rows, [(table.title or "Content") ~ " Key", "Type", "Description"], (table.title or "")) }}
|
||||||
|
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
Example:
|
Example:
|
||||||
|
|
|
@ -8,8 +8,8 @@
|
||||||
#
|
#
|
||||||
# 'rows' is the list of parameters. Each row should be a TypeTableRow.
|
# 'rows' is the list of parameters. Each row should be a TypeTableRow.
|
||||||
#}
|
#}
|
||||||
{% macro paramtable(rows, titles=["Parameter", "Type", "Description"]) -%}
|
{% macro paramtable(rows, titles=["Parameter", "Type", "Description"], caption="") -%}
|
||||||
{{ split_paramtable({None: rows}, titles) }}
|
{{ split_paramtable({None: rows}, titles, caption) }}
|
||||||
{% endmacro %}
|
{% endmacro %}
|
||||||
|
|
||||||
|
|
||||||
|
@ -21,7 +21,7 @@
|
||||||
# written for that location. This is used by the standard 'paramtable' macro.
|
# written for that location. This is used by the standard 'paramtable' macro.
|
||||||
#}
|
#}
|
||||||
{% macro split_paramtable(rows_by_loc,
|
{% macro split_paramtable(rows_by_loc,
|
||||||
titles=["Parameter", "Type", "Description"]) -%}
|
titles=["Parameter", "Type", "Description"], caption="") -%}
|
||||||
|
|
||||||
{% set rowkeys = ['key', 'title', 'desc'] %}
|
{% set rowkeys = ['key', 'title', 'desc'] %}
|
||||||
{% set titlerow = {'key': titles[0], 'title': titles[1], 'desc': titles[2]} %}
|
{% set titlerow = {'key': titles[0], 'title': titles[1], 'desc': titles[2]} %}
|
||||||
|
@ -36,6 +36,9 @@
|
||||||
{% set fieldwidths = (([titlerow] + flatrows) |
|
{% set fieldwidths = (([titlerow] + flatrows) |
|
||||||
fieldwidths(rowkeys[0:-1], [10, 10])) + [50] -%}
|
fieldwidths(rowkeys[0:-1], [10, 10])) + [50] -%}
|
||||||
|
|
||||||
|
{{".. table:: "}}{{ caption }}
|
||||||
|
{{" :widths: auto"}}
|
||||||
|
{{""}}
|
||||||
{{ tableheader(fieldwidths) }}
|
{{ tableheader(fieldwidths) }}
|
||||||
{{ tablerow(fieldwidths, titlerow, rowkeys) }}
|
{{ tablerow(fieldwidths, titlerow, rowkeys) }}
|
||||||
{{ tableheader(fieldwidths) }}
|
{{ tableheader(fieldwidths) }}
|
||||||
|
@ -59,7 +62,7 @@
|
||||||
# Write a table header row, for the given column widths
|
# Write a table header row, for the given column widths
|
||||||
#}
|
#}
|
||||||
{% macro tableheader(widths) -%}
|
{% macro tableheader(widths) -%}
|
||||||
{% for arg in widths -%}
|
{{" "}}{% for arg in widths -%}
|
||||||
{{"="*arg}} {% endfor -%}
|
{{"="*arg}} {% endfor -%}
|
||||||
{% endmacro %}
|
{% endmacro %}
|
||||||
|
|
||||||
|
@ -71,7 +74,7 @@
|
||||||
# attributes of 'row' to look up for values to put in the columns.
|
# attributes of 'row' to look up for values to put in the columns.
|
||||||
#}
|
#}
|
||||||
{% macro tablerow(widths, row, keys) -%}
|
{% macro tablerow(widths, row, keys) -%}
|
||||||
{% for key in keys -%}
|
{{" "}}{% for key in keys -%}
|
||||||
{% set value=row[key] -%}
|
{% set value=row[key] -%}
|
||||||
{% if not loop.last -%}
|
{% if not loop.last -%}
|
||||||
{# the first few columns need space after them -#}
|
{# the first few columns need space after them -#}
|
||||||
|
@ -81,7 +84,7 @@
|
||||||
the preceding columns, plus the number of preceding columns (for the
|
the preceding columns, plus the number of preceding columns (for the
|
||||||
separators)) -#}
|
separators)) -#}
|
||||||
{{ value | wrap(widths[loop.index0]) |
|
{{ value | wrap(widths[loop.index0]) |
|
||||||
indent_block(widths[0:-1]|sum + loop.index0) -}}
|
indent_block(widths[0:-1]|sum + loop.index0 + 2) -}}
|
||||||
{% endif -%}
|
{% endif -%}
|
||||||
{% endfor -%}
|
{% endfor -%}
|
||||||
{% endmacro %}
|
{% endmacro %}
|
||||||
|
@ -93,10 +96,10 @@
|
||||||
# write a tablespan row. This is a single value which spans the entire table.
|
# write a tablespan row. This is a single value which spans the entire table.
|
||||||
#}
|
#}
|
||||||
{% macro tablespan(widths, value) -%}
|
{% macro tablespan(widths, value) -%}
|
||||||
{{value}}
|
{{" "}}{{value}}
|
||||||
{# we write a trailing space to stop the separator being misinterpreted
|
{# we write a trailing space to stop the separator being misinterpreted
|
||||||
# as a header line. -#}
|
# as a header line. -#}
|
||||||
{{"-"*(widths|sum + widths|length -1)}} {% endmacro %}
|
{{" "}}{{"-"*(widths|sum + widths|length -1)}} {% endmacro %}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -138,9 +138,6 @@ Some requests have unique error codes:
|
||||||
:``M_INVALID_ROOM_STATE``:
|
:``M_INVALID_ROOM_STATE``:
|
||||||
Sent when the intial state given to the ``createRoom`` API is invalid.
|
Sent when the intial state given to the ``createRoom`` API is invalid.
|
||||||
|
|
||||||
:``M_BAD_PAGINATION``:
|
|
||||||
Encountered when specifying bad pagination query parameters.
|
|
||||||
|
|
||||||
:``M_THREEPID_IN_USE``:
|
:``M_THREEPID_IN_USE``:
|
||||||
Sent when a threepid given to an API cannot be used because the same threepid is already in use.
|
Sent when a threepid given to an API cannot be used because the same threepid is already in use.
|
||||||
|
|
||||||
|
@ -1018,39 +1015,21 @@ Pagination is the process of dividing a dataset into multiple discrete pages.
|
||||||
Matrix makes use of pagination to allow clients to view extremely large datasets.
|
Matrix makes use of pagination to allow clients to view extremely large datasets.
|
||||||
These datasets are not limited to events in a room (for example clients may want
|
These datasets are not limited to events in a room (for example clients may want
|
||||||
to paginate a list of rooms in addition to events within those rooms). Regardless
|
to paginate a list of rooms in addition to events within those rooms). Regardless
|
||||||
of *what* is being paginated, there is a common underlying API which is used to
|
of what is being paginated, there is a common approach which is used to give
|
||||||
to give clients a consistent way of selecting subsets of a potentially changing
|
clients an easy way of selecting subsets of a potentially changing dataset. Each
|
||||||
dataset. Requests pass in ``from``, ``to``, ``dir`` and ``limit`` parameters
|
endpoint that uses pagination may use different parameters. However the theme
|
||||||
which describe where to read from the stream. ``from`` and ``to`` are opaque
|
among them is that they take a ``from`` and ``to`` token, and occasionally
|
||||||
textual 'stream tokens' which describe the current position in the dataset.
|
a ``limit`` and ``dir``. Together, these parameters describe the position in a
|
||||||
The ``dir`` parameter is an enum representing the direction of events to return:
|
data set, where ``from`` and ``to`` are known as "stream tokens" matching the
|
||||||
either ``f`` orwards or ``b`` ackwards. The response returns new ``start`` and
|
regular expression ``[a-zA-Z0-9.=_-]+``. If supported, the ``dir`` defines the
|
||||||
``end`` stream token values which can then be passed to subsequent requests to
|
direction of events to return: either forwards (``f``) or backwards (``b``).
|
||||||
continue pagination. Not all endpoints will make use of all the parameters
|
The response may contain tokens that can be used for retrieving results before
|
||||||
outlined here: see the specific endpoint in question for more information.
|
or after the returned set. These tokens may be called `start` or `prev_batch`
|
||||||
|
for retrieving the previous result set, or `end`, `next_batch` or `next_token`
|
||||||
|
for retrieving the next result set.
|
||||||
|
|
||||||
Pagination Request Query Parameters
|
In the following examples, 'START' and 'END' are placeholders to signify the
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
start and end of the data sets respectively.
|
||||||
|
|
||||||
Query parameters:
|
|
||||||
from:
|
|
||||||
$streamtoken - The opaque token to start streaming from.
|
|
||||||
to:
|
|
||||||
$streamtoken - The opaque token to end streaming at. Typically,
|
|
||||||
clients will not know the item of data to end at, so this will usually be
|
|
||||||
omitted.
|
|
||||||
limit:
|
|
||||||
integer - An integer representing the maximum number of items to
|
|
||||||
return.
|
|
||||||
dir:
|
|
||||||
f|b - The direction to return events in. Typically this is ``b`` to paginate
|
|
||||||
backwards in time.
|
|
||||||
|
|
||||||
'START' and 'END' are placeholder values used in these examples to describe the
|
|
||||||
start and end of the dataset respectively.
|
|
||||||
|
|
||||||
Unless specified, the default pagination parameters are ``from=START``,
|
|
||||||
``to=END``, without a limit set.
|
|
||||||
|
|
||||||
For example, if an endpoint had events E1 -> E15. The client wants the last 5
|
For example, if an endpoint had events E1 -> E15. The client wants the last 5
|
||||||
events and doesn't know any previous events::
|
events and doesn't know any previous events::
|
||||||
|
@ -1067,8 +1046,8 @@ events and doesn't know any previous events::
|
||||||
|
|
||||||
|
|
||||||
Another example: a public room list has rooms R1 -> R17. The client is showing 5
|
Another example: a public room list has rooms R1 -> R17. The client is showing 5
|
||||||
rooms at a time on screen, and is on page 2. They want to
|
rooms at a time on screen, and is on page 2. They want to now show page 3 (rooms
|
||||||
now show page 3 (rooms R11 -> 15)::
|
R11 -> 15)::
|
||||||
|
|
||||||
S E
|
S E
|
||||||
| 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | stream token
|
| 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | stream token
|
||||||
|
@ -1086,20 +1065,17 @@ token from the initial request was '9' which corresponded to R10. When the 2nd
|
||||||
request was made, R10 did not appear again, even though from=9 was specified. If
|
request was made, R10 did not appear again, even though from=9 was specified. If
|
||||||
you know the token, you already have the data.
|
you know the token, you already have the data.
|
||||||
|
|
||||||
Pagination Response
|
Responses for pagination-capable endpoints SHOULD have a ``chunk`` array alongside
|
||||||
~~~~~~~~~~~~~~~~~~~
|
the applicable stream tokens to represent the result set.
|
||||||
|
|
||||||
Responses to pagination requests MUST follow the format::
|
In general, when the end of a result set is reached the applicable stream token
|
||||||
|
will be excluded from the response. For example, if a user was backwards-paginating
|
||||||
{
|
events in a room they'd eventually reach the first event in the room. In this scenario,
|
||||||
"chunk": [ ... , Responses , ... ],
|
the ``prev_batch`` token would be excluded from the response. Some paginated
|
||||||
"start" : $streamtoken,
|
endpoints are open-ended in one direction, such as endpoints which expose an event
|
||||||
"end" : $streamtoken
|
stream for an active room. In this case, it is not possible for the client to reach
|
||||||
}
|
the true "end" of the data set and therefore should always be presented with a token
|
||||||
|
to keep moving forwards.
|
||||||
Where $streamtoken is an opaque token which can be used in another query to
|
|
||||||
get the next set of results. The "start" and "end" keys can only be omitted if
|
|
||||||
the complete dataset is provided in "chunk".
|
|
||||||
|
|
||||||
.. _`filter`:
|
.. _`filter`:
|
||||||
|
|
||||||
|
|
|
@ -39,3 +39,10 @@ Client Behaviour
|
||||||
----------------
|
----------------
|
||||||
|
|
||||||
{{account_data_cs_http_api}}
|
{{account_data_cs_http_api}}
|
||||||
|
|
||||||
|
|
||||||
|
Server Behaviour
|
||||||
|
----------------
|
||||||
|
|
||||||
|
Servers MUST reject clients from setting account data for event types that
|
||||||
|
the server manages. Currently, this only includes `m.fully_read`_.
|
||||||
|
|
|
@ -224,6 +224,145 @@ process:
|
||||||
.. |device_lists| replace:: ``device_lists``
|
.. |device_lists| replace:: ``device_lists``
|
||||||
.. _`device_lists`: `device_lists_sync`_
|
.. _`device_lists`: `device_lists_sync`_
|
||||||
|
|
||||||
|
|
||||||
|
Sending encrypted attachments
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
When encryption is enabled in a room, files should be uploaded encrypted on
|
||||||
|
the homeserver.
|
||||||
|
|
||||||
|
In order to achieve this, a client should generate a single-use 256-bit AES
|
||||||
|
key, and encrypt the file using AES-CTR. The counter should be 64-bit long,
|
||||||
|
starting at 0 and prefixed by a random 64-bit Initialization Vector (IV), which
|
||||||
|
together form a 128-bit unique counter block.
|
||||||
|
|
||||||
|
.. Warning::
|
||||||
|
An IV must never be used multiple times with the same key. This implies that
|
||||||
|
if there are multiple files to encrypt in the same message, typically an
|
||||||
|
image and its thumbnail, the files must not share both the same key and IV.
|
||||||
|
|
||||||
|
Then, the encrypted file can be uploaded to the homeserver.
|
||||||
|
The key and the IV must be included in the room event along with the resulting
|
||||||
|
``mxc://`` in order to allow recipients to decrypt the file. As the event
|
||||||
|
containing those will be Megolm encrypted, the server will never have access to
|
||||||
|
the decrypted file.
|
||||||
|
|
||||||
|
A hash of the ciphertext must also be included, in order to prevent the homeserver from
|
||||||
|
changing the file content.
|
||||||
|
|
||||||
|
A client should send the data as an encrypted ``m.room.message`` event, using
|
||||||
|
either ``m.file`` as the msgtype, or the appropriate msgtype for the file
|
||||||
|
type. The key is sent using the `JSON Web Key`_ format, with a `W3C
|
||||||
|
extension`_.
|
||||||
|
|
||||||
|
.. anchor for link from m.message api spec
|
||||||
|
.. |encrypted_files| replace:: End-to-end encryption
|
||||||
|
.. _encrypted_files:
|
||||||
|
|
||||||
|
Extensions to ``m.message`` msgtypes
|
||||||
|
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
|
||||||
|
|
||||||
|
This module adds ``file`` and ``thumbnail_file`` properties, of type
|
||||||
|
``EncryptedFile``, to ``m.message`` msgtypes that reference files, such as
|
||||||
|
`m.file`_ and `m.image`_, replacing the ``url`` and ``thumbnail_url``
|
||||||
|
properties.
|
||||||
|
|
||||||
|
.. todo: generate this from a swagger definition?
|
||||||
|
|
||||||
|
``EncryptedFile``
|
||||||
|
|
||||||
|
========= ================ =====================================================
|
||||||
|
Parameter Type Description
|
||||||
|
========= ================ =====================================================
|
||||||
|
url string **Required.** The URL to the file.
|
||||||
|
key JWK **Required.** A `JSON Web Key`_ object.
|
||||||
|
iv string **Required.** The Initialisation Vector used by
|
||||||
|
AES-CTR, encoded as unpadded base64.
|
||||||
|
hashes {string: string} **Required.** A map from an algorithm name to a hash
|
||||||
|
of the ciphertext, encoded as unpadded base64. Clients
|
||||||
|
should support the SHA-256 hash, which uses the key
|
||||||
|
``sha256``.
|
||||||
|
v string **Required.** Version of the encrypted attachments
|
||||||
|
protocol. Must be ``v2``.
|
||||||
|
========= ================ =====================================================
|
||||||
|
|
||||||
|
``JWK``
|
||||||
|
|
||||||
|
========= ========= ============================================================
|
||||||
|
Parameter Type Description
|
||||||
|
========= ========= ============================================================
|
||||||
|
key string **Required.** Key type. Must be ``oct``.
|
||||||
|
key_opts [string] **Required.** Key operations. Must at least contain
|
||||||
|
``encrypt`` and ``decrypt``.
|
||||||
|
alg string **Required.** Algorithm. Must be ``A256CTR``.
|
||||||
|
k string **Required.** The key, encoded as urlsafe unpadded base64.
|
||||||
|
ext boolean **Required.** Extractable. Must be ``true``. This is a
|
||||||
|
`W3C extension`_.
|
||||||
|
========= ========= ============================================================
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
.. code :: json
|
||||||
|
|
||||||
|
{
|
||||||
|
"content": {
|
||||||
|
"body": "something-important.jpg",
|
||||||
|
"file": {
|
||||||
|
"url": "mxc://domain.com/FHyPlCeYUSFFxlgbQYZmoEoe",
|
||||||
|
"mimetype": "image/jpeg",
|
||||||
|
"v": "v2",
|
||||||
|
"key": {
|
||||||
|
"alg": "A256CTR",
|
||||||
|
"ext": true,
|
||||||
|
"k": "aWF6-32KGYaC3A_FEUCk1Bt0JA37zP0wrStgmdCaW-0",
|
||||||
|
"key_ops": ["encrypt","decrypt"],
|
||||||
|
"kty": "oct"
|
||||||
|
},
|
||||||
|
"iv": "w+sE15fzSc0AAAAAAAAAAA",
|
||||||
|
"hashes": {
|
||||||
|
"sha256": "fdSLu/YkRx3Wyh3KQabP3rd6+SFiKg5lsJZQHtkSAYA"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"info": {
|
||||||
|
"mimetype": "image/jpeg",
|
||||||
|
"h": 1536,
|
||||||
|
"size": 422018,
|
||||||
|
"thumbnail_file": {
|
||||||
|
"hashes": {
|
||||||
|
"sha256": "/NogKqW5bz/m8xHgFiH5haFGjCNVmUIPLzfvOhHdrxY"
|
||||||
|
},
|
||||||
|
"iv": "U+k7PfwLr6UAAAAAAAAAAA",
|
||||||
|
"key": {
|
||||||
|
"alg": "A256CTR",
|
||||||
|
"ext": true,
|
||||||
|
"k": "RMyd6zhlbifsACM1DXkCbioZ2u0SywGljTH8JmGcylg",
|
||||||
|
"key_ops": ["encrypt", "decrypt"],
|
||||||
|
"kty": "oct"
|
||||||
|
},
|
||||||
|
"mimetype": "image/jpeg",
|
||||||
|
"url": "mxc://domain.com/pmVJxyxGlmxHposwVSlOaEOv",
|
||||||
|
"v": "v2"
|
||||||
|
},
|
||||||
|
"thumbnail_info": {
|
||||||
|
"h": 768,
|
||||||
|
"mimetype": "image/jpeg",
|
||||||
|
"size": 211009,
|
||||||
|
"w": 432
|
||||||
|
},
|
||||||
|
"w": 864
|
||||||
|
},
|
||||||
|
"msgtype": "m.image"
|
||||||
|
},
|
||||||
|
"event_id": "$143273582443PhrSn:domain.com",
|
||||||
|
"origin_server_ts": 1432735824653,
|
||||||
|
"room_id": "!jEsUZKDJdhlrceRyVU:domain.com",
|
||||||
|
"sender": "@example:domain.com",
|
||||||
|
"type": "m.room.message",
|
||||||
|
"unsigned": {
|
||||||
|
"age": 1234
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Claiming one-time keys
|
Claiming one-time keys
|
||||||
~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
@ -583,6 +722,8 @@ Example response:
|
||||||
.. _curve25519: https://cr.yp.to/ecdh.html
|
.. _curve25519: https://cr.yp.to/ecdh.html
|
||||||
.. _`Olm specification`: http://matrix.org/docs/spec/olm.html
|
.. _`Olm specification`: http://matrix.org/docs/spec/olm.html
|
||||||
.. _`Megolm specification`: http://matrix.org/docs/spec/megolm.html
|
.. _`Megolm specification`: http://matrix.org/docs/spec/megolm.html
|
||||||
|
.. _`JSON Web Key`: https://tools.ietf.org/html/rfc7517#appendix-A.3
|
||||||
|
.. _`W3C extension`: https://w3c.github.io/webcrypto/#iana-section-jwk
|
||||||
|
|
||||||
.. _`Signing JSON`: ../appendices.html#signing-json
|
.. _`Signing JSON`: ../appendices.html#signing-json
|
||||||
|
|
||||||
|
|
|
@ -112,6 +112,13 @@ surrounding text due to Rich Text Editors. HTML included in events should otherw
|
||||||
such as having appropriate closing tags, appropriate attributes (considering the custom ones
|
such as having appropriate closing tags, appropriate attributes (considering the custom ones
|
||||||
defined in this specification), and generally valid structure.
|
defined in this specification), and generally valid structure.
|
||||||
|
|
||||||
|
A special tag, ``mx-reply``, may appear on rich replies (described below) and should be
|
||||||
|
allowed if, and only if, the tag appears as the very first tag in the ``formatted_body``.
|
||||||
|
The tag cannot be nested and cannot be located after another tag in the tree. Because the
|
||||||
|
tag contains HTML, an ``mx-reply`` is expected to have a partner closing tag and should
|
||||||
|
be treated similar to a ``div``. Clients that support rich replies will end up stripping
|
||||||
|
the tag and its contents and therefore may wish to exclude the tag entirely.
|
||||||
|
|
||||||
.. Note::
|
.. Note::
|
||||||
A future iteration of the specification will support more powerful and extensible
|
A future iteration of the specification will support more powerful and extensible
|
||||||
message formatting options, such as the proposal `MSC1225 <https://github.com/matrix-org/matrix-doc/issues/1225>`_.
|
message formatting options, such as the proposal `MSC1225 <https://github.com/matrix-org/matrix-doc/issues/1225>`_.
|
||||||
|
@ -346,6 +353,192 @@ change unexpectedly.
|
||||||
an English-language implementation on them all? See
|
an English-language implementation on them all? See
|
||||||
https://matrix.org/jira/browse/SPEC-425.
|
https://matrix.org/jira/browse/SPEC-425.
|
||||||
|
|
||||||
|
|
||||||
|
Forming relationships between events
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
In some cases, events may wish to reference other events. This could be to form
|
||||||
|
a thread of messages for the user to follow along with, or to provide more context
|
||||||
|
as to what a particular event is describing. Currently, the only kind of relation
|
||||||
|
defined is a "rich reply" where a user may reference another message to create a
|
||||||
|
thread-like conversation.
|
||||||
|
|
||||||
|
Relationships are defined under an ``m.relates_to`` key in the event's ``content``.
|
||||||
|
If the event is of the type ``m.room.encrypted``, the ``m.relates_to`` key MUST NOT
|
||||||
|
be covered by the encryption and instead be put alongside the encryption information
|
||||||
|
held in the ``content``.
|
||||||
|
|
||||||
|
|
||||||
|
Rich replies
|
||||||
|
++++++++++++
|
||||||
|
|
||||||
|
Users may wish to reference another message when forming their own message, and
|
||||||
|
clients may wish to better embed the referenced message for the user to have a
|
||||||
|
better context for the conversation being had. This sort of embedding another
|
||||||
|
message in a message is known as a "rich reply", or occasionally just a "reply".
|
||||||
|
|
||||||
|
A rich reply is formed through use of an ``m.relates_to`` relation for ``m.in_reply_to``
|
||||||
|
where a single key, ``event_id``, is used to reference the event being replied to.
|
||||||
|
The referenced event ID SHOULD belong to the same room where the reply is being sent.
|
||||||
|
Clients should be cautious of the event ID belonging to another room, or being invalid
|
||||||
|
entirely. Rich replies can only be constructed in the form of ``m.room.message`` events
|
||||||
|
with a ``msgtype`` of ``m.text`` or ``m.notice``. Due to the fallback requirements, rich
|
||||||
|
replies cannot be constructed for types of ``m.emote``, ``m.file``, etc. Rich replies
|
||||||
|
may reference any other ``m.room.message`` event, however. Rich replies may reference
|
||||||
|
another event which also has a rich reply, infinitely.
|
||||||
|
|
||||||
|
An ``m.in_reply_to`` relationship looks like the following::
|
||||||
|
|
||||||
|
{
|
||||||
|
...
|
||||||
|
"type": "m.room.message",
|
||||||
|
"content": {
|
||||||
|
"msgtype": "m.text",
|
||||||
|
"body": "<body including fallback>",
|
||||||
|
"format": "org.matrix.custom.html",
|
||||||
|
"formatted_body": "<HTML including fallback>",
|
||||||
|
"m.relates_to": {
|
||||||
|
"m.in_reply_to": {
|
||||||
|
"event_id": "$another:event.com"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Fallbacks and event representation
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
Some clients may not have support for rich replies and therefore need a fallback
|
||||||
|
to use instead. Clients that do not support rich replies should render the event
|
||||||
|
as if rich replies were not special.
|
||||||
|
|
||||||
|
Clients that do support rich replies MUST provide the fallback format on replies,
|
||||||
|
and MUST strip the fallback before rendering the reply. Rich replies MUST have
|
||||||
|
a ``format`` of ``org.matrix.custom.html`` and therefore a ``formatted_body``
|
||||||
|
alongside the ``body`` and appropriate ``msgtype``. The specific fallback text
|
||||||
|
is different for each ``msgtype``, however the general format for the ``body`` is:
|
||||||
|
|
||||||
|
.. code-block:: text
|
||||||
|
|
||||||
|
> <@alice:example.org> This is the original body
|
||||||
|
|
||||||
|
This is where the reply goes
|
||||||
|
|
||||||
|
|
||||||
|
The ``formatted_body`` should use the following template:
|
||||||
|
|
||||||
|
.. code-block:: html
|
||||||
|
|
||||||
|
<mx-reply>
|
||||||
|
<blockquote>
|
||||||
|
<a href="https://matrix.to/#/!somewhere:domain.com/$event:domain.com">In reply to</a>
|
||||||
|
<a href="https://matrix.to/#/@alice:example.org">@alice:example.org</a>
|
||||||
|
<br />
|
||||||
|
<!-- This is where the related event's HTML would be. -->
|
||||||
|
</blockquote>
|
||||||
|
</mx-reply>
|
||||||
|
This is where the reply goes.
|
||||||
|
|
||||||
|
|
||||||
|
If the related event does not have a ``formatted_body``, the event's ``body`` should
|
||||||
|
be considered after encoding any HTML special characters. Note that the ``href`` in
|
||||||
|
both of the anchors use a `matrix.to URI <../appendices.html#matrix-to-navigation>`_.
|
||||||
|
|
||||||
|
Stripping the fallback
|
||||||
|
``````````````````````
|
||||||
|
|
||||||
|
Clients which support rich replies MUST strip the fallback from the event before
|
||||||
|
rendering the event. This is because the text provided in the fallback cannot be
|
||||||
|
trusted to be an accurate representation of the event. After removing the fallback,
|
||||||
|
clients are recommended to represent the event referenced by ``m.in_reply_to``
|
||||||
|
similar to the fallback's representation, although clients do have creative freedom
|
||||||
|
for their user interface. Clients should prefer the ``formatted_body`` over the
|
||||||
|
``body``, just like with other ``m.room.message`` events.
|
||||||
|
|
||||||
|
To strip the fallback on the ``body``, the client should iterate over each line of
|
||||||
|
the string, removing any lines that start with the fallback prefix ("> ",
|
||||||
|
including the space, without quotes) and stopping when a line is encountered without
|
||||||
|
the prefix. This prefix is known as the "fallback prefix sequence".
|
||||||
|
|
||||||
|
To strip the fallback on the ``formatted_body``, the client should remove the
|
||||||
|
entirety of the ``mx-reply`` tag.
|
||||||
|
|
||||||
|
Fallback for ``m.text``, ``m.notice``, and unrecognised message types
|
||||||
|
`````````````````````````````````````````````````````````````````````
|
||||||
|
|
||||||
|
Using the prefix sequence, the first line of the related event's ``body`` should
|
||||||
|
be prefixed with the user's ID, followed by each line being prefixed with the fallback
|
||||||
|
prefix sequence. For example::
|
||||||
|
|
||||||
|
> <@alice:example.org> This is the first line
|
||||||
|
> This is the second line
|
||||||
|
|
||||||
|
This is the reply
|
||||||
|
|
||||||
|
|
||||||
|
The ``formatted_body`` uses the template defined earlier in this section.
|
||||||
|
|
||||||
|
Fallback for ``m.emote``
|
||||||
|
````````````````````````
|
||||||
|
|
||||||
|
Similar to the fallback for ``m.text``, each line gets prefixed with the fallback
|
||||||
|
prefix sequence. However an asterisk should be inserted before the user's ID, like
|
||||||
|
so::
|
||||||
|
|
||||||
|
> * <@alice:example.org> feels like today is going to be a great day
|
||||||
|
|
||||||
|
This is the reply
|
||||||
|
|
||||||
|
|
||||||
|
The ``formatted_body`` has a subtle difference for the template where the asterisk
|
||||||
|
is also inserted ahead of the user's ID:
|
||||||
|
|
||||||
|
.. code-block:: html
|
||||||
|
|
||||||
|
<mx-reply>
|
||||||
|
<blockquote>
|
||||||
|
<a href="https://matrix.to/#/!somewhere:domain.com/$event:domain.com">In reply to</a>
|
||||||
|
* <a href="https://matrix.to/#/@alice:example.org">@alice:example.org</a>
|
||||||
|
<br />
|
||||||
|
<!-- This is where the related event's HTML would be. -->
|
||||||
|
</blockquote>
|
||||||
|
</mx-reply>
|
||||||
|
This is where the reply goes.
|
||||||
|
|
||||||
|
|
||||||
|
Fallback for ``m.image``, ``m.video``, ``m.audio``, and ``m.file``
|
||||||
|
``````````````````````````````````````````````````````````````````
|
||||||
|
|
||||||
|
The related event's ``body`` would be a file name, which may not be very descriptive.
|
||||||
|
The related event should additionally not have a ``format`` or ``formatted_body``
|
||||||
|
in the ``content`` - if the event does have a ``format`` and/or ``formatted_body``,
|
||||||
|
those fields should be ignored. Because the filename alone may not be descriptive,
|
||||||
|
the related event's ``body`` should be considered to be ``"sent a file."`` such that
|
||||||
|
the output looks similar to the following::
|
||||||
|
|
||||||
|
> <@alice:example.org> sent a file.
|
||||||
|
|
||||||
|
This is the reply
|
||||||
|
|
||||||
|
|
||||||
|
.. code-block:: html
|
||||||
|
|
||||||
|
<mx-reply>
|
||||||
|
<blockquote>
|
||||||
|
<a href="https://matrix.to/#/!somewhere:domain.com/$event:domain.com">In reply to</a>
|
||||||
|
<a href="https://matrix.to/#/@alice:example.org">@alice:example.org</a>
|
||||||
|
<br />
|
||||||
|
sent a file.
|
||||||
|
</blockquote>
|
||||||
|
</mx-reply>
|
||||||
|
This is where the reply goes.
|
||||||
|
|
||||||
|
|
||||||
|
For ``m.image``, the text should be ``"sent an image."``. For ``m.video``, the text
|
||||||
|
should be ``"sent a video."``. For ``m.audio``, the text should be ``"sent an audio file"``.
|
||||||
|
|
||||||
|
|
||||||
Server behaviour
|
Server behaviour
|
||||||
----------------
|
----------------
|
||||||
|
|
||||||
|
|
67
specification/modules/read_markers.rst
Normal file
67
specification/modules/read_markers.rst
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
.. Copyright 2018 New Vector Ltd
|
||||||
|
..
|
||||||
|
.. Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
.. you may not use this file except in compliance with the License.
|
||||||
|
.. You may obtain a copy of the License at
|
||||||
|
..
|
||||||
|
.. http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
..
|
||||||
|
.. Unless required by applicable law or agreed to in writing, software
|
||||||
|
.. distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
.. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
.. See the License for the specific language governing permissions and
|
||||||
|
.. limitations under the License.
|
||||||
|
|
||||||
|
Fully read markers
|
||||||
|
==================
|
||||||
|
|
||||||
|
.. _module:read-markers:
|
||||||
|
|
||||||
|
The history for a given room may be split into three sections: messages the
|
||||||
|
user has read (or indicated they aren't interested in them), messages the user
|
||||||
|
might have read some but not others, and messages the user hasn't seen yet.
|
||||||
|
The "fully read marker" (also known as a "read marker") marks the last event
|
||||||
|
of the first section, whereas the user's read receipt marks the last event of
|
||||||
|
the second section.
|
||||||
|
|
||||||
|
Events
|
||||||
|
------
|
||||||
|
The user's fully read marker is kept as an event in the room's `account data`_.
|
||||||
|
The event may be read to determine the user's current fully read marker location
|
||||||
|
in the room, and just like other account data events the event will be pushed down
|
||||||
|
the event stream when updated.
|
||||||
|
|
||||||
|
The fully read marker is kept under an ``m.fully_read`` event. If the event does
|
||||||
|
not exist on the user's account data, the fully read marker should be considered
|
||||||
|
to be the user's read receipt location.
|
||||||
|
|
||||||
|
{{m_fully_read_event}}
|
||||||
|
|
||||||
|
Client behaviour
|
||||||
|
----------------
|
||||||
|
The client cannot update fully read markers by directly modifying the ``m.fully_read``
|
||||||
|
account data event. Instead, the client must make use of the read markers API
|
||||||
|
to change the values.
|
||||||
|
|
||||||
|
The read markers API can additionally update the user's read receipt (``m.read``)
|
||||||
|
location in the same operation as setting the fully read marker location. This is
|
||||||
|
because read receipts and read markers are commonly updated at the same time,
|
||||||
|
and therefore the client might wish to save an extra HTTP call. Providing an
|
||||||
|
``m.read`` location performs the same task as a request to ``/receipts/m.read/$event:domain.com``.
|
||||||
|
|
||||||
|
{{read_markers_cs_http_api}}
|
||||||
|
|
||||||
|
Server behaviour
|
||||||
|
----------------
|
||||||
|
The server MUST prevent clients from setting ``m.fully_read`` directly in
|
||||||
|
room account data. The server must additionally ensure that it treats the
|
||||||
|
presence of ``m.read`` in the ``/read_markers`` request the same as how it
|
||||||
|
would for a request to ``/receipts/m.read/$event:domain.com``.
|
||||||
|
|
||||||
|
Upon updating the ``m.fully_read`` event due to a request to ``/read_markers``,
|
||||||
|
the server MUST send the updated account data event through to the client via
|
||||||
|
the event stream (eg: ``/sync``), provided any applicable filters are also
|
||||||
|
satisfied.
|
||||||
|
|
||||||
|
|
||||||
|
.. _`account data`: #client-config
|
|
@ -45,6 +45,7 @@ groups: # reusable blobs of files when prefixed with 'group:'
|
||||||
- modules/voip_events.rst
|
- modules/voip_events.rst
|
||||||
- modules/typing_notifications.rst
|
- modules/typing_notifications.rst
|
||||||
- modules/receipts.rst
|
- modules/receipts.rst
|
||||||
|
- modules/read_markers.rst
|
||||||
- modules/presence.rst
|
- modules/presence.rst
|
||||||
- modules/content_repo.rst
|
- modules/content_repo.rst
|
||||||
- modules/send_to_device.rst
|
- modules/send_to_device.rst
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue