Merge branch 'master' into daniel/threepidinvites-2
Conflicts: specification/targets.yamlpull/41/head
commit
2502ca7ac6
@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"kind": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": ["event_match", "profile_tag", "contains_display_name", "room_member_count"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"default": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"enabled": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"rule_id": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"actions": {
|
||||||
|
"items": {
|
||||||
|
"type": ["object", "string"]
|
||||||
|
},
|
||||||
|
"type": "array"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,60 @@
|
|||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"content": {
|
||||||
|
"items": {
|
||||||
|
"type": "object",
|
||||||
|
"allOf": [
|
||||||
|
{
|
||||||
|
"$ref": "push_rule.json"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"type": "array"
|
||||||
|
},
|
||||||
|
"override": {
|
||||||
|
"items": {
|
||||||
|
"type": "object",
|
||||||
|
"allOf": [
|
||||||
|
{
|
||||||
|
"$ref": "push_rule.json"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"type": "array"
|
||||||
|
},
|
||||||
|
"sender": {
|
||||||
|
"items": {
|
||||||
|
"type": "object",
|
||||||
|
"allOf": [
|
||||||
|
{
|
||||||
|
"$ref": "push_rule.json"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"type": "array"
|
||||||
|
},
|
||||||
|
"underride": {
|
||||||
|
"items": {
|
||||||
|
"type": "object",
|
||||||
|
"allOf": [
|
||||||
|
{
|
||||||
|
"$ref": "push_rule.json"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"type": "array"
|
||||||
|
},
|
||||||
|
"room": {
|
||||||
|
"items": {
|
||||||
|
"type": "object",
|
||||||
|
"allOf": [
|
||||||
|
{
|
||||||
|
"$ref": "push_rule.json"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"type": "array"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,192 @@
|
|||||||
|
swagger: '2.0'
|
||||||
|
info:
|
||||||
|
title: "Matrix Push Notification API"
|
||||||
|
version: "1.0.0"
|
||||||
|
host: localhost:8008
|
||||||
|
schemes:
|
||||||
|
- https
|
||||||
|
- http
|
||||||
|
basePath: /_matrix/push/v1
|
||||||
|
consumes:
|
||||||
|
- application/json
|
||||||
|
produces:
|
||||||
|
- application/json
|
||||||
|
paths:
|
||||||
|
"/notify":
|
||||||
|
post:
|
||||||
|
summary: Notify a push gateway about an event.
|
||||||
|
description: |-
|
||||||
|
This endpoint is invoked by HTTP pushers to notify a push gateway about
|
||||||
|
an event.
|
||||||
|
*NB: Notifications are sent to the URL configured when the pusher is
|
||||||
|
created. This means that the HTTP path may be different depending on the
|
||||||
|
push gateway.*
|
||||||
|
parameters:
|
||||||
|
- in: body
|
||||||
|
name: notification
|
||||||
|
description: Information about the push notification.
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: object
|
||||||
|
example: |-
|
||||||
|
{
|
||||||
|
"notification": {
|
||||||
|
"id": "$3957tyerfgewrf384",
|
||||||
|
"room_id": "!slw48wfj34rtnrf:example.com",
|
||||||
|
"type": "m.room.message",
|
||||||
|
"sender": "@exampleuser:matrix.org",
|
||||||
|
"sender_display_name": "Major Tom",
|
||||||
|
"room_name": "Mission Control",
|
||||||
|
"room_alias": "#exampleroom:matrix.org",
|
||||||
|
"prio": "high",
|
||||||
|
"content": {
|
||||||
|
"msgtype": "m.text",
|
||||||
|
"body": "I'm floating in a most peculiar way."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"counts": {
|
||||||
|
"unread" : 2,
|
||||||
|
"missed_calls": 1
|
||||||
|
},
|
||||||
|
"devices": [
|
||||||
|
{
|
||||||
|
"app_id": "org.matrix.matrixConsole.ios",
|
||||||
|
"pushkey": "V2h5IG9uIGVhcnRoIGRpZCB5b3UgZGVjb2RlIHRoaXM/",
|
||||||
|
"pushkey_ts": 12345678,
|
||||||
|
"data" : {},
|
||||||
|
"tweaks": {
|
||||||
|
"sound": "bing"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
required: ["notification", "counts", "devices"]
|
||||||
|
properties:
|
||||||
|
notification:
|
||||||
|
type: object
|
||||||
|
description: Information about the push notification
|
||||||
|
required: ["id", "room_id", "type", "sender"]
|
||||||
|
properties:
|
||||||
|
id:
|
||||||
|
type: string
|
||||||
|
description: |-
|
||||||
|
An identifier for this notification that may be used to
|
||||||
|
detect duplicate notification requests. This is not
|
||||||
|
necessarily the ID of the event that triggered the
|
||||||
|
notification.
|
||||||
|
room_id:
|
||||||
|
type: string
|
||||||
|
description: The ID of the room in which this event occurred.
|
||||||
|
type:
|
||||||
|
type: string
|
||||||
|
description: The type of the event as in the event's ``type`` field.
|
||||||
|
sender:
|
||||||
|
type: string
|
||||||
|
description: The sender of the event as in the corresponding event field.
|
||||||
|
sender_display_name:
|
||||||
|
type: string
|
||||||
|
description: |-
|
||||||
|
The current display name of the sender in the room in which
|
||||||
|
the event occurred.
|
||||||
|
room_name:
|
||||||
|
type: string
|
||||||
|
description: The name of the room in which the event occurred.
|
||||||
|
room_alias:
|
||||||
|
type: string
|
||||||
|
description: An alias to display for the room in which the event occurred.
|
||||||
|
user_is_target:
|
||||||
|
type: boolean
|
||||||
|
description: |-
|
||||||
|
This is true if the user receiving the notification is the
|
||||||
|
subject of a member event (i.e. the ``state_key`` of the
|
||||||
|
member event is equal to the user's Matrix ID).
|
||||||
|
prio:
|
||||||
|
type: string
|
||||||
|
enum: ["high", "low"]
|
||||||
|
description: |-
|
||||||
|
The priority of the notification. If omitted, ``high`` is
|
||||||
|
assumed. This may be used by push gateways to deliver less
|
||||||
|
time-sensitive notifications in a way that will preserve
|
||||||
|
battery power on mobile devices.
|
||||||
|
content:
|
||||||
|
type: object
|
||||||
|
title: EventContent
|
||||||
|
description: |-
|
||||||
|
The ``content`` field from the event, if present. If the
|
||||||
|
event had no content field, this field is omitted.
|
||||||
|
counts:
|
||||||
|
type: object
|
||||||
|
description: |-
|
||||||
|
This is a dictionary of the current number of unacknowledged
|
||||||
|
communications for the recipient user. Counts whose value is
|
||||||
|
zero are omitted.
|
||||||
|
properties:
|
||||||
|
unread:
|
||||||
|
type: integer
|
||||||
|
description: |-
|
||||||
|
The number of unread messages a user has across all of the
|
||||||
|
rooms they are a member of.
|
||||||
|
missed_calls:
|
||||||
|
type: integer
|
||||||
|
description: |-
|
||||||
|
The number of unacknowledged missed calls a user has
|
||||||
|
across all rooms of which they are a member.
|
||||||
|
devices:
|
||||||
|
type: array
|
||||||
|
title: Devices
|
||||||
|
description: |-
|
||||||
|
This is an array of devices that the notification should be sent to.
|
||||||
|
items:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
app_id:
|
||||||
|
type: string
|
||||||
|
description: |-
|
||||||
|
The app_id given when the pusher was created.
|
||||||
|
pushkey:
|
||||||
|
type: string
|
||||||
|
description: The pushkey given when the pusher was created.
|
||||||
|
pushkey_ts:
|
||||||
|
type: integer
|
||||||
|
description: |-
|
||||||
|
The unix timestamp (in seconds) when the
|
||||||
|
pushkey was last updated.
|
||||||
|
data:
|
||||||
|
type: object
|
||||||
|
title: PusherData
|
||||||
|
description: |-
|
||||||
|
A dictionary of additional pusher-specific data. For
|
||||||
|
'http' pushers, this is the data dictionary passed in at
|
||||||
|
pusher creation minus the ``url`` key.
|
||||||
|
tweaks:
|
||||||
|
type: object
|
||||||
|
title: Tweaks
|
||||||
|
description: |-
|
||||||
|
A dictionary of customisations made to the way this
|
||||||
|
notification is to be presented. These are added by push rules.
|
||||||
|
responses:
|
||||||
|
200:
|
||||||
|
description: A list of rejected push keys.
|
||||||
|
examples:
|
||||||
|
application/json: |-
|
||||||
|
{
|
||||||
|
"rejected": [ "V2h5IG9uIGVhcnRoIGRpZCB5b3UgZGVjb2RlIHRoaXM/" ]
|
||||||
|
}
|
||||||
|
schema:
|
||||||
|
type: object # empty json object
|
||||||
|
properties:
|
||||||
|
rejected:
|
||||||
|
type: array
|
||||||
|
description: |-
|
||||||
|
A list of all pushkeys given in the notification request that
|
||||||
|
are not valid. These could have been rejected by an upstream
|
||||||
|
gateway because they have expired or have never been valid.
|
||||||
|
Homeservers must cease sending notification requests for these
|
||||||
|
pushkeys and remove the associated pushers. It may not
|
||||||
|
necessarily be the notification in the request that failed:
|
||||||
|
it could be that a previous notification to the same pushkey
|
||||||
|
failed.
|
||||||
|
items:
|
||||||
|
type: string
|
||||||
|
description: A pushkey
|
||||||
|
|
@ -0,0 +1,144 @@
|
|||||||
|
swagger: '2.0'
|
||||||
|
info:
|
||||||
|
title: "Matrix Client-Server v1 Push API"
|
||||||
|
version: "1.0.0"
|
||||||
|
host: localhost:8008
|
||||||
|
schemes:
|
||||||
|
- https
|
||||||
|
- http
|
||||||
|
basePath: /_matrix/client/api/v1
|
||||||
|
consumes:
|
||||||
|
- application/json
|
||||||
|
produces:
|
||||||
|
- application/json
|
||||||
|
securityDefinitions:
|
||||||
|
accessToken:
|
||||||
|
type: apiKey
|
||||||
|
description: The user_id or application service access_token
|
||||||
|
name: access_token
|
||||||
|
in: query
|
||||||
|
paths:
|
||||||
|
"/pushers/set":
|
||||||
|
post:
|
||||||
|
summary: Modify a pusher for this user on the homeserver.
|
||||||
|
description: |-
|
||||||
|
This endpoint allows the creation, modification and deletion of `pushers`_
|
||||||
|
for this user ID. The behaviour of this endpoint varies depending on the
|
||||||
|
values in the JSON body.
|
||||||
|
security:
|
||||||
|
- accessToken: []
|
||||||
|
parameters:
|
||||||
|
- in: body
|
||||||
|
name: pusher
|
||||||
|
description: The pusher information
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: object
|
||||||
|
example: |-
|
||||||
|
{
|
||||||
|
"lang": "en",
|
||||||
|
"kind": "http",
|
||||||
|
"app_display_name": "Mat Rix",
|
||||||
|
"device_display_name": "iPhone 9",
|
||||||
|
"app_id": "com.example.app.ios",
|
||||||
|
"profile_tag": "4bea66906d0111e59d70feff819cdc9f",
|
||||||
|
"pushkey": "APA91bHPRgkF3JUikC4ENAHEeMrd41Zxv3hVZjC9KtT8OvPVGJ-hQMRKRrZuJAEcl7B338qju59zJMjw2DELjzEvxwYv7hH5Ynpc1ODQ0aT4U4OFEeco8ohsN5PjL1iC2dNtk2BAokeMCg2ZXKqpc8FXKmhX94kIxQ",
|
||||||
|
"data": {
|
||||||
|
"url": "https://push-gateway.location.here"
|
||||||
|
},
|
||||||
|
"append": false
|
||||||
|
}
|
||||||
|
properties:
|
||||||
|
pushkey:
|
||||||
|
type: string
|
||||||
|
description: |-
|
||||||
|
This is a unique identifier for this pusher. The value you
|
||||||
|
should use for this is the routing or destination address
|
||||||
|
information for the notification, for example, the APNS token
|
||||||
|
for APNS or the Registration ID for GCM. If your notification
|
||||||
|
client has no such concept, use any unique identifier.
|
||||||
|
Max length, 512 bytes.
|
||||||
|
kind:
|
||||||
|
type: string
|
||||||
|
enum: ["http", null]
|
||||||
|
description: |-
|
||||||
|
The kind of pusher to configure. ``"http"`` makes a pusher that
|
||||||
|
sends HTTP pokes. ``null`` deletes the pusher.
|
||||||
|
profile_tag:
|
||||||
|
type: string
|
||||||
|
description: |-
|
||||||
|
This is a string that determines what set of device rules will
|
||||||
|
be matched when evaluating push rules for this pusher. It is
|
||||||
|
an arbitrary string. Multiple devices may use the same
|
||||||
|
``profile_tag``. It is advised that when an app's data is
|
||||||
|
copied or restored to a different device, this value remain
|
||||||
|
the same. Client apps should offer ways to change the
|
||||||
|
``profile_tag``, optionally copying rules from the old
|
||||||
|
profile tag. Max length, 32 bytes.
|
||||||
|
app_id:
|
||||||
|
type: string
|
||||||
|
description: |-
|
||||||
|
This is a reverse-DNS style identifier for the application.
|
||||||
|
It is recommended that this end with the platform, such that
|
||||||
|
different platform versions get different app identifiers.
|
||||||
|
Max length, 64 chars.
|
||||||
|
app_display_name:
|
||||||
|
type: string
|
||||||
|
description: |-
|
||||||
|
A string that will allow the user to identify what application
|
||||||
|
owns this pusher.
|
||||||
|
device_display_name:
|
||||||
|
type: string
|
||||||
|
description: |-
|
||||||
|
A string that will allow the user to identify what device owns
|
||||||
|
this pusher.
|
||||||
|
lang:
|
||||||
|
type: string
|
||||||
|
description: |-
|
||||||
|
The preferred language for receiving notifications (e.g. 'en'
|
||||||
|
or 'en-US')
|
||||||
|
data:
|
||||||
|
type: object
|
||||||
|
description: |-
|
||||||
|
A dictionary of information for the pusher implementation
|
||||||
|
itself. If ``kind`` is ``http``, this should contain ``url``
|
||||||
|
which is the URL to use to send notifications to.
|
||||||
|
properties:
|
||||||
|
url:
|
||||||
|
type: string
|
||||||
|
description: |-
|
||||||
|
Required if ``kind`` is ``http``. The URL to use to send
|
||||||
|
notifications to.
|
||||||
|
append:
|
||||||
|
type: boolean
|
||||||
|
description: |-
|
||||||
|
If true, the homeserver should add another pusher with the
|
||||||
|
given pushkey and App ID in addition to any others with
|
||||||
|
different user IDs. Otherwise, the Home Server must remove any
|
||||||
|
other pushers with the same App ID and pushkey for different
|
||||||
|
users. The default is ``false``.
|
||||||
|
required: ['profile_tag', 'kind', 'app_id', 'app_display_name',
|
||||||
|
'device_display_name', 'pushkey', 'lang', 'data']
|
||||||
|
responses:
|
||||||
|
200:
|
||||||
|
description: The pusher was set.
|
||||||
|
examples:
|
||||||
|
application/json: |-
|
||||||
|
{}
|
||||||
|
schema:
|
||||||
|
type: object # empty json object
|
||||||
|
400:
|
||||||
|
description: One or more of the pusher values were invalid.
|
||||||
|
examples:
|
||||||
|
application/json: |-
|
||||||
|
{
|
||||||
|
"error": "Missing parameters: lang, data",
|
||||||
|
"errcode": "M_MISSING_PARAM"
|
||||||
|
}
|
||||||
|
schema:
|
||||||
|
type: object
|
||||||
|
429:
|
||||||
|
description: This request was rate-limited.
|
||||||
|
schema:
|
||||||
|
"$ref": "definitions/error.yaml"
|
||||||
|
|
@ -0,0 +1,488 @@
|
|||||||
|
swagger: '2.0'
|
||||||
|
info:
|
||||||
|
title: "Matrix Client-Server v1 Push Rules API"
|
||||||
|
version: "1.0.0"
|
||||||
|
host: localhost:8008
|
||||||
|
schemes:
|
||||||
|
- https
|
||||||
|
- http
|
||||||
|
basePath: /_matrix/client/api/v1
|
||||||
|
consumes:
|
||||||
|
- application/json
|
||||||
|
produces:
|
||||||
|
- application/json
|
||||||
|
securityDefinitions:
|
||||||
|
accessToken:
|
||||||
|
type: apiKey
|
||||||
|
description: The user_id or application service access_token
|
||||||
|
name: access_token
|
||||||
|
in: query
|
||||||
|
paths:
|
||||||
|
"/pushrules/":
|
||||||
|
get:
|
||||||
|
summary: Retrieve all push rulesets.
|
||||||
|
description: |-
|
||||||
|
Retrieve all push rulesets for this user. Clients can "drill-down" on
|
||||||
|
the rulesets by suffixing a ``scope`` to this path e.g.
|
||||||
|
``/pushrules/global/``. This will return a subset of this data under the
|
||||||
|
specified key e.g. the ``global`` key.
|
||||||
|
security:
|
||||||
|
- accessToken: []
|
||||||
|
responses:
|
||||||
|
200:
|
||||||
|
description: All the push rulesets for this user.
|
||||||
|
schema:
|
||||||
|
type: object
|
||||||
|
required: ["device", "global"]
|
||||||
|
properties:
|
||||||
|
device:
|
||||||
|
type: object
|
||||||
|
title: Devices
|
||||||
|
description: A dictionary of profile tags to rulesets.
|
||||||
|
additionalProperties:
|
||||||
|
x-pattern: "$PROFILE_TAG"
|
||||||
|
type: object
|
||||||
|
description: The ruleset for this profile tag.
|
||||||
|
title: Ruleset
|
||||||
|
allOf: [
|
||||||
|
"$ref": "definitions/push_ruleset.json"
|
||||||
|
]
|
||||||
|
global:
|
||||||
|
type: object
|
||||||
|
description: The global ruleset.
|
||||||
|
title: Ruleset
|
||||||
|
allOf: [
|
||||||
|
"$ref": "definitions/push_ruleset.json"
|
||||||
|
]
|
||||||
|
examples:
|
||||||
|
application/json: |-
|
||||||
|
{
|
||||||
|
"device": {},
|
||||||
|
"global": {
|
||||||
|
"content": [
|
||||||
|
{
|
||||||
|
"actions": [
|
||||||
|
"notify",
|
||||||
|
{
|
||||||
|
"set_tweak": "sound",
|
||||||
|
"value": "default"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"set_tweak": "highlight"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"default": true,
|
||||||
|
"enabled": true,
|
||||||
|
"pattern": "alice",
|
||||||
|
"rule_id": ".m.rule.contains_user_name"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"override": [
|
||||||
|
{
|
||||||
|
"actions": [
|
||||||
|
"dont_notify"
|
||||||
|
],
|
||||||
|
"conditions": [],
|
||||||
|
"default": true,
|
||||||
|
"enabled": false,
|
||||||
|
"rule_id": ".m.rule.master"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"actions": [
|
||||||
|
"dont_notify"
|
||||||
|
],
|
||||||
|
"conditions": [
|
||||||
|
{
|
||||||
|
"key": "content.msgtype",
|
||||||
|
"kind": "event_match",
|
||||||
|
"pattern": "m.notice"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"default": true,
|
||||||
|
"enabled": true,
|
||||||
|
"rule_id": ".m.rule.suppress_notices"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"room": [],
|
||||||
|
"sender": [],
|
||||||
|
"underride": [
|
||||||
|
{
|
||||||
|
"actions": [
|
||||||
|
"notify",
|
||||||
|
{
|
||||||
|
"set_tweak": "sound",
|
||||||
|
"value": "ring"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"set_tweak": "highlight",
|
||||||
|
"value": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"conditions": [
|
||||||
|
{
|
||||||
|
"key": "type",
|
||||||
|
"kind": "event_match",
|
||||||
|
"pattern": "m.call.invite"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"default": true,
|
||||||
|
"enabled": true,
|
||||||
|
"rule_id": ".m.rule.call"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"actions": [
|
||||||
|
"notify",
|
||||||
|
{
|
||||||
|
"set_tweak": "sound",
|
||||||
|
"value": "default"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"set_tweak": "highlight"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"conditions": [
|
||||||
|
{
|
||||||
|
"kind": "contains_display_name"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"default": true,
|
||||||
|
"enabled": true,
|
||||||
|
"rule_id": ".m.rule.contains_display_name"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"actions": [
|
||||||
|
"notify",
|
||||||
|
{
|
||||||
|
"set_tweak": "sound",
|
||||||
|
"value": "default"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"set_tweak": "highlight",
|
||||||
|
"value": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"conditions": [
|
||||||
|
{
|
||||||
|
"is": "2",
|
||||||
|
"kind": "room_member_count"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"default": true,
|
||||||
|
"enabled": true,
|
||||||
|
"rule_id": ".m.rule.room_one_to_one"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"actions": [
|
||||||
|
"notify",
|
||||||
|
{
|
||||||
|
"set_tweak": "sound",
|
||||||
|
"value": "default"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"set_tweak": "highlight",
|
||||||
|
"value": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"conditions": [
|
||||||
|
{
|
||||||
|
"key": "type",
|
||||||
|
"kind": "event_match",
|
||||||
|
"pattern": "m.room.member"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "content.membership",
|
||||||
|
"kind": "event_match",
|
||||||
|
"pattern": "invite"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "state_key",
|
||||||
|
"kind": "event_match",
|
||||||
|
"pattern": "@alice:example.com"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"default": true,
|
||||||
|
"enabled": true,
|
||||||
|
"rule_id": ".m.rule.invite_for_me"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"actions": [
|
||||||
|
"notify",
|
||||||
|
{
|
||||||
|
"set_tweak": "highlight",
|
||||||
|
"value": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"conditions": [
|
||||||
|
{
|
||||||
|
"key": "type",
|
||||||
|
"kind": "event_match",
|
||||||
|
"pattern": "m.room.member"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"default": true,
|
||||||
|
"enabled": true,
|
||||||
|
"rule_id": ".m.rule.member_event"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"actions": [
|
||||||
|
"notify",
|
||||||
|
{
|
||||||
|
"set_tweak": "highlight",
|
||||||
|
"value": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"conditions": [
|
||||||
|
{
|
||||||
|
"key": "type",
|
||||||
|
"kind": "event_match",
|
||||||
|
"pattern": "m.room.message"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"default": true,
|
||||||
|
"enabled": true,
|
||||||
|
"rule_id": ".m.rule.message"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"/pushrules/{scope}/{kind}/{ruleId}":
|
||||||
|
get:
|
||||||
|
summary: Retrieve a push rule.
|
||||||
|
description: |-
|
||||||
|
Retrieve a single specified push rule.
|
||||||
|
security:
|
||||||
|
- accessToken: []
|
||||||
|
parameters:
|
||||||
|
- in: path
|
||||||
|
type: string
|
||||||
|
name: scope
|
||||||
|
required: true
|
||||||
|
x-example: "global"
|
||||||
|
description: |-
|
||||||
|
Either ``global`` or ``device/<profile_tag>`` to specify global
|
||||||
|
rules or device rules for the given ``profile_tag``.
|
||||||
|
- in: path
|
||||||
|
type: string
|
||||||
|
name: kind
|
||||||
|
required: true
|
||||||
|
x-example: room
|
||||||
|
enum: ["override", "underride", "sender", "room", "content"]
|
||||||
|
description: |
|
||||||
|
The kind of rule
|
||||||
|
- in: path
|
||||||
|
type: string
|
||||||
|
name: ruleId
|
||||||
|
required: true
|
||||||
|
x-example: "#spam:example.com"
|
||||||
|
description: |
|
||||||
|
The identifier for the rule.
|
||||||
|
responses:
|
||||||
|
200:
|
||||||
|
description: |-
|
||||||
|
The specific push rule. This will also include keys specific to the
|
||||||
|
rule itself such as the rule's ``actions`` and ``conditions`` if set.
|
||||||
|
examples:
|
||||||
|
application/json: |-
|
||||||
|
{
|
||||||
|
"actions": [
|
||||||
|
"dont_notify"
|
||||||
|
],
|
||||||
|
"rule_id": "#spam:matrix.org",
|
||||||
|
"enabled": true
|
||||||
|
}
|
||||||
|
schema:
|
||||||
|
type: object
|
||||||
|
description: The push rule.
|
||||||
|
title: PushRule
|
||||||
|
allOf: [
|
||||||
|
"$ref": "definitions/push_rule.json"
|
||||||
|
]
|
||||||
|
delete:
|
||||||
|
summary: Delete a push rule.
|
||||||
|
description: |-
|
||||||
|
This endpoint removes the push rule defined in the path.
|
||||||
|
security:
|
||||||
|
- accessToken: []
|
||||||
|
parameters:
|
||||||
|
- in: path
|
||||||
|
type: string
|
||||||
|
name: scope
|
||||||
|
required: true
|
||||||
|
x-example: "global"
|
||||||
|
description: |-
|
||||||
|
Either ``global`` or ``device/<profile_tag>`` to specify global
|
||||||
|
rules or device rules for the given ``profile_tag``.
|
||||||
|
- in: path
|
||||||
|
type: string
|
||||||
|
name: kind
|
||||||
|
required: true
|
||||||
|
x-example: room
|
||||||
|
enum: ["override", "underride", "sender", "room", "content"]
|
||||||
|
description: |
|
||||||
|
The kind of rule
|
||||||
|
- in: path
|
||||||
|
type: string
|
||||||
|
name: ruleId
|
||||||
|
required: true
|
||||||
|
x-example: "#spam:example.com"
|
||||||
|
description: |
|
||||||
|
The identifier for the rule.
|
||||||
|
responses:
|
||||||
|
200:
|
||||||
|
description: The push rule was deleted.
|
||||||
|
examples:
|
||||||
|
application/json: |-
|
||||||
|
{}
|
||||||
|
schema:
|
||||||
|
type: object # empty json object
|
||||||
|
put:
|
||||||
|
summary: Add or change a push rule.
|
||||||
|
description: |-
|
||||||
|
This endpoint allows the creation, modification and deletion of pushers
|
||||||
|
for this user ID. The behaviour of this endpoint varies depending on the
|
||||||
|
values in the JSON body.
|
||||||
|
security:
|
||||||
|
- accessToken: []
|
||||||
|
parameters:
|
||||||
|
- in: path
|
||||||
|
type: string
|
||||||
|
name: scope
|
||||||
|
required: true
|
||||||
|
x-example: "global"
|
||||||
|
description: |-
|
||||||
|
Either ``global`` or ``device/<profile_tag>`` to specify global
|
||||||
|
rules or device rules for the given ``profile_tag``.
|
||||||
|
- in: path
|
||||||
|
type: string
|
||||||
|
name: kind
|
||||||
|
required: true
|
||||||
|
x-example: room
|
||||||
|
enum: ["override", "underride", "sender", "room", "content"]
|
||||||
|
description: |
|
||||||
|
The kind of rule
|
||||||
|
- in: path
|
||||||
|
type: string
|
||||||
|
name: ruleId
|
||||||
|
required: true
|
||||||
|
x-example: "#spam:example.com"
|
||||||
|
description: |
|
||||||
|
The identifier for the rule.
|
||||||
|
- in: query
|
||||||
|
type: string
|
||||||
|
name: before
|
||||||
|
required: false
|
||||||
|
x-example: someRuleId
|
||||||
|
description: |-
|
||||||
|
Use 'before' with a ``rule_id`` as its value to make the new rule the
|
||||||
|
next-most important rule with respect to the given rule.
|
||||||
|
- in: query
|
||||||
|
type: string
|
||||||
|
name: after
|
||||||
|
required: false
|
||||||
|
x-example: anotherRuleId
|
||||||
|
description: |-
|
||||||
|
This makes the new rule the next-less important rule relative to the
|
||||||
|
given rule.
|
||||||
|
- in: body
|
||||||
|
name: pushrule
|
||||||
|
description: |-
|
||||||
|
The push rule data. Additional top-level keys may be present depending
|
||||||
|
on the parameters for the rule ``kind``.
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: object
|
||||||
|
example: |-
|
||||||
|
{
|
||||||
|
"pattern": "cake*lie",
|
||||||
|
"actions": ["notify"]
|
||||||
|
}
|
||||||
|
properties:
|
||||||
|
actions:
|
||||||
|
type: array
|
||||||
|
description: |-
|
||||||
|
The action(s) to perform when the conditions for this rule are met.
|
||||||
|
items:
|
||||||
|
type: string
|
||||||
|
enum: ["notify", "dont_notify", "coalesce", "set_tweak"]
|
||||||
|
# TODO: type: object e.g. {"set_sound":"beeroclock.wav"} :/
|
||||||
|
conditions:
|
||||||
|
type: array
|
||||||
|
description: |-
|
||||||
|
The conditions that must hold true for an event in order for a
|
||||||
|
rule to be applied to an event. A rule with no conditions
|
||||||
|
always matches.
|
||||||
|
items:
|
||||||
|
type: object
|
||||||
|
title: conditions
|
||||||
|
allOf: [ "$ref": "definitions/push_condition.json" ]
|
||||||
|
required: ["actions"]
|
||||||
|
responses:
|
||||||
|
200:
|
||||||
|
description: The pusher was set.
|
||||||
|
examples:
|
||||||
|
application/json: |-
|
||||||
|
{}
|
||||||
|
schema:
|
||||||
|
type: object # empty json object
|
||||||
|
400:
|
||||||
|
description: There was a problem configuring this push rule.
|
||||||
|
examples:
|
||||||
|
application/json: |-
|
||||||
|
{
|
||||||
|
"error": "before/after rule not found: someRuleId",
|
||||||
|
"errcode": "M_UNKNOWN"
|
||||||
|
}
|
||||||
|
schema:
|
||||||
|
type: object
|
||||||
|
429:
|
||||||
|
description: This request was rate-limited.
|
||||||
|
schema:
|
||||||
|
"$ref": "definitions/error.yaml"
|
||||||
|
"/pushrules/{scope}/{kind}/{ruleId}/enabled":
|
||||||
|
put:
|
||||||
|
summary: "Enable or disable a push rule."
|
||||||
|
description: |-
|
||||||
|
This endpoint allows clients to enable or disable the specified push rule.
|
||||||
|
security:
|
||||||
|
- accessToken: []
|
||||||
|
parameters:
|
||||||
|
- in: path
|
||||||
|
type: string
|
||||||
|
name: scope
|
||||||
|
required: true
|
||||||
|
x-example: "global"
|
||||||
|
description: |-
|
||||||
|
Either ``global`` or ``device/<profile_tag>`` to specify global
|
||||||
|
rules or device rules for the given ``profile_tag``.
|
||||||
|
- in: path
|
||||||
|
type: string
|
||||||
|
name: kind
|
||||||
|
required: true
|
||||||
|
x-example: room
|
||||||
|
enum: ["override", "underride", "sender", "room", "content"]
|
||||||
|
description: |
|
||||||
|
The kind of rule
|
||||||
|
- in: path
|
||||||
|
type: string
|
||||||
|
name: ruleId
|
||||||
|
required: true
|
||||||
|
x-example: "#spam:example.com"
|
||||||
|
description: |
|
||||||
|
The identifier for the rule.
|
||||||
|
- in: body
|
||||||
|
name: <body>
|
||||||
|
description: |
|
||||||
|
Whether the push rule is enabled or not.
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: boolean
|
||||||
|
example: |-
|
||||||
|
true
|
||||||
|
responses:
|
||||||
|
200:
|
||||||
|
description: The push rule was enabled or disabled.
|
||||||
|
examples:
|
||||||
|
application/json: |-
|
||||||
|
{}
|
||||||
|
schema:
|
||||||
|
type: object # empty json object
|
@ -0,0 +1,438 @@
|
|||||||
|
Push Notifications
|
||||||
|
==================
|
||||||
|
|
||||||
|
.. _module:push:
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
+--------------------+ +-------------------+
|
||||||
|
Matrix HTTP | | | |
|
||||||
|
Notification Protocol | App Developer | | Device Vendor |
|
||||||
|
| | | |
|
||||||
|
+-------------------+ | +----------------+ | | +---------------+ |
|
||||||
|
| | | | | | | | | |
|
||||||
|
| Matrix Home Server+-----> Push Gateway +------> Push Provider | |
|
||||||
|
| | | | | | | | | |
|
||||||
|
+-^-----------------+ | +----------------+ | | +----+----------+ |
|
||||||
|
| | | | | |
|
||||||
|
Matrix | | | | | |
|
||||||
|
Client/Server API + | | | | |
|
||||||
|
| | +--------------------+ +-------------------+
|
||||||
|
| +--+-+ |
|
||||||
|
| | <-------------------------------------------+
|
||||||
|
+---+ |
|
||||||
|
| | Provider Push Protocol
|
||||||
|
+----+
|
||||||
|
|
||||||
|
Mobile Device or Client
|
||||||
|
|
||||||
|
|
||||||
|
This module adds support for push notifications. Homeservers send notifications
|
||||||
|
of events to user-configured HTTP endpoints. Users may also configure a
|
||||||
|
number of rules that determine which events generate notifications. These are
|
||||||
|
all stored and managed by the user's homeserver. This allows user-specific push
|
||||||
|
settings to be reused between client applications.
|
||||||
|
|
||||||
|
The above diagram shows the flow of push notifications being sent to a handset
|
||||||
|
where push notifications are submitted via the handset vendor, such as Apple's
|
||||||
|
APNS or Google's GCM. This happens as follows:
|
||||||
|
|
||||||
|
1. The client app signs in to a homeserver.
|
||||||
|
2. The client app registers with its vendor's Push Provider and
|
||||||
|
obtains a routing token of some kind.
|
||||||
|
3. The mobile app uses the Client/Server API to add a 'pusher', providing the
|
||||||
|
URL of a specific Push Gateway which is configured for that
|
||||||
|
application. It also provides the routing token it has acquired from the
|
||||||
|
Push Provider.
|
||||||
|
4. The homeserver starts sending HTTP requests to the Push Gateway using the
|
||||||
|
supplied URL. The Push Gateway relays this notification to
|
||||||
|
the Push Provider, passing the routing token along with any
|
||||||
|
necessary private credentials the provider requires to send push
|
||||||
|
notifications.
|
||||||
|
5. The Push Provider sends the notification to the device.
|
||||||
|
|
||||||
|
Definitions for terms used in this section are below:
|
||||||
|
|
||||||
|
Push Provider
|
||||||
|
A push provider is a service managed by the device vendor which can send
|
||||||
|
notifications directly to the device. Google Cloud Messaging (GCM) and Apple
|
||||||
|
Push Notification Service (APNS) are two examples of push providers.
|
||||||
|
|
||||||
|
Push Gateway
|
||||||
|
A push gateway is a server that receives HTTP event notifications from
|
||||||
|
homeservers and passes them on to a different protocol such as APNS for iOS
|
||||||
|
devices or GCM for Android devices. Clients inform the homeserver which
|
||||||
|
Push Gateway to send notifications to when it sets up a Pusher.
|
||||||
|
|
||||||
|
.. _def:pushers:
|
||||||
|
|
||||||
|
Pusher
|
||||||
|
A pusher is a worker on the homeserver that manages the sending
|
||||||
|
of HTTP notifications for a user. A user can have multiple pushers: one per
|
||||||
|
device.
|
||||||
|
|
||||||
|
Push Rule
|
||||||
|
A push rule is a single rule that states under what *conditions* an event should
|
||||||
|
be passed onto a push gateway and *how* the notification should be presented.
|
||||||
|
These rules are stored on the user's homeserver. They are manually configured
|
||||||
|
by the user, who can create and view them via the Client/Server API.
|
||||||
|
|
||||||
|
Push Ruleset
|
||||||
|
A push ruleset *scopes a set of rules according to some criteria*. For example,
|
||||||
|
some rules may only be applied for messages from a particular sender,
|
||||||
|
a particular room, or by default. The push ruleset contains the entire set
|
||||||
|
of scopes and rules.
|
||||||
|
|
||||||
|
Client behaviour
|
||||||
|
----------------
|
||||||
|
|
||||||
|
Clients MUST configure a Pusher before they will receive push notifications.
|
||||||
|
There is a single API endpoint for this, as described below.
|
||||||
|
|
||||||
|
{{pusher_http_api}}
|
||||||
|
|
||||||
|
.. _pushers: `def:pushers`_
|
||||||
|
|
||||||
|
Push Rules
|
||||||
|
~~~~~~~~~~
|
||||||
|
A push rule is a single rule that states under what *conditions* an event should
|
||||||
|
be passed onto a push gateway and *how* the notification should be presented.
|
||||||
|
There are different "kinds" of push rules and each rule has an associated
|
||||||
|
priority. Every push rule MUST have a ``kind`` and ``rule_id``. The ``rule_id``
|
||||||
|
is a unique string within the kind of rule and its' scope: ``rule_ids`` do not
|
||||||
|
need to be unique between rules of the same kind on different devices. Rules may
|
||||||
|
have extra keys depending on the value of ``kind``.The different kinds of rule
|
||||||
|
in descending order of priority are:
|
||||||
|
|
||||||
|
Override Rules ``override``
|
||||||
|
The highest priority rules are user-configured overrides.
|
||||||
|
Content-specific Rules ``content``
|
||||||
|
These configure behaviour for (unencrypted) messages that match certain
|
||||||
|
patterns. Content rules take one parameter: ``pattern``, that gives the glob
|
||||||
|
pattern to match against. This is treated in the same way as ``pattern`` for
|
||||||
|
``event_match``.
|
||||||
|
Room-specific Rules ``room``
|
||||||
|
These rules change the behaviour of all messages for a given room. The
|
||||||
|
``rule_id`` of a room rule is always the ID of the room that it affects.
|
||||||
|
Sender-specific rules ``sender``
|
||||||
|
These rules configure notification behaviour for messages from a specific
|
||||||
|
Matrix user ID. The ``rule_id`` of Sender rules is always the Matrix user
|
||||||
|
ID of the user whose messages they'd apply to.
|
||||||
|
Underride rules ``underride``
|
||||||
|
These are identical to ``override`` rules, but have a lower priority than
|
||||||
|
``content``, ``room`` and ``sender`` rules.
|
||||||
|
|
||||||
|
Push rules may be either global or device-specific. Device specific rules only
|
||||||
|
affect delivery of notifications via pushers with a matching ``profile_tag``.
|
||||||
|
All device-specific rules have a higher priority than global rules. This means
|
||||||
|
that the full list of rule kinds, in descending priority order, is as follows:
|
||||||
|
|
||||||
|
* Device-specific Override
|
||||||
|
* Device-specific Content
|
||||||
|
* Device-specific Room
|
||||||
|
* Device-specific Sender
|
||||||
|
* Device-specific Underride
|
||||||
|
* Global Override
|
||||||
|
* Global Content
|
||||||
|
* Global Room
|
||||||
|
* Global Sender
|
||||||
|
* Global Underride
|
||||||
|
|
||||||
|
Rules with the same ``kind`` can specify an ordering priority. This determines
|
||||||
|
which rule is selected in the event of multiple matches. For example, a rule
|
||||||
|
matching "tea" and a separate rule matching "time" would both match the sentence
|
||||||
|
"It's time for tea". The ordering of the rules would then resolve the tiebreak
|
||||||
|
to determine which rule is executed. Only ``actions`` for highest priority rule
|
||||||
|
will be sent to the Push Gateway.
|
||||||
|
|
||||||
|
Each rule can be enabled or disabled. Disabled rules never match. If no rules
|
||||||
|
match an event, the homeserver MUST NOT notify the Push Gateway for that event.
|
||||||
|
Homeservers MUST NOT notify the Push Gateway for events that the user has sent
|
||||||
|
themselves.
|
||||||
|
|
||||||
|
Actions
|
||||||
|
+++++++
|
||||||
|
All rules have an associated list of ``actions``. An action affects if and how a
|
||||||
|
notification is delivered for a matching event. The following actions are defined:
|
||||||
|
|
||||||
|
``notify``
|
||||||
|
This causes each matching event to generate a notification.
|
||||||
|
``dont_notify``
|
||||||
|
This prevents each matching event from generating a notification
|
||||||
|
``coalesce``
|
||||||
|
This enables notifications for matching events but activates homeserver
|
||||||
|
specific behaviour to intelligently coalesce multiple events into a single
|
||||||
|
notification. Not all homeservers may support this. Those that do not support
|
||||||
|
it should treat it as the ``notify`` action.
|
||||||
|
``set_tweak``
|
||||||
|
Sets an entry in the ``tweaks`` dictionary key that is sent in the notification
|
||||||
|
request to the Push Gateway. This takes the form of a dictionary with a
|
||||||
|
``set_tweak`` key whose value is the name of the tweak to set. It may also
|
||||||
|
have a ``value`` key which is the value to which it should be set.
|
||||||
|
|
||||||
|
Actions that have no parameters are represented as a string. Otherwise, they are
|
||||||
|
represented as a dictionary with a key equal to their name and other keys as
|
||||||
|
their parameters, e.g. ``{ "set_tweak": "sound", "value": "default" }``
|
||||||
|
|
||||||
|
Tweaks
|
||||||
|
^^^^^^
|
||||||
|
The ``set_tweak`` action is used to add an entry to the 'tweaks' dictionary
|
||||||
|
that is sent in the notification request to the Push Gateway. The following
|
||||||
|
tweaks are defined:
|
||||||
|
|
||||||
|
``sound``
|
||||||
|
A string representing the sound to be played when this notification arrives.
|
||||||
|
A value of ``default`` means to play a default sound.
|
||||||
|
``highlight``
|
||||||
|
A boolean representing whether or not this message should be highlighted in
|
||||||
|
the UI. This will normally take the form of presenting the message in a
|
||||||
|
different colour and/or style. The UI might also be adjusted to draw
|
||||||
|
particular attention to the room in which the event occurred. The ``value``
|
||||||
|
may be omitted from the highlight tweak, in which case it should default to
|
||||||
|
``true``.
|
||||||
|
|
||||||
|
Tweaks are passed transparently through the homeserver so client applications
|
||||||
|
and Push Gateways may agree on additional tweaks. For example, a tweak may be
|
||||||
|
added to specify how to flash the notification light on a mobile device.
|
||||||
|
|
||||||
|
Predefined Rules
|
||||||
|
++++++++++++++++
|
||||||
|
Homeservers can specify "server-default rules" which operate at a lower priority
|
||||||
|
than "user-defined rules". The ``rule_id`` for all server-default rules MUST
|
||||||
|
start with a dot (".") to identify them as "server-default". The following
|
||||||
|
server-default rules are specified:
|
||||||
|
|
||||||
|
``.m.rule.contains_user_name``
|
||||||
|
Matches any message whose content is unencrypted and contains the local part
|
||||||
|
of the user's Matrix ID, separated by word boundaries.
|
||||||
|
|
||||||
|
Definition (as a ``content`` rule)::
|
||||||
|
|
||||||
|
{
|
||||||
|
"rule_id": ".m.rule.contains_user_name"
|
||||||
|
"pattern": "[the local part of the user's Matrix ID]",
|
||||||
|
"actions": [
|
||||||
|
"notify",
|
||||||
|
{
|
||||||
|
"set_tweak": "sound",
|
||||||
|
"value": "default"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
``.m.rule.contains_display_name``
|
||||||
|
Matches any message whose content is unencrypted and contains the user's
|
||||||
|
current display name in the room in which it was sent.
|
||||||
|
|
||||||
|
Definition (this rule can only be an ``override`` or ``underride`` rule)::
|
||||||
|
|
||||||
|
{
|
||||||
|
"rule_id": ".m.rule.contains_display_name"
|
||||||
|
"conditions": [
|
||||||
|
{
|
||||||
|
"kind": "contains_display_name"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"actions": [
|
||||||
|
"notify",
|
||||||
|
{
|
||||||
|
"set_tweak": "sound",
|
||||||
|
"value": "default"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
``.m.rule.room_one_to_one``
|
||||||
|
Matches any message sent in a room with exactly two members.
|
||||||
|
|
||||||
|
Definition (this rule can only be an ``override`` or ``underride`` rule)::
|
||||||
|
|
||||||
|
{
|
||||||
|
"rule_id": ".m.rule.room_two_members"
|
||||||
|
"conditions": [
|
||||||
|
{
|
||||||
|
"is": "2",
|
||||||
|
"kind": "room_member_count"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"actions": [
|
||||||
|
"notify",
|
||||||
|
{
|
||||||
|
"set_tweak": "sound",
|
||||||
|
"value": "default"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
``.m.rule.suppress_notices``
|
||||||
|
Matches messages with a ``msgtype`` of ``notice``. This should be an
|
||||||
|
``override`` rule so that it takes priority over ``content`` / ``sender`` /
|
||||||
|
``room`` rules.
|
||||||
|
|
||||||
|
Definition::
|
||||||
|
|
||||||
|
{
|
||||||
|
'rule_id': '.m.rule.suppress_notices',
|
||||||
|
'conditions': [
|
||||||
|
{
|
||||||
|
'kind': 'event_match',
|
||||||
|
'key': 'content.msgtype',
|
||||||
|
'pattern': 'm.notice',
|
||||||
|
}
|
||||||
|
],
|
||||||
|
'actions': [
|
||||||
|
'dont-notify',
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
``.m.rule.fallback``
|
||||||
|
Matches any message. Used to define the behaviour of messages that match no
|
||||||
|
other rules. If homeservers define this it should be the lowest priority
|
||||||
|
``underride`` rule.
|
||||||
|
|
||||||
|
Definition::
|
||||||
|
|
||||||
|
{
|
||||||
|
"rule_id": ".m.rule.fallback"
|
||||||
|
"conditions": [],
|
||||||
|
"actions": [
|
||||||
|
"notify"
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Conditions
|
||||||
|
++++++++++
|
||||||
|
|
||||||
|
Override, Underride and Default Rules MAY have a list of 'conditions'.
|
||||||
|
All conditions must hold true for an event in order to apply the ``action`` for
|
||||||
|
the event. A rule with no conditions always matches. Room, Sender, User and
|
||||||
|
Content rules do not have conditions in the same way, but instead have
|
||||||
|
predefined conditions. These conditions can be configured using the parameters
|
||||||
|
outlined below. In the cases of room and sender rules, the ``rule_id`` of the
|
||||||
|
rule determines its behaviour. The following conditions are defined:
|
||||||
|
|
||||||
|
``event_match``
|
||||||
|
This is a glob pattern match on a field of the event. Parameters:
|
||||||
|
* ``key``: The dot-separated field of the event to match, e.g. ``content.body``
|
||||||
|
* ``pattern``: The glob-style pattern to match against. Patterns with no
|
||||||
|
special glob characters should be treated as having asterisks
|
||||||
|
prepended and appended when testing the condition.
|
||||||
|
|
||||||
|
``profile_tag``
|
||||||
|
Matches the ``profile_tag`` of the device that the notification would be
|
||||||
|
delivered to. Parameters:
|
||||||
|
|
||||||
|
* ``profile_tag``: The profile_tag to match with.
|
||||||
|
|
||||||
|
``contains_display_name``
|
||||||
|
This matches unencrypted messages where ``content.body`` contains the owner's
|
||||||
|
display name in that room. This is a separate rule because display names may
|
||||||
|
change and as such it would be hard to maintain a rule that matched the user's
|
||||||
|
display name. This condition has no parameters.
|
||||||
|
|
||||||
|
``room_member_count``
|
||||||
|
This matches the current number of members in the room. Parameters:
|
||||||
|
* ``is``: A decimal integer optionally prefixed by one of, ``==``, ``<``,
|
||||||
|
``>``, ``>=`` or ``<=``. A prefix of ``<`` matches rooms where the member
|
||||||
|
count is strictly less than the given number and so forth. If no prefix is
|
||||||
|
present, this parameter defaults to ``==``.
|
||||||
|
|
||||||
|
Push Rules: API
|
||||||
|
~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Clients can retrieve, add, modify and remove push rules globally or per-device
|
||||||
|
using the APIs below.
|
||||||
|
|
||||||
|
{{pushrules_http_api}}
|
||||||
|
|
||||||
|
Examples
|
||||||
|
++++++++
|
||||||
|
|
||||||
|
To create a rule that suppresses notifications for the room with ID
|
||||||
|
``!dj234r78wl45Gh4D:matrix.org``::
|
||||||
|
|
||||||
|
curl -X PUT -H "Content-Type: application/json" "http://localhost:8008/_matrix/client/api/v1/pushrules/global/room/%21dj234r78wl45Gh4D%3Amatrix.org?access_token=123456" -d \
|
||||||
|
'{
|
||||||
|
"actions" : ["dont_notify"]
|
||||||
|
}'
|
||||||
|
|
||||||
|
To suppress notifications for the user ``@spambot:matrix.org``::
|
||||||
|
|
||||||
|
curl -X PUT -H "Content-Type: application/json" "http://localhost:8008/_matrix/client/api/v1/pushrules/global/sender/%40spambot%3Amatrix.org?access_token=123456" -d \
|
||||||
|
'{
|
||||||
|
"actions" : ["dont_notify"]
|
||||||
|
}'
|
||||||
|
|
||||||
|
To always notify for messages that contain the work 'cake' and set a specific
|
||||||
|
sound (with a rule_id of ``SSByZWFsbHkgbGlrZSBjYWtl``)::
|
||||||
|
|
||||||
|
curl -X PUT -H "Content-Type: application/json" "http://localhost:8008/_matrix/client/api/v1/pushrules/global/content/SSByZWFsbHkgbGlrZSBjYWtl?access_token=123456" -d \
|
||||||
|
'{
|
||||||
|
"pattern": "cake",
|
||||||
|
"actions" : ["notify", {"set_sound":"cakealarm.wav"}]
|
||||||
|
}'
|
||||||
|
|
||||||
|
To add a rule suppressing notifications for messages starting with 'cake' but
|
||||||
|
ending with 'lie', superseding the previous rule::
|
||||||
|
|
||||||
|
curl -X PUT -H "Content-Type: application/json" "http://localhost:8008/_matrix/client/api/v1/pushrules/global/content/U3BvbmdlIGNha2UgaXMgYmVzdA?access_token=123456&before=SSByZWFsbHkgbGlrZSBjYWtl" -d \
|
||||||
|
'{
|
||||||
|
"pattern": "cake*lie",
|
||||||
|
"actions" : ["notify"]
|
||||||
|
}'
|
||||||
|
|
||||||
|
To add a custom sound for notifications messages containing the word 'beer' in
|
||||||
|
any rooms with 10 members or fewer (with greater importance than the room,
|
||||||
|
sender and content rules)::
|
||||||
|
|
||||||
|
curl -X PUT -H "Content-Type: application/json" "http://localhost:8008/_matrix/client/api/v1/pushrules/global/override/U2VlIHlvdSBpbiBUaGUgRHVrZQ?access_token=123456" -d \
|
||||||
|
'{
|
||||||
|
"conditions": [
|
||||||
|
{"kind": "event_match", "key": "content.body", "pattern": "beer" },
|
||||||
|
{"kind": "room_member_count", "is": "<=10"}
|
||||||
|
],
|
||||||
|
"actions" : [
|
||||||
|
"notify",
|
||||||
|
{"set_sound":"beeroclock.wav"}
|
||||||
|
]
|
||||||
|
}'
|
||||||
|
|
||||||
|
Server behaviour
|
||||||
|
----------------
|
||||||
|
|
||||||
|
This describes the format used by "HTTP" pushers to send notifications of
|
||||||
|
events to Push Gateways. If the endpoint returns an HTTP error code, the
|
||||||
|
homeserver SHOULD retry for a reasonable amount of time using exponential-backoff.
|
||||||
|
|
||||||
|
{{push_notifier_http_api}}
|
||||||
|
|
||||||
|
Push Gateway behaviour
|
||||||
|
----------------------
|
||||||
|
|
||||||
|
Recommendations for APNS
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
The exact format for sending APNS notifications is flexible and up to the
|
||||||
|
client app and its' push gateway to agree on. As APNS requires that the sender
|
||||||
|
has a private key owned by the app developer, each app must have its own push
|
||||||
|
gateway. It is recommended that:
|
||||||
|
|
||||||
|
* The APNS token be base64 encoded and used as the pushkey.
|
||||||
|
* A different app_id be used for apps on the production and sandbox
|
||||||
|
APS environments.
|
||||||
|
* APNS push gateways do not attempt to wait for errors from the APNS
|
||||||
|
gateway before returning and instead to store failures and return
|
||||||
|
'rejected' responses next time that pushkey is used.
|
||||||
|
|
||||||
|
Security considerations
|
||||||
|
-----------------------
|
||||||
|
|
||||||
|
Clients specify the Push Gateway URL to use to send event notifications to. This
|
||||||
|
URL should be over HTTPS and *never* over HTTP.
|
||||||
|
|
||||||
|
As push notifications will pass through a Push Provider, message content
|
||||||
|
shouldn't be sent in the push itself where possible. Instead, Push Gateways
|
||||||
|
should send a "sync" command to instruct the client to get new events from the
|
||||||
|
homeserver directly.
|
||||||
|
|
@ -1,420 +0,0 @@
|
|||||||
Pushers HTTP API
|
|
||||||
----------------
|
|
||||||
|
|
||||||
To receive any notification pokes at all, it is necessary to configure a
|
|
||||||
'pusher' on the Home Server that you wish to receive notifications from. There
|
|
||||||
is a single API endpoint for this::
|
|
||||||
|
|
||||||
POST $PREFIX/pushers/set
|
|
||||||
|
|
||||||
This takes a JSON object with the following keys:
|
|
||||||
|
|
||||||
pushkey
|
|
||||||
This is a unique identifier for this pusher. The value you should use for this
|
|
||||||
is the routing or destination address information for the notification, for
|
|
||||||
example, the APNS token for APNS or the Registration ID for GCM. If your
|
|
||||||
notification client has no such concept, use any unique identifier. Max length,
|
|
||||||
512 bytes.
|
|
||||||
kind
|
|
||||||
The kind of pusher to configure. 'http' makes a pusher that sends HTTP pokes.
|
|
||||||
null deletes the pusher.
|
|
||||||
profile_tag
|
|
||||||
This is a string that determines what set of device rules will be matched when
|
|
||||||
evaluating push rules for this pusher. It is an arbitrary string. Multiple
|
|
||||||
devices maybe use the same profile_tag. It is advised that when an app's
|
|
||||||
data is copied or restored to a different device, this value remain the same.
|
|
||||||
Client apps should offer ways to change the profile_tag, optionally copying
|
|
||||||
rules from the old profile tag. Max length, 32 bytes.
|
|
||||||
app_id
|
|
||||||
appId is a reverse-DNS style identifier for the application. It is recommended
|
|
||||||
that this end with the platform, such that different platform versions get
|
|
||||||
different app identifiers. Max length, 64 chars.
|
|
||||||
app_display_name
|
|
||||||
A string that will allow the user to identify what application owns this
|
|
||||||
pusher.
|
|
||||||
device_display_name
|
|
||||||
A string that will allow the user to identify what device owns this pusher.
|
|
||||||
lang
|
|
||||||
The preferred language for receiving notifications (eg, 'en' or 'en-US')
|
|
||||||
data
|
|
||||||
A dictionary of information for the pusher implementation itself. For HTTP
|
|
||||||
pushers, this must contain a 'url' key which is a string of the URL that
|
|
||||||
should be used to send notifications.
|
|
||||||
append
|
|
||||||
If this is set to boolean true, the Home Server should add another pusher
|
|
||||||
with the given pushkey and App ID in addition to any others with different
|
|
||||||
user IDs. Otherwise, the Home Server must remove any other pushers with the
|
|
||||||
same App ID and pushkey for different users. The default is false.
|
|
||||||
|
|
||||||
If the pusher was created successfully, a JSON dictionary is returned (which may
|
|
||||||
be empty).
|
|
||||||
|
|
||||||
|
|
||||||
Push Rules
|
|
||||||
----------
|
|
||||||
Home Servers have an interface to configure what events trigger notifications.
|
|
||||||
This behaviour is configured through 'Push Rules'. Push Rules come in a variety
|
|
||||||
of different kinds and each kind of rule has an associated priority. The
|
|
||||||
different kinds of rule, in descending order of priority, are:
|
|
||||||
|
|
||||||
Override Rules
|
|
||||||
The highest priority rules are user-configured overrides.
|
|
||||||
Content Rules
|
|
||||||
These configure behaviour for (unencrypted) messages that match certain
|
|
||||||
patterns. Content rules take one parameter, 'pattern', that gives the pattern
|
|
||||||
to match against. This is treated in the same way as pattern for event_match
|
|
||||||
conditions, below.
|
|
||||||
Room Rules
|
|
||||||
These change the behaviour of all messages to a given room. The rule_id of a
|
|
||||||
room rule is always the ID of the room that it affects.
|
|
||||||
Sender
|
|
||||||
These rules configure notification behaviour for messages from a specific,
|
|
||||||
named Matrix user ID. The rule_id of Sender rules is always the Matrix user
|
|
||||||
ID of the user whose messages they'd apply to.
|
|
||||||
Underride
|
|
||||||
These are identical to override rules, but have a lower priority than content,
|
|
||||||
room and sender rules.
|
|
||||||
|
|
||||||
In addition, each kind of rule may be either global or device-specific. Device
|
|
||||||
specific rules only affect delivery of notifications via pushers with a matching
|
|
||||||
profile_tag. All device-specific rules are higher priority than all global
|
|
||||||
rules. Thusly, the full list of rule kinds, in descending priority order, is as
|
|
||||||
follows:
|
|
||||||
|
|
||||||
* Device-specific Override
|
|
||||||
* Device-specific Content
|
|
||||||
* Device-specific Room
|
|
||||||
* Device-specific Sender
|
|
||||||
* Device-specific Underride
|
|
||||||
* Global Override
|
|
||||||
* Global Content
|
|
||||||
* Global Room
|
|
||||||
* Global Sender
|
|
||||||
* Global Underride
|
|
||||||
|
|
||||||
For some kinds of rule, rules of the same kind also have an ordering with
|
|
||||||
respect to one another. The kinds that do not are room and sender rules where
|
|
||||||
the rules are mutually exclusive by definition and therefore an ordering would
|
|
||||||
be redundant. Actions for the highest priority rule and only that rule apply
|
|
||||||
(for example, a set_tweak action in a lower priority rule will not apply if a
|
|
||||||
higher priority rule matches, even if that rule does not specify any tweaks).
|
|
||||||
|
|
||||||
Rules also have an identifier, ``rule_id``, which is a string. The ``rule_id``
|
|
||||||
is unique within the kind of rule and scope: ``rule_ids`` need not be unique
|
|
||||||
between rules of the same kind on different devices. A home server may also have
|
|
||||||
server default rules of each kind and in each scope. Server default rules are
|
|
||||||
lower priority than user-defined rules in each scope. Server default rules (and
|
|
||||||
only server default rules) begin with a dot ('.') character. In addition, all
|
|
||||||
rules may be enabled or disabled. Disabled rules never match.
|
|
||||||
|
|
||||||
If no rules match an event, the Home Server should not notify for the message
|
|
||||||
(that is to say, the default action is "dont-notify"). Events that the user sent
|
|
||||||
themselves are never alerted for.
|
|
||||||
|
|
||||||
Predefined Rules
|
|
||||||
----------------
|
|
||||||
Matrix specifies the following rule IDs for server default rules. Home Servers
|
|
||||||
may define rules as follows with the given IDs. If Home Servers provide rules
|
|
||||||
with these IDs, their semantics should match those given below:
|
|
||||||
|
|
||||||
.m.rule.contains_user_name
|
|
||||||
Matches any message whose content is unencrypted and contains the local part
|
|
||||||
of the user's Matrix ID, separated by word boundaries.
|
|
||||||
|
|
||||||
Definition (as a content rule)::
|
|
||||||
|
|
||||||
{
|
|
||||||
"rule_id": ".m.rule.contains_user_name"
|
|
||||||
"pattern": "[the local part of the user's Matrix ID]",
|
|
||||||
"actions": [
|
|
||||||
"notify",
|
|
||||||
{
|
|
||||||
"set_tweak": "sound",
|
|
||||||
"value": "default"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
}
|
|
||||||
|
|
||||||
.m.rule.contains_display_name
|
|
||||||
Matches any message whose content is unencrypted and contains the user's
|
|
||||||
current display name in the room in which it was sent.
|
|
||||||
|
|
||||||
Definition (this rule can only be an override or underride rule)::
|
|
||||||
|
|
||||||
{
|
|
||||||
"rule_id": ".m.rule.contains_display_name"
|
|
||||||
"conditions": [
|
|
||||||
{
|
|
||||||
"kind": "contains_display_name"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"actions": [
|
|
||||||
"notify",
|
|
||||||
{
|
|
||||||
"set_tweak": "sound",
|
|
||||||
"value": "default"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
}
|
|
||||||
|
|
||||||
.m.rule.room_one_to_one
|
|
||||||
Matches any message sent in a room with exactly two members.
|
|
||||||
|
|
||||||
Definition (this rule can only be an override or underride rule)::
|
|
||||||
|
|
||||||
{
|
|
||||||
"rule_id": ".m.rule.room_two_members"
|
|
||||||
"conditions": [
|
|
||||||
{
|
|
||||||
"is": "2",
|
|
||||||
"kind": "room_member_count"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"actions": [
|
|
||||||
"notify",
|
|
||||||
{
|
|
||||||
"set_tweak": "sound",
|
|
||||||
"value": "default"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
}
|
|
||||||
|
|
||||||
.m.rule.suppress_notices
|
|
||||||
Matches messages with 'msgtype' of 'notice'. This should be an override rule
|
|
||||||
such that, when enabled, it takes priority over content / sender / room rules.
|
|
||||||
|
|
||||||
Definition::
|
|
||||||
|
|
||||||
{
|
|
||||||
'rule_id': '.m.rule.suppress_notices',
|
|
||||||
'conditions': [
|
|
||||||
{
|
|
||||||
'kind': 'event_match',
|
|
||||||
'key': 'content.msgtype',
|
|
||||||
'pattern': 'm.notice',
|
|
||||||
}
|
|
||||||
],
|
|
||||||
'actions': [
|
|
||||||
'dont-notify',
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
.m.rule.fallback
|
|
||||||
Matches any message. Used to define the behaviour of messages that match no
|
|
||||||
other rules. Therefore, if Home Servers define this, it should be the lowest
|
|
||||||
priority underride rule.
|
|
||||||
|
|
||||||
Definition::
|
|
||||||
|
|
||||||
{
|
|
||||||
"rule_id": ".m.rule.fallback"
|
|
||||||
"conditions": [],
|
|
||||||
"actions": [
|
|
||||||
"notify"
|
|
||||||
],
|
|
||||||
}
|
|
||||||
|
|
||||||
Push Rules: Actions:
|
|
||||||
--------------------
|
|
||||||
All rules have an associated list of 'actions'. An action affects if and how a
|
|
||||||
notification is delivered for a matching event. This standard defines the
|
|
||||||
following actions, although if Home servers wish to support more, they are free
|
|
||||||
to do so:
|
|
||||||
|
|
||||||
notify
|
|
||||||
This causes each matching event to generate a notification.
|
|
||||||
dont_notify
|
|
||||||
Prevents this event from generating a notification
|
|
||||||
coalesce
|
|
||||||
This enables notifications for matching events but activates Home Server
|
|
||||||
specific behaviour to intelligently coalesce multiple events into a single
|
|
||||||
notification. Not all Home Servers may support this. Those that do not should
|
|
||||||
treat it as the 'notify' action.
|
|
||||||
set_tweak
|
|
||||||
Sets an entry in the 'tweaks' dictionary key that is sent in the notification
|
|
||||||
poke. This takes the form of a dictionary with a 'set_tweak' key whose value
|
|
||||||
is the name of the tweak to set. It may also have a 'value' key which is
|
|
||||||
the value to which it should be set.
|
|
||||||
|
|
||||||
Actions that have no parameters are represented as a string. Otherwise, they are
|
|
||||||
represented as a dictionary with a key equal to their name and other keys as
|
|
||||||
their parameters, e.g. ``{ "set_tweak": "sound", "value": "default" }``
|
|
||||||
|
|
||||||
Push Rules: Actions: Tweaks
|
|
||||||
---------------------------
|
|
||||||
The ``set_tweak`` key action is used to add an entry to the 'tweaks' dictionary
|
|
||||||
that is sent in the notification poke. The following tweaks are defined:
|
|
||||||
|
|
||||||
sound
|
|
||||||
A sound to be played when this notification arrives. 'default' means to
|
|
||||||
play a default sound.
|
|
||||||
highlight
|
|
||||||
Whether or not this message should be highlighted in the UI. This will
|
|
||||||
normally take the form of presenting the message in a different colour and/or
|
|
||||||
weight. The UI might also be adjusted to draw particular attention to the room
|
|
||||||
in which the event occurred. The value may be omitted from the highlight
|
|
||||||
tweak, in which case it should be read as if it had a value of true.
|
|
||||||
|
|
||||||
Tweaks are passed transparently through the Home Server so client applications
|
|
||||||
and push gateways may agree on additional tweaks, for example, how to flash the
|
|
||||||
notification light on a mobile device.
|
|
||||||
|
|
||||||
If a kind of tweak that a client understands is not specified in an action, the
|
|
||||||
client may choose a sensible behaviour for the tweak.
|
|
||||||
|
|
||||||
Push Rules: Conditions
|
|
||||||
----------------------
|
|
||||||
Override, Underride and Default rules have a list of 'conditions'. All
|
|
||||||
conditions must hold true for an event in order for a rule to be applied to an
|
|
||||||
event. A rule with no conditions always matches. Matrix specifies the following
|
|
||||||
conditions, although if Home Servers wish to support others, they are free to
|
|
||||||
do so:
|
|
||||||
|
|
||||||
event_match
|
|
||||||
This is a glob pattern match on a field of the event. Parameters:
|
|
||||||
* 'key': The dot-separated field of the event to match, e.g. content.body
|
|
||||||
* 'pattern': The glob-style pattern to match against. Patterns with no
|
|
||||||
special glob characters should be treated as having asterisks
|
|
||||||
prepended and appended when testing the condition.
|
|
||||||
profile_tag
|
|
||||||
Matches the profile_tag of the device that the notification would be
|
|
||||||
delivered to. Parameters:
|
|
||||||
|
|
||||||
* 'profile_tag': The profile_tag to match with.
|
|
||||||
contains_display_name
|
|
||||||
This matches unencrypted messages where content.body contains the owner's
|
|
||||||
display name in that room. This is a separate rule because display names may
|
|
||||||
change and as such it would be hard to maintain a rule that matched the user's
|
|
||||||
display name. This condition has no parameters.
|
|
||||||
room_member_count
|
|
||||||
This matches the current number of members in the room.
|
|
||||||
* 'is': A decimal integer optionally prefixed by one of, '==', '<', '>',
|
|
||||||
'>=' or '<='. A prefix of '<' matches rooms where the member count is
|
|
||||||
strictly less than the given number and so forth. If no prefix is present,
|
|
||||||
this matches rooms where the member count is exactly equal to the given
|
|
||||||
number (i.e. the same as '==').
|
|
||||||
|
|
||||||
Room, Sender, User and Content rules do not have conditions in the same way,
|
|
||||||
but instead have predefined conditions, the behaviour of which can be configured
|
|
||||||
using parameters named as described above. In the cases of room and sender
|
|
||||||
rules, the rule_id of the rule determines its behaviour.
|
|
||||||
|
|
||||||
Push Rules: API
|
|
||||||
---------------
|
|
||||||
Rules live under a hierarchy in the REST API that resembles::
|
|
||||||
|
|
||||||
$PREFIX/pushrules/<scope>/<kind>/<rule_id>
|
|
||||||
|
|
||||||
The component parts are as follows:
|
|
||||||
|
|
||||||
scope
|
|
||||||
Either 'global' or 'device/<profile_tag>' to specify global rules or
|
|
||||||
device rules for the given profile_tag.
|
|
||||||
kind
|
|
||||||
The kind of rule, i.e. 'override', 'underride', 'sender', 'room', 'content'.
|
|
||||||
rule_id
|
|
||||||
The identifier for the rule.
|
|
||||||
|
|
||||||
To add or change a rule, a client performs a PUT request to the appropriate URL.
|
|
||||||
When adding rules of a type that has an ordering, the client can add parameters
|
|
||||||
that define the priority of the rule:
|
|
||||||
|
|
||||||
before
|
|
||||||
Use 'before' with a rule_id as its value to make the new rule the next-more
|
|
||||||
important rule with respect to the given rule.
|
|
||||||
after
|
|
||||||
This makes the new rule the next-less important rule relative to the given
|
|
||||||
rule.
|
|
||||||
|
|
||||||
All requests to the push rules API also require an access_token as a query
|
|
||||||
parameter.
|
|
||||||
|
|
||||||
The content of the PUT request is a JSON object with a list of actions under the
|
|
||||||
'actions' key and either conditions (under the 'conditions' key) or the
|
|
||||||
appropriate parameters for the rule (under the appropriate key name).
|
|
||||||
|
|
||||||
Examples:
|
|
||||||
|
|
||||||
To create a rule that suppresses notifications for the room with ID '!dj234r78wl45Gh4D:matrix.org'::
|
|
||||||
|
|
||||||
curl -X PUT -H "Content-Type: application/json" -d '{ "actions" : ["dont_notify"] }' "http://localhost:8008/_matrix/client/api/v1/pushrules/global/room/%21dj234r78wl45Gh4D%3Amatrix.org?access_token=123456"
|
|
||||||
|
|
||||||
To suppress notifications for the user '@spambot:matrix.org'::
|
|
||||||
|
|
||||||
curl -X PUT -H "Content-Type: application/json" -d '{ "actions" : ["dont_notify"] }' "http://localhost:8008/_matrix/client/api/v1/pushrules/global/sender/%40spambot%3Amatrix.org?access_token=123456"
|
|
||||||
|
|
||||||
To always notify for messages that contain the work 'cake' and set a specific sound (with a rule_id of 'SSByZWFsbHkgbGlrZSBjYWtl')::
|
|
||||||
|
|
||||||
curl -X PUT -H "Content-Type: application/json" -d '{ "pattern": "cake", "actions" : ["notify", {"set_sound":"cakealarm.wav"}] }' "http://localhost:8008/_matrix/client/api/v1/pushrules/global/content/SSByZWFsbHkgbGlrZSBjYWtl?access_token=123456"
|
|
||||||
|
|
||||||
To add a rule suppressing notifications for messages starting with 'cake' but ending with 'lie', superseeding the previous rule::
|
|
||||||
|
|
||||||
curl -X PUT -H "Content-Type: application/json" -d '{ "pattern": "cake*lie", "actions" : ["notify"] }' "http://localhost:8008/_matrix/client/api/v1/pushrules/global/content/U3BvbmdlIGNha2UgaXMgYmVzdA?access_token=123456&before=SSByZWFsbHkgbGlrZSBjYWtl"
|
|
||||||
|
|
||||||
To add a custom sound for notifications messages containing the word 'beer' in any rooms with 10 members or fewer (with greater importance than the room, sender and content rules)::
|
|
||||||
|
|
||||||
curl -X PUT -H "Content-Type: application/json" -d '{ "conditions": [{"kind": "event_match", "key": "content.body", "pattern": "beer" }, {"kind": "room_member_count", "is": "<=10"}], "actions" : ["notify", {"set_sound":"beeroclock.wav"}] }' "http://localhost:8008/_matrix/client/api/v1/pushrules/global/override/U2VlIHlvdSBpbiBUaGUgRHVrZQ?access_token=123456
|
|
||||||
|
|
||||||
|
|
||||||
To delete rules, a client would just make a DELETE request to the same URL::
|
|
||||||
|
|
||||||
curl -X DELETE "http://localhost:8008/_matrix/client/api/v1/pushrules/global/room/%23spam%3Amatrix.org?access_token=123456"
|
|
||||||
|
|
||||||
|
|
||||||
Retrieving the current ruleset can be done either by fetching individual rules
|
|
||||||
using the scheme as specified above. This returns the rule in the same format as
|
|
||||||
would be given in the PUT API with the addition of a rule_id::
|
|
||||||
|
|
||||||
curl "http://localhost:8008/_matrix/client/api/v1/pushrules/global/room/%23spam%3Amatrix.org?access_token=123456"
|
|
||||||
|
|
||||||
Returns::
|
|
||||||
|
|
||||||
{
|
|
||||||
"actions": [
|
|
||||||
"dont_notify"
|
|
||||||
],
|
|
||||||
"rule_id": "#spam:matrix.org",
|
|
||||||
"enabled": true
|
|
||||||
}
|
|
||||||
|
|
||||||
Clients can also fetch broader sets of rules by removing path components.
|
|
||||||
Requesting the root level returns a structure as follows::
|
|
||||||
|
|
||||||
{
|
|
||||||
"device": {
|
|
||||||
"exampledevice": {
|
|
||||||
"content": [],
|
|
||||||
"override": [],
|
|
||||||
"room": [
|
|
||||||
{
|
|
||||||
"actions": [
|
|
||||||
"dont_notify"
|
|
||||||
],
|
|
||||||
"rule_id": "#spam:matrix.org",
|
|
||||||
"enabled", true
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"sender": [],
|
|
||||||
"underride": []
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"global": {
|
|
||||||
"content": [],
|
|
||||||
"override": [],
|
|
||||||
"room": [],
|
|
||||||
"sender": [],
|
|
||||||
"underride": []
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Adding patch components to the request drills down into this structure to filter
|
|
||||||
to only the requested set of rules.
|
|
||||||
|
|
||||||
Enabling and Disabling Rules
|
|
||||||
----------------------------
|
|
||||||
Rules can be enabled or disabled with a PUT operation to the 'enabled' component
|
|
||||||
beneath the rule's URI with a content of 'true' or 'false'::
|
|
||||||
|
|
||||||
curl -X PUT -H "Content-Type: application/json" -d 'false' "http://localhost:8008/_matrix/client/api/v1/pushrules/global/sender/%40spambot%3Amatrix.org/enabled?access_token=123456"
|
|
||||||
|
|
||||||
|
|
@ -1,80 +0,0 @@
|
|||||||
Push Notifications
|
|
||||||
==================
|
|
||||||
|
|
||||||
.. _module:push:
|
|
||||||
|
|
||||||
Overview
|
|
||||||
--------
|
|
||||||
|
|
||||||
::
|
|
||||||
|
|
||||||
+--------------------+ +-------------------+
|
|
||||||
Matrix HTTP | | | |
|
|
||||||
Notification Protocol | App Developer | | Device Vendor |
|
|
||||||
| | | |
|
|
||||||
+-------------------+ | +----------------+ | | +---------------+ |
|
|
||||||
| | | | | | | | | |
|
|
||||||
| Matrix Home Server+-----> Push Gateway | +---> Push Provider | |
|
|
||||||
| | | | | | | | | |
|
|
||||||
+-^-----------------+ | +----------------+ | | +----+----------+ |
|
|
||||||
| | | | | |
|
|
||||||
Matrix | | | | | |
|
|
||||||
Client/Server API + | | | | |
|
|
||||||
| | +--------------------+ +-------------------+
|
|
||||||
| +--+-+ |
|
|
||||||
| | <------------------------------------------+
|
|
||||||
+---+ |
|
|
||||||
| | Provider Push Protocol
|
|
||||||
+----+
|
|
||||||
|
|
||||||
Mobile Device or Client
|
|
||||||
|
|
||||||
|
|
||||||
Matrix supports push notifications as a first class citizen. Home Servers send
|
|
||||||
notifications of user events to user-configured HTTP endpoints. User may also
|
|
||||||
configure a number of rules that determine what events generate notifications.
|
|
||||||
These are all stored and managed by the users home server such that settings can
|
|
||||||
be reused between client apps as appropriate.
|
|
||||||
|
|
||||||
The above diagram shows the flow of push notifications being sent to a handset
|
|
||||||
where push notifications are submitted via the handset vendor, such as Apple's
|
|
||||||
APNS or Google's GCM. This happens as follows:
|
|
||||||
|
|
||||||
1. The client app signs in to a Matrix Home Server
|
|
||||||
2. The client app registers with its vendor's Push Notification provider and
|
|
||||||
obtains a routing token of some kind.
|
|
||||||
3. The mobile app, uses the Matrix client/server API to add a 'pusher',
|
|
||||||
providing the URL of a specific Push Gateway which is configured for that
|
|
||||||
application. It also provides the routing token it has acquired from the
|
|
||||||
Push Notification Provider.
|
|
||||||
4. The Home Server starts sending notification HTTP requests to the Push
|
|
||||||
Gateway using the supplied URL. The Push Gateway relays this notification to
|
|
||||||
the Push Notification Provider, passing the routing token along with any
|
|
||||||
necessary private credentials the provider requires to send push
|
|
||||||
notifications.
|
|
||||||
5. The Push Notification provider sends the notification to the device.
|
|
||||||
|
|
||||||
Nomenclature
|
|
||||||
------------
|
|
||||||
|
|
||||||
Pusher
|
|
||||||
A 'pusher' is an activity in the Home Server that manages the sending
|
|
||||||
of HTTP notifications for a single device of a single user.
|
|
||||||
|
|
||||||
Push Rules
|
|
||||||
A push rule is a single rule, configured by a matrix user, that gives
|
|
||||||
instructions to the Home Server about whether an event should be notified
|
|
||||||
about and how given a set of conditions. Matrix clients allow the user to
|
|
||||||
configure these. They create and view them via the Client to Server REST API.
|
|
||||||
|
|
||||||
Push Gateway
|
|
||||||
A push gateway is a server that receives HTTP event notifications from Home
|
|
||||||
Servers and passes them on to a different protocol such as APNS for iOS
|
|
||||||
devices or GCM for Android devices. Matrix.org provides a reference push
|
|
||||||
gateway, 'sygnal'. A client app tells a Home Server what push gateway
|
|
||||||
to send notifications to when it sets up a pusher.
|
|
||||||
|
|
||||||
For information on the client-server API for setting pushers and push rules, see
|
|
||||||
the Client Server API section. For more information on the format of HTTP
|
|
||||||
notifications, see the HTTP Notification Protocol section.
|
|
||||||
|
|
@ -1,144 +0,0 @@
|
|||||||
HTTP Notification Protocol
|
|
||||||
--------------------------
|
|
||||||
|
|
||||||
This describes the format used by "HTTP" pushers to send notifications of
|
|
||||||
events.
|
|
||||||
|
|
||||||
Notifications are sent as HTTP POST requests to the URL configured when the
|
|
||||||
pusher is created, but Matrix strongly recommends that the path should be::
|
|
||||||
|
|
||||||
/_matrix/push/v1/notify
|
|
||||||
|
|
||||||
The body of the POST request is a JSON dictionary. The format
|
|
||||||
is as follows::
|
|
||||||
|
|
||||||
{
|
|
||||||
"notification": {
|
|
||||||
"id": "$3957tyerfgewrf384",
|
|
||||||
"room_id": "!slw48wfj34rtnrf:example.com",
|
|
||||||
"type": "m.room.message",
|
|
||||||
"sender": "@exampleuser:matrix.org",
|
|
||||||
"sender_display_name": "Major Tom",
|
|
||||||
"room_name": "Mission Control",
|
|
||||||
"room_alias": "#exampleroom:matrix.org",
|
|
||||||
"prio": "high",
|
|
||||||
"content": {
|
|
||||||
"msgtype": "m.text",
|
|
||||||
"body": "I'm floating in a most peculiar way."
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"counts": {
|
|
||||||
"unread" : 2,
|
|
||||||
"missed_calls": 1
|
|
||||||
}
|
|
||||||
"devices": [
|
|
||||||
{
|
|
||||||
"app_id": "org.matrix.matrixConsole.ios",
|
|
||||||
"pushkey": "V2h5IG9uIGVhcnRoIGRpZCB5b3UgZGVjb2RlIHRoaXM/",
|
|
||||||
"pushkey_ts": 12345678,
|
|
||||||
"data" : {
|
|
||||||
},
|
|
||||||
"tweaks": {
|
|
||||||
"sound": "bing"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
The contents of this dictionary are defined as follows:
|
|
||||||
|
|
||||||
id
|
|
||||||
An identifier for this notification that may be used to detect duplicate
|
|
||||||
notification requests. This is not necessarily the ID of the event that
|
|
||||||
triggered the notification.
|
|
||||||
room_id
|
|
||||||
The ID of the room in which this event occurred.
|
|
||||||
type
|
|
||||||
The type of the event as in the event's 'type' field.
|
|
||||||
sender
|
|
||||||
The sender of the event as in the corresponding event field.
|
|
||||||
sender_display_name
|
|
||||||
The current display name of the sender in the room in which the event
|
|
||||||
occurred.
|
|
||||||
room_name
|
|
||||||
The name of the room in which the event occurred.
|
|
||||||
room_alias
|
|
||||||
An alias to display for the room in which the event occurred.
|
|
||||||
prio
|
|
||||||
The priority of the notification. Acceptable values are 'high' or 'low. If
|
|
||||||
omitted, 'high' is assumed. This may be used by push gateways to deliver less
|
|
||||||
time-sensitive notifications in a way that will preserve battery power on
|
|
||||||
mobile devices.
|
|
||||||
content
|
|
||||||
The 'content' field from the event, if present. If the event had no content
|
|
||||||
field, this field is omitted.
|
|
||||||
counts
|
|
||||||
This is a dictionary of the current number of unacknowledged communications
|
|
||||||
for the recipient user. Counts whose value is zero are omitted.
|
|
||||||
unread
|
|
||||||
The number of unread messages a user has across all of the rooms they are a
|
|
||||||
member of.
|
|
||||||
missed_calls
|
|
||||||
The number of unacknowledged missed calls a user has across all rooms of
|
|
||||||
which they are a member.
|
|
||||||
device
|
|
||||||
This is an array of devices that the notification should be sent to.
|
|
||||||
app_id
|
|
||||||
The app_id given when the pusher was created.
|
|
||||||
pushkey
|
|
||||||
The pushkey given when the pusher was created.
|
|
||||||
pushkey_ts
|
|
||||||
The unix timestamp (in seconds) when the pushkey was last updated.
|
|
||||||
data
|
|
||||||
A dictionary of additional pusher-specific data. For 'http' pushers, this is
|
|
||||||
the data dictionary passed in at pusher creation minus the 'url' key.
|
|
||||||
tweaks
|
|
||||||
A dictionary of customisations made to the way this notification is to be
|
|
||||||
presented. These are added by push rules.
|
|
||||||
sound
|
|
||||||
Sets the sound file that should be played. 'default' means that a default
|
|
||||||
sound should be played.
|
|
||||||
|
|
||||||
And additional key is defined but only present on member events:
|
|
||||||
|
|
||||||
user_is_target
|
|
||||||
This is true if the user receiving the notification is the subject of a member
|
|
||||||
event (i.e. the state_key of the member event is equal to the user's Matrix
|
|
||||||
ID).
|
|
||||||
|
|
||||||
The recipient of an HTTP notification should respond with an HTTP 2xx response
|
|
||||||
when the notification has been processed. If the endpoint returns an HTTP error
|
|
||||||
code, the Home Server should retry for a reasonable amount of time with a
|
|
||||||
reasonable back-off scheme.
|
|
||||||
|
|
||||||
The endpoint should return a JSON dictionary as follows::
|
|
||||||
|
|
||||||
{
|
|
||||||
"rejected": [ "V2h5IG9uIGVhcnRoIGRpZCB5b3UgZGVjb2RlIHRoaXM/" ]
|
|
||||||
}
|
|
||||||
|
|
||||||
Whose keys are:
|
|
||||||
|
|
||||||
rejected
|
|
||||||
A list of all pushkeys given in the notification request that are not valid.
|
|
||||||
These could have been rejected by an upstream gateway because they have
|
|
||||||
expired or have never been valid. Home Servers must cease sending notification
|
|
||||||
requests for these pushkeys and remove the associated pushers. It may not
|
|
||||||
necessarily be the notification in the request that failed: it could be that
|
|
||||||
a previous notification to the same pushkey failed.
|
|
||||||
|
|
||||||
Push: Recommendations for APNS
|
|
||||||
------------------------------
|
|
||||||
For sending APNS notifications, the exact format is flexible and up to the
|
|
||||||
client app and its push gateway to agree on (since APNS requires that the sender
|
|
||||||
have a private key owned by the app developer, each app must have its own push
|
|
||||||
gateway). However, Matrix strongly recommends:
|
|
||||||
|
|
||||||
* That the APNS token be base64 encoded and used as the pushkey.
|
|
||||||
* That a different app_id be used for apps on the production and sandbox
|
|
||||||
APS environments.
|
|
||||||
* That APNS push gateways do not attempt to wait for errors from the APNS
|
|
||||||
gateway before returning and instead to store failures and return
|
|
||||||
'rejected' responses next time that pushkey is used.
|
|
||||||
|
|
@ -1,16 +0,0 @@
|
|||||||
# Site settings
|
|
||||||
title: Matrix
|
|
||||||
email: webmaster@matrix.org
|
|
||||||
description: > # this means to ignore newlines until "baseurl:"
|
|
||||||
Matrix.org documentation
|
|
||||||
baseurl: "/docs/guides" # the subpath of your site, e.g. /blog/
|
|
||||||
url: "http://matrix.org" # the base hostname & protocol for your site
|
|
||||||
twitter_username: matrixdotorg
|
|
||||||
github_username: matrix-org
|
|
||||||
|
|
||||||
# Build settings
|
|
||||||
markdown: kramdown
|
|
||||||
|
|
||||||
#defaults:
|
|
||||||
#permalink: /:categories/:title.html # can use this when/if we use jekyll for all docs
|
|
||||||
permalink: :title.html
|
|
@ -1,25 +0,0 @@
|
|||||||
<div id="footer"><div id="footerContent">© 2014-2015 Matrix.org</div></div>
|
|
||||||
<script>
|
|
||||||
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
|
|
||||||
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
|
|
||||||
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
|
|
||||||
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
|
|
||||||
|
|
||||||
ga('create', 'UA-54779209-1', 'auto');
|
|
||||||
ga('send', 'pageview');
|
|
||||||
</script>
|
|
||||||
<script type='text/javascript'>
|
|
||||||
/* <![CDATA[ */
|
|
||||||
var tocplus = {"smooth_scroll":"1"};
|
|
||||||
/* ]]> */
|
|
||||||
</script>
|
|
||||||
<script type='text/javascript' src='https://matrix.org/blog/wp-content/plugins/table-of-contents-plus/front.min.js?ver=1509'></script>
|
|
||||||
<script type='text/javascript' src='https://matrix.org/blog/wp-content/themes/Divi-child/js/jquery.fitvids.js?ver=2.2'></script>
|
|
||||||
<script type='text/javascript' src='https://matrix.org/blog/wp-content/themes/Divi-child/js/waypoints.min.js?ver=2.2'></script>
|
|
||||||
<script type='text/javascript' src='https://matrix.org/blog/wp-content/themes/Divi-child/js/jquery.magnific-popup.js?ver=2.2'></script>
|
|
||||||
<script type='text/javascript'>
|
|
||||||
/* <![CDATA[ */
|
|
||||||
var et_custom = {"ajaxurl":"https:\/\/matrix.org\/blog\/wp-admin\/admin-ajax.php","images_uri":"https:\/\/matrix.org\/blog\/wp-content\/themes\/Divi-child\/images","et_load_nonce":"dc61fa2f94","subscription_failed":"Please, check the fields below to make sure you entered the correct information.","fill":"Fill","field":"field","invalid":"Invalid email","captcha":"Captcha","prev":"Prev","next":"Next"};
|
|
||||||
/* ]]> */
|
|
||||||
</script>
|
|
||||||
<script type='text/javascript' src='https://matrix.org/blog/wp-content/themes/Divi-child/js/custom.js?ver=2.2'></script>
|
|
@ -1,31 +0,0 @@
|
|||||||
<meta charset="utf-8">
|
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
||||||
|
|
||||||
<title>{% if page.title %}{{ page.title }}{% else %}{{ site.title }}{% endif %}</title>
|
|
||||||
<meta name="description" content="{% if page.excerpt %}{{ page.excerpt | strip_html | strip_newlines | truncate: 160 }}{% else %}{{ site.description }}{% endif %}">
|
|
||||||
|
|
||||||
<link rel="stylesheet" href="http://matrix.org/site.css">
|
|
||||||
<link rel="stylesheet" href="{{ "/css/main.css" | prepend: site.baseurl }}">
|
|
||||||
|
|
||||||
<link rel="stylesheet" href="{{ "/css/site_overrides.css" | prepend: site.baseurl }}">
|
|
||||||
<link rel="stylesheet" href="{{ "/css/basic.css" | prepend: site.baseurl }}">
|
|
||||||
<link rel="stylesheet" href="{{ "/css/nature.css" | prepend: site.baseurl }}">
|
|
||||||
|
|
||||||
<link rel='stylesheet' id='toc-screen-css' href='https://matrix.org/blog/wp-content/plugins/table-of-contents-plus/screen.min.css?ver=1509' type='text/css' media='all' />
|
|
||||||
<link rel='stylesheet' id='divi-fonts-css' href='https://fonts.googleapis.com/css?family=Open+Sans:300italic,400italic,700italic,800italic,400,300,700,800&subset=latin,latin-ext' type='text/css' media='all' />
|
|
||||||
<link rel='stylesheet' id='divi-style-css' href='https://matrix.org/blog/wp-content/themes/Divi-child/style.css?ver=2.2' type='text/css' media='all' />
|
|
||||||
|
|
||||||
<link rel="canonical" href="{{ page.url | replace:'index.html','' | prepend: site.baseurl | prepend: site.url }}">
|
|
||||||
<link rel="alternate" type="application/rss+xml" title="{{ site.title }}" href="{{ "/feed.xml" | prepend: site.baseurl | prepend: site.url }}" />
|
|
||||||
|
|
||||||
<script>
|
|
||||||
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
|
|
||||||
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
|
|
||||||
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
|
|
||||||
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
|
|
||||||
|
|
||||||
ga('create', 'UA-54779209-1', 'auto');
|
|
||||||
ga('send', 'pageview');
|
|
||||||
</script>
|
|
||||||
|
|
@ -1,33 +0,0 @@
|
|||||||
<header id="main-header" class="et_nav_text_color_dark et-fixed-header">
|
|
||||||
<div class="container clearfix" >
|
|
||||||
<a href="https://matrix.org/blog/">
|
|
||||||
<img src="https://matrix.org/blog/wp-content/uploads/2015/01/logo1.png" alt="Matrix.org" id="logo" />
|
|
||||||
</a>
|
|
||||||
<div id="et-top-navigation">
|
|
||||||
<nav id="top-menu-nav">
|
|
||||||
<ul id="top-menu" class="nav"><li id="menu-item-17" class="menu-item menu-item-type-custom menu-item-object-custom menu-item-17"><a href="http://matrix.org/">Home</a></li>
|
|
||||||
<li id="menu-item-794" class="menu-item menu-item-type-custom menu-item-object-custom menu-item-794"><a href="https://matrix.org/blog/try-matrix-now">Try Matrix Now!</a></li>
|
|
||||||
<li id="menu-item-348" class="menu-item menu-item-type-custom menu-item-object-custom menu-item-348"><a href="/docs/howtos">HOWTOs</a></li>
|
|
||||||
<li id="menu-item-349" class="menu-item menu-item-type-custom menu-item-object-custom menu-item-349"><a href="/docs/spec">Spec</a></li>
|
|
||||||
<li id="menu-item-350" class="menu-item menu-item-type-custom menu-item-object-custom menu-item-350"><a href="/docs/api">APIs</a></li>
|
|
||||||
<li id="menu-item-351" class="menu-item menu-item-type-custom menu-item-object-custom menu-item-351"><a href="/code">Code</a></li>
|
|
||||||
<li id="menu-item-352" class="menu-item menu-item-type-custom menu-item-object-custom menu-item-352"><a href="/jira">JIRA</a></li>
|
|
||||||
<li id="menu-item-347" class="menu-item menu-item-type-post_type menu-item-object-page menu-item-347"><a href="https://matrix.org/blog/faq/">FAQ</a></li>
|
|
||||||
<li id="menu-item-353" class="menu-item menu-item-type-custom menu-item-object-custom menu-item-353"><a href="/blog">Blog</a></li>
|
|
||||||
</ul>
|
|
||||||
</nav>
|
|
||||||
<div id="et_top_search">
|
|
||||||
<span id="et_search_icon"></span>
|
|
||||||
<form role="search" method="get" class="et-search-form et-hidden" action="https://matrix.org/blog/">
|
|
||||||
<input type="search" class="et-search-field" placeholder="Search …" value="" name="s" title="Search for:" />
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
<div id="et_mobile_nav_menu">
|
|
||||||
<a href="#" class="mobile_nav closed">
|
|
||||||
<span class="select_page">Select Page</span>
|
|
||||||
<span class="mobile_menu_bar"></span>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div> <!-- #et-top-navigation -->
|
|
||||||
</div> <!-- .container -->
|
|
||||||
</header> <!-- #main-header -->
|
|
@ -1,23 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
{% include head.html %}
|
|
||||||
|
|
||||||
</head>
|
|
||||||
<body class="blog et_fixed_nav et_cover_background linux et_right_sidebar chrome">
|
|
||||||
|
|
||||||
<div class="page-content" id="page-container" style="padding-top: 78px;">
|
|
||||||
{% include nav.html %}
|
|
||||||
<div id="main-content">
|
|
||||||
|
|
||||||
<div class="wrapper" id="wrapper">
|
|
||||||
<div class="document_foo" id="document">
|
|
||||||
{{ content }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="push"></div>
|
|
||||||
</div>
|
|
||||||
{% include footer.html %}
|
|
||||||
</body>
|
|
||||||
|
|
||||||
</html>
|
|
@ -1,14 +0,0 @@
|
|||||||
---
|
|
||||||
layout: default
|
|
||||||
---
|
|
||||||
<div class="post">
|
|
||||||
|
|
||||||
<header class="post-header">
|
|
||||||
<h1 class="post-title">{{ page.title }}</h1>
|
|
||||||
</header>
|
|
||||||
|
|
||||||
<article class="post-content">
|
|
||||||
{{ content }}
|
|
||||||
</article>
|
|
||||||
|
|
||||||
</div>
|
|
@ -1,6 +0,0 @@
|
|||||||
---
|
|
||||||
layout: default
|
|
||||||
---
|
|
||||||
|
|
||||||
{{ content }}
|
|
||||||
|
|
@ -1 +0,0 @@
|
|||||||
*.pyc
|
|
@ -1,20 +0,0 @@
|
|||||||
The MIT License (MIT)
|
|
||||||
Copyright (c) 2011 Greg Thornton, http://xdissent.com
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in
|
|
||||||
all copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
THE SOFTWARE.
|
|
@ -1,97 +0,0 @@
|
|||||||
Overview
|
|
||||||
========
|
|
||||||
|
|
||||||
This plugin adds `ReStructuredText`_ support to `Jekyll`_ and `Octopress`_.
|
|
||||||
It renders ReST in posts and pages, and provides a custom directive to
|
|
||||||
support Octopress-compatible syntax highlighting.
|
|
||||||
|
|
||||||
Requirements
|
|
||||||
============
|
|
||||||
|
|
||||||
* Jekyll *or* Octopress >= 2.0
|
|
||||||
* Docutils
|
|
||||||
* Pygments
|
|
||||||
* `RbST`_
|
|
||||||
|
|
||||||
Installation
|
|
||||||
============
|
|
||||||
|
|
||||||
1. Install Docutils and Pygments.
|
|
||||||
|
|
||||||
The most convenient way is to use virtualenv_burrito:
|
|
||||||
|
|
||||||
::
|
|
||||||
|
|
||||||
$ curl -s https://raw.github.com/brainsik/virtualenv-burrito/master/virtualenv-burrito.sh | bash
|
|
||||||
$ source /Users/xdissent/.venvburrito/startup.sh
|
|
||||||
$ mkvirtualenv jekyll-rst
|
|
||||||
$ pip install docutils pygments
|
|
||||||
|
|
||||||
2. Install RbST.
|
|
||||||
|
|
||||||
If you use `bundler`_ with Octopress, add ``gem 'RbST'`` to
|
|
||||||
your ``Gemfile`` in the ``development`` group, then run
|
|
||||||
``bundle install``. Otherwise, ``gem install RbST``.
|
|
||||||
|
|
||||||
3. Install the plugin.
|
|
||||||
|
|
||||||
For Jekyll:
|
|
||||||
|
|
||||||
::
|
|
||||||
|
|
||||||
$ cd <jekyll-project-path>
|
|
||||||
$ git submodule add https://github.com/xdissent/jekyll-rst.git _plugins/jekyll-rst
|
|
||||||
|
|
||||||
For Octopress:
|
|
||||||
|
|
||||||
::
|
|
||||||
|
|
||||||
$ cd <octopress-project-path>
|
|
||||||
$ git submodule add https://github.com/xdissent/jekyll-rst.git plugins/jekyll-rst
|
|
||||||
|
|
||||||
4. Start blogging in ReStructuredText. Any file with the ``.rst`` extension
|
|
||||||
will be parsed as ReST and rendered into HTML.
|
|
||||||
|
|
||||||
.. note:: Be sure to activate the ``jekyll-rst`` virtualenv before generating
|
|
||||||
the site by issuing a ``workon jekyll-rst``. I suggest you follow `Harry
|
|
||||||
Marr's advice`_ and create a ``.venv`` file that will automatically
|
|
||||||
activate the ``jekyll-rst`` virtualenv when you ``cd`` into your project.
|
|
||||||
|
|
||||||
Source Code Highlighting
|
|
||||||
========================
|
|
||||||
|
|
||||||
A ``code-block`` ReST directive is registered and aliased as ``sourcecode``.
|
|
||||||
It adds syntax highlighting to code blocks in your documents::
|
|
||||||
|
|
||||||
.. code-block:: ruby
|
|
||||||
|
|
||||||
# Output "I love ReST"
|
|
||||||
say = "I love ReST"
|
|
||||||
puts say
|
|
||||||
|
|
||||||
Optional arguments exist to supply a caption, link, and link title::
|
|
||||||
|
|
||||||
.. code-block:: console
|
|
||||||
:caption: Read Hacker News on a budget
|
|
||||||
:url: http://news.ycombinator.com
|
|
||||||
:title: Hacker News
|
|
||||||
|
|
||||||
$ curl http://news.ycombinator.com | less
|
|
||||||
|
|
||||||
Octopress already includes style sheets for syntax highlighting, but you'll
|
|
||||||
need to generate one yourself if using Jekyll::
|
|
||||||
|
|
||||||
$ pygmentize -S default -f html > css/pygments.css
|
|
||||||
|
|
||||||
Octopress Tips
|
|
||||||
==============
|
|
||||||
|
|
||||||
* Use ``.. more`` in your ReST documents to indicate where Octopress's
|
|
||||||
``excerpt`` tag should split your content for summary views.
|
|
||||||
|
|
||||||
.. _ReStructuredText: http://docutils.sourceforge.net/rst.html
|
|
||||||
.. _Jekyll: http://jekyllrb.com/
|
|
||||||
.. _Octopress: http://octopress.com/
|
|
||||||
.. _RbST: http://rubygems.org/gems/RbST
|
|
||||||
.. _bundler: http://gembundler.com/
|
|
||||||
.. _Harry Marr's advice: http://hmarr.com/2010/jan/19/making-virtualenv-play-nice-with-git/
|
|
@ -1,30 +0,0 @@
|
|||||||
require 'rbst'
|
|
||||||
|
|
||||||
module Jekyll
|
|
||||||
class RestConverter < Converter
|
|
||||||
safe true
|
|
||||||
|
|
||||||
priority :low
|
|
||||||
|
|
||||||
def matches(ext)
|
|
||||||
ext =~ /rst/i
|
|
||||||
end
|
|
||||||
|
|
||||||
def output_ext(ext)
|
|
||||||
".html"
|
|
||||||
end
|
|
||||||
|
|
||||||
def convert(content)
|
|
||||||
RbST.executables = {:html => "#{File.expand_path(File.dirname(__FILE__))}/rst2html.py"}
|
|
||||||
RbST.new(content).to_html(:part => :fragment, :initial_header_level => 2)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
module Filters
|
|
||||||
def restify(input)
|
|
||||||
site = @context.registers[:site]
|
|
||||||
converter = site.getConverterImpl(Jekyll::RestConverter)
|
|
||||||
converter.convert(input)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
@ -1,97 +0,0 @@
|
|||||||
# Define a new directive `code-block` (aliased as `sourcecode`) that uses the
|
|
||||||
# `pygments` source highlighter to render code in color.
|
|
||||||
#
|
|
||||||
# Incorporates code from the `Pygments`_ documentation for `Using Pygments in
|
|
||||||
# ReST documents`_ and `Octopress`_.
|
|
||||||
#
|
|
||||||
# .. _Pygments: http://pygments.org/
|
|
||||||
# .. _Using Pygments in ReST documents: http://pygments.org/docs/rstdirective/
|
|
||||||
# .. _Octopress: http://octopress.org/
|
|
||||||
|
|
||||||
import re
|
|
||||||
import os
|
|
||||||
import md5
|
|
||||||
import __main__
|
|
||||||
|
|
||||||
# Absolute path to pygments cache dir
|
|
||||||
PYGMENTS_CACHE_DIR = os.path.abspath(os.path.join(os.path.dirname(__main__.__file__), '../../.pygments-cache'))
|
|
||||||
|
|
||||||
# Ensure cache dir exists
|
|
||||||
if not os.path.exists(PYGMENTS_CACHE_DIR):
|
|
||||||
os.makedirs(PYGMENTS_CACHE_DIR)
|
|
||||||
|
|
||||||
from pygments.formatters import HtmlFormatter
|
|
||||||
|
|
||||||
from docutils import nodes
|
|
||||||
from docutils.parsers.rst import directives, Directive
|
|
||||||
|
|
||||||
from pygments import highlight
|
|
||||||
from pygments.lexers import get_lexer_by_name, TextLexer
|
|
||||||
|
|
||||||
class Pygments(Directive):
|
|
||||||
""" Source code syntax hightlighting.
|
|
||||||
"""
|
|
||||||
required_arguments = 1
|
|
||||||
optional_arguments = 0
|
|
||||||
final_argument_whitespace = True
|
|
||||||
string_opts = ['title', 'url', 'caption']
|
|
||||||
option_spec = dict([(key, directives.unchanged) for key in string_opts])
|
|
||||||
has_content = True
|
|
||||||
|
|
||||||
def run(self):
|
|
||||||
self.assert_has_content()
|
|
||||||
try:
|
|
||||||
lexer_name = self.arguments[0]
|
|
||||||
lexer = get_lexer_by_name(lexer_name)
|
|
||||||
except ValueError:
|
|
||||||
# no lexer found - use the text one instead of an exception
|
|
||||||
lexer_name = 'text'
|
|
||||||
lexer = TextLexer()
|
|
||||||
formatter = HtmlFormatter()
|
|
||||||
|
|
||||||
# Construct cache filename
|
|
||||||
cache_file = None
|
|
||||||
content_text = u'\n'.join(self.content)
|
|
||||||
cache_file_name = '%s-%s.html' % (lexer_name, md5.new(content_text).hexdigest())
|
|
||||||
cached_path = os.path.join(PYGMENTS_CACHE_DIR, cache_file_name)
|
|
||||||
|
|
||||||
# Look for cached version, otherwise parse
|
|
||||||
if os.path.exists(cached_path):
|
|
||||||
cache_file = open(cached_path, 'r')
|
|
||||||
parsed = cache_file.read()
|
|
||||||
else:
|
|
||||||
parsed = highlight(content_text, lexer, formatter)
|
|
||||||
|
|
||||||
# Strip pre tag and everything outside it
|
|
||||||
pres = re.compile("<pre>(.+)<\/pre>", re.S)
|
|
||||||
stripped = pres.search(parsed).group(1)
|
|
||||||
|
|
||||||
# Create tabular code with line numbers
|
|
||||||
table = '<div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers">'
|
|
||||||
lined = ''
|
|
||||||
for idx, line in enumerate(stripped.splitlines(True)):
|
|
||||||
table += '<span class="line-number">%d</span>\n' % (idx + 1)
|
|
||||||
lined += '<span class="line">%s</span>' % line
|
|
||||||
table += '</pre></td><td class="code"><pre><code class="%s">%s</code></pre></td></tr></table></div>' % (lexer_name, lined)
|
|
||||||
|
|
||||||
# Add wrapper with optional caption and link
|
|
||||||
code = '<figure class="code">'
|
|
||||||
if self.options:
|
|
||||||
caption = ('<span>%s</span>' % self.options['caption']) if 'caption' in self.options else ''
|
|
||||||
title = self.options['title'] if 'title' in self.options else 'link'
|
|
||||||
link = ('<a href="%s">%s</a>' % (self.options['url'], title)) if 'url' in self.options else ''
|
|
||||||
|
|
||||||
if caption or link:
|
|
||||||
code += '<figcaption>%s %s</figcaption>' % (caption, link)
|
|
||||||
code += '%s</figure>' % table
|
|
||||||
|
|
||||||
# Write cache
|
|
||||||
if cache_file is None:
|
|
||||||
cache_file = open(cached_path, 'w')
|
|
||||||
cache_file.write(parsed)
|
|
||||||
cache_file.close()
|
|
||||||
|
|
||||||
return [nodes.raw('', code, format='html')]
|
|
||||||
|
|
||||||
directives.register_directive('code-block', Pygments)
|
|
||||||
directives.register_directive('sourcecode', Pygments)
|
|
@ -1,39 +0,0 @@
|
|||||||
#!/usr/bin/env python
|
|
||||||
|
|
||||||
# :Author: David Goodger, the Pygments team, Guenter Milde
|
|
||||||
# :Date: $Date: $
|
|
||||||
# :Copyright: This module has been placed in the public domain.
|
|
||||||
|
|
||||||
# This is a merge of the `Docutils`_ `rst2html` front end with an extension
|
|
||||||
# suggestion taken from the `Pygments`_ documentation, reworked specifically
|
|
||||||
# for `Octopress`_.
|
|
||||||
#
|
|
||||||
# .. _Pygments: http://pygments.org/
|
|
||||||
# .. _Docutils: http://docutils.sourceforge.net/
|
|
||||||
# .. _Octopress: http://octopress.org/
|
|
||||||
|
|
||||||
"""
|
|
||||||
A front end to docutils, producing HTML with syntax colouring using pygments
|
|
||||||
"""
|
|
||||||
|
|
||||||
try:
|
|
||||||
import locale
|
|
||||||
locale.setlocale(locale.LC_ALL, '')
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
|
|
||||||
from transform import transform
|
|
||||||
from docutils.writers.html4css1 import Writer
|
|
||||||
from docutils.core import default_description
|
|
||||||
from directives import Pygments
|
|
||||||
|
|
||||||
description = ('Generates (X)HTML documents from standalone reStructuredText '
|
|
||||||
'sources. Uses `pygments` to colorize the content of'
|
|
||||||
'"code-block" directives. Needs an adapted stylesheet'
|
|
||||||
+ default_description)
|
|
||||||
|
|
||||||
def main():
|
|
||||||
return transform(writer=Writer(), part='html_body')
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
print(main())
|
|
@ -1,42 +0,0 @@
|
|||||||
import sys
|
|
||||||
from docutils.core import publish_parts
|
|
||||||
from optparse import OptionParser
|
|
||||||
from docutils.frontend import OptionParser as DocutilsOptionParser
|
|
||||||
from docutils.parsers.rst import Parser
|
|
||||||
|
|
||||||
def transform(writer=None, part=None):
|
|
||||||
p = OptionParser(add_help_option=False)
|
|
||||||
|
|
||||||
# Collect all the command line options
|
|
||||||
docutils_parser = DocutilsOptionParser(components=(writer, Parser()))
|
|
||||||
for group in docutils_parser.option_groups:
|
|
||||||
p.add_option_group(group.title, None).add_options(group.option_list)
|
|
||||||
|
|
||||||
p.add_option('--part', default=part)
|
|
||||||
|
|
||||||
opts, args = p.parse_args()
|
|
||||||
|
|
||||||
settings = dict({
|
|
||||||
'file_insertion_enabled': False,
|
|
||||||
'raw_enabled': False,
|
|
||||||
'doctitle_xform': False,
|
|
||||||
})
|
|
||||||
|
|
||||||
if len(args) == 1:
|
|
||||||
try:
|
|
||||||
content = open(args[0], 'r').read()
|
|
||||||
except IOError:
|
|
||||||
content = args[0]
|
|
||||||
else:
|
|
||||||
content = sys.stdin.read()
|
|
||||||
|
|
||||||
parts = publish_parts(
|
|
||||||
source=content,
|
|
||||||
writer=writer,
|
|
||||||
settings_overrides=settings
|
|
||||||
)
|
|
||||||
if 'html_body' in parts:
|
|
||||||
return parts['html_body']
|
|
||||||
if opts.part in parts:
|
|
||||||
return parts[opts.part]
|
|
||||||
return ''
|
|
@ -1,51 +0,0 @@
|
|||||||
module Jekyll
|
|
||||||
class ProjectVersionTag < Liquid::Tag
|
|
||||||
NO_GIT_MESSAGE = 'Oops, are you sure this is a git project?'
|
|
||||||
UNABLE_TO_PARSE_MESSAGE = 'Sorry, could not read project version at the moment'
|
|
||||||
|
|
||||||
def render(context)
|
|
||||||
if git_repo?
|
|
||||||
current_version.chomp
|
|
||||||
else
|
|
||||||
NO_GIT_MESSAGE
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def current_version
|
|
||||||
@_current_version ||= begin
|
|
||||||
# attempt to find the latest tag, falling back to last commit
|
|
||||||
version = git_describe || parse_head
|
|
||||||
|
|
||||||
version || UNABLE_TO_PARSE_MESSAGE
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def git_describe
|
|
||||||
tagged_version = %x{ git describe --tags --always }
|
|
||||||
|
|
||||||
if command_succeeded?
|
|
||||||
tagged_version
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def parse_head
|
|
||||||
head_commitish = %x{ git rev-parse --short HEAD }
|
|
||||||
|
|
||||||
if command_succeeded?
|
|
||||||
head_commitish
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def command_succeeded?
|
|
||||||
!$?.nil? && $?.success?
|
|
||||||
end
|
|
||||||
|
|
||||||
def git_repo?
|
|
||||||
system('git rev-parse')
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
Liquid::Template.register_tag('project_version', Jekyll::ProjectVersionTag)
|
|
@ -1,18 +0,0 @@
|
|||||||
---
|
|
||||||
layout: default
|
|
||||||
---
|
|
||||||
{% include basic.css %}
|
|
||||||
{% include nature.css %}
|
|
||||||
|
|
||||||
<div class="post">
|
|
||||||
|
|
||||||
<header class="post-header">
|
|
||||||
<div class="post-title">{{ page.title }}</div>
|
|
||||||
<p class="post-meta">{{ page.date | date: "%b %-d, %Y" }}{% if page.author %} • {{ page.author }}{% endif %}{% if page.meta %} • {{ page.meta }}{% endif %}</p>
|
|
||||||
</header>
|
|
||||||
|
|
||||||
<article class="post-content">
|
|
||||||
{{ content }}
|
|
||||||
</article>
|
|
||||||
|
|
||||||
</div>
|
|
@ -1,204 +0,0 @@
|
|||||||
/**
|
|
||||||
* Reset some basic elements
|
|
||||||
*/
|
|
||||||
body, h1, h2, h3, h4, h5, h6,
|
|
||||||
p, blockquote, pre, hr,
|
|
||||||
dl, dd, ol, ul, figure {
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Basic styling
|
|
||||||
*/
|
|
||||||
body {
|
|
||||||
font-family: $base-font-family;
|
|
||||||
font-size: $base-font-size;
|
|
||||||
line-height: $base-line-height;
|
|
||||||
font-weight: 300;
|
|
||||||
color: $text-color;
|
|
||||||
background-color: $background-color;
|
|
||||||
-webkit-text-size-adjust: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set `margin-bottom` to maintain vertical rhythm
|
|
||||||
*/
|
|
||||||
h1, h2, h3, h4, h5, h6,
|
|
||||||
p, blockquote, pre,
|
|
||||||
ul, ol, dl, figure,
|
|
||||||
%vertical-rhythm {
|
|
||||||
margin-bottom: $spacing-unit / 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Images
|
|
||||||
*/
|
|
||||||
img {
|
|
||||||
max-width: 100%;
|
|
||||||
vertical-align: middle;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Figures
|
|
||||||
*/
|
|
||||||
figure > img {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
figcaption {
|
|
||||||
font-size: $small-font-size;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Lists
|
|
||||||
*/
|
|
||||||
ul, ol {
|
|
||||||
margin-left: $spacing-unit;
|
|
||||||
}
|
|
||||||
|
|
||||||
li {
|
|
||||||
> ul,
|
|
||||||
> ol {
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Headings
|
|
||||||
*/
|
|
||||||
h1, h2, h3, h4, h5, h6 {
|
|
||||||
font-weight: 300;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Links
|
|
||||||
*/
|
|
||||||
a {
|
|
||||||
color: $brand-color;
|
|
||||||
text-decoration: none;
|
|
||||||
|
|
||||||
&:visited {
|
|
||||||
color: darken($brand-color, 15%);
|
|
||||||
}
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
color: $text-color;
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Blockquotes
|
|
||||||
*/
|
|
||||||
blockquote {
|
|
||||||
color: $grey-color;
|
|
||||||
border-left: 4px solid $grey-color-light;
|
|
||||||
padding-left: $spacing-unit / 2;
|
|
||||||
font-size: 18px;
|
|
||||||
letter-spacing: -1px;
|
|
||||||
font-style: italic;
|
|
||||||
|
|
||||||
> :last-child {
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Code formatting
|
|
||||||
*/
|
|
||||||
pre,
|
|
||||||
code {
|
|
||||||
font-size: 15px;
|
|
||||||
border: 1px solid $grey-color-light;
|
|
||||||
border-radius: 3px;
|
|
||||||
background-color: #eef;
|
|
||||||
}
|
|
||||||
|
|
||||||
code {
|
|
||||||
padding: 1px 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
pre {
|
|
||||||
padding: 8px 12px;
|
|
||||||
overflow-x: scroll;
|
|
||||||
|
|
||||||
> code {
|
|
||||||
border: 0;
|
|
||||||
padding-right: 0;
|
|
||||||
padding-left: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Wrapper
|
|
||||||
*/
|
|
||||||
.wrapper {
|
|
||||||
max-width: -webkit-calc(#{$content-width} - (#{$spacing-unit} * 2));
|
|
||||||
max-width: calc(#{$content-width} - (#{$spacing-unit} * 2));
|
|
||||||
margin-right: auto;
|
|
||||||
margin-left: auto;
|
|
||||||
padding-right: $spacing-unit;
|
|
||||||
padding-left: $spacing-unit;
|
|
||||||
@extend %clearfix;
|
|
||||||
|
|
||||||
@include media-query($on-laptop) {
|
|
||||||
max-width: -webkit-calc(#{$content-width} - (#{$spacing-unit}));
|
|
||||||
max-width: calc(#{$content-width} - (#{$spacing-unit}));
|
|
||||||
padding-right: $spacing-unit / 2;
|
|
||||||
padding-left: $spacing-unit / 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Clearfix
|
|
||||||
*/
|
|
||||||
%clearfix {
|
|
||||||
|
|
||||||
&:after {
|
|
||||||
content: "";
|
|
||||||
display: table;
|
|
||||||
clear: both;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Icons
|
|
||||||
*/
|
|
||||||
.icon {
|
|
||||||
|
|
||||||
> svg {
|
|
||||||
display: inline-block;
|
|
||||||
width: 16px;
|
|
||||||
height: 16px;
|
|
||||||
vertical-align: middle;
|
|
||||||
|
|
||||||
path {
|
|
||||||
fill: $grey-color;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,236 +0,0 @@
|
|||||||
/**
|
|
||||||
* Site header
|
|
||||||
*/
|
|
||||||
.site-header {
|
|
||||||
border-top: 5px solid $grey-color-dark;
|
|
||||||
border-bottom: 1px solid $grey-color-light;
|
|
||||||
min-height: 56px;
|
|
||||||
|
|
||||||
// Positioning context for the mobile navigation icon
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
.site-title {
|
|
||||||
font-size: 26px;
|
|
||||||
line-height: 56px;
|
|
||||||
letter-spacing: -1px;
|
|
||||||
margin-bottom: 0;
|
|
||||||
float: left;
|
|
||||||
|
|
||||||
&,
|
|
||||||
&:visited {
|
|
||||||
color: $grey-color-dark;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.site-nav {
|
|
||||||
float: right;
|
|
||||||
line-height: 56px;
|
|
||||||
|
|
||||||
.menu-icon {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.page-link {
|
|
||||||
color: $text-color;
|
|
||||||
line-height: $base-line-height;
|
|
||||||
|
|
||||||
// Gaps between nav items, but not on the first one
|
|
||||||
&:not(:first-child) {
|
|
||||||
margin-left: 20px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@include media-query($on-palm) {
|
|
||||||
position: absolute;
|
|
||||||
top: 9px;
|
|
||||||
right: 30px;
|
|
||||||
background-color: $background-color;
|
|
||||||
border: 1px solid $grey-color-light;
|
|
||||||
border-radius: 5px;
|
|
||||||
text-align: right;
|
|
||||||
|
|
||||||
.menu-icon {
|
|
||||||
display: block;
|
|
||||||
float: right;
|
|
||||||
width: 36px;
|
|
||||||
height: 26px;
|
|
||||||
line-height: 0;
|
|
||||||
padding-top: 10px;
|
|
||||||
text-align: center;
|
|
||||||
|
|
||||||
> svg {
|
|
||||||
width: 18px;
|
|
||||||
height: 15px;
|
|
||||||
|
|
||||||
path {
|
|
||||||
fill: $grey-color-dark;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.trigger {
|
|
||||||
clear: both;
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:hover .trigger {
|
|
||||||
display: block;
|
|
||||||
padding-bottom: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.page-link {
|
|
||||||
display: block;
|
|
||||||
padding: 5px 10px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Site footer
|
|
||||||
*/
|
|
||||||
.site-footer {
|
|
||||||
border-top: 1px solid $grey-color-light;
|
|
||||||
padding: $spacing-unit 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.footer-heading {
|
|
||||||
font-size: 18px;
|
|
||||||
margin-bottom: $spacing-unit / 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
.contact-list,
|
|
||||||
.social-media-list {
|
|
||||||
list-style: none;
|
|
||||||
margin-left: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.footer-col-wrapper {
|
|
||||||
font-size: 15px;
|
|
||||||
color: $grey-color;
|
|
||||||
margin-left: -$spacing-unit / 2;
|
|
||||||
@extend %clearfix;
|
|
||||||
}
|
|
||||||
|
|
||||||
.footer-col {
|
|
||||||
float: left;
|
|
||||||
margin-bottom: $spacing-unit / 2;
|
|
||||||
padding-left: $spacing-unit / 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
.footer-col-1 {
|
|
||||||
width: -webkit-calc(35% - (#{$spacing-unit} / 2));
|
|
||||||
width: calc(35% - (#{$spacing-unit} / 2));
|
|
||||||
}
|
|
||||||
|
|
||||||
.footer-col-2 {
|
|
||||||
width: -webkit-calc(20% - (#{$spacing-unit} / 2));
|
|
||||||
width: calc(20% - (#{$spacing-unit} / 2));
|
|
||||||
}
|
|
||||||
|
|
||||||
.footer-col-3 {
|
|
||||||
width: -webkit-calc(45% - (#{$spacing-unit} / 2));
|
|
||||||
width: calc(45% - (#{$spacing-unit} / 2));
|
|
||||||
}
|
|
||||||
|
|
||||||
@include media-query($on-laptop) {
|
|
||||||
.footer-col-1,
|
|
||||||
.footer-col-2 {
|
|
||||||
width: -webkit-calc(50% - (#{$spacing-unit} / 2));
|
|
||||||
width: calc(50% - (#{$spacing-unit} / 2));
|
|
||||||
}
|
|
||||||
|
|
||||||
.footer-col-3 {
|
|
||||||
width: -webkit-calc(100% - (#{$spacing-unit} / 2));
|
|
||||||
width: calc(100% - (#{$spacing-unit} / 2));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@include media-query($on-palm) {
|
|
||||||
.footer-col {
|
|
||||||
float: none;
|
|
||||||
width: -webkit-calc(100% - (#{$spacing-unit} / 2));
|
|
||||||
width: calc(100% - (#{$spacing-unit} / 2));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Page content
|
|
||||||
*/
|
|
||||||
.page-content {
|
|
||||||
padding: $spacing-unit 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.page-heading {
|
|
||||||
font-size: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.post-list {
|
|
||||||
margin-left: 0;
|
|
||||||
list-style: none;
|
|
||||||
|
|
||||||
> li {
|
|
||||||
margin-bottom: $spacing-unit;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.post-meta {
|
|
||||||
font-size: $small-font-size;
|
|
||||||
color: $grey-color;
|
|
||||||
}
|
|
||||||
|
|
||||||
.post-link {
|
|
||||||
display: block;
|
|
||||||
font-size: 24px;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Posts
|
|
||||||
*/
|
|
||||||
.post-header {
|
|
||||||
margin-bottom: $spacing-unit;
|
|
||||||
}
|
|
||||||
|
|
||||||
.post-title {
|
|
||||||
font-size: 42px;
|
|
||||||
letter-spacing: -1px;
|
|
||||||
line-height: 1;
|
|
||||||
|
|
||||||
@include media-query($on-laptop) {
|
|
||||||
font-size: 36px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.post-content {
|
|
||||||
margin-bottom: $spacing-unit;
|
|
||||||
|
|
||||||
h2 {
|
|
||||||
font-size: 32px;
|
|
||||||
|
|
||||||
@include media-query($on-laptop) {
|
|
||||||
font-size: 28px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
h3 {
|
|
||||||
font-size: 26px;
|
|
||||||
|
|
||||||
@include media-query($on-laptop) {
|
|
||||||
font-size: 22px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
h4 {
|
|
||||||
font-size: 20px;
|
|
||||||
|
|
||||||
@include media-query($on-laptop) {
|
|
||||||
font-size: 18px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,67 +0,0 @@
|
|||||||
/**
|
|
||||||
* Syntax highlighting styles
|
|
||||||
*/
|
|
||||||
.highlight {
|
|
||||||
background: #fff;
|
|
||||||
@extend %vertical-rhythm;
|
|
||||||
|
|
||||||
.c { color: #998; font-style: italic } // Comment
|
|
||||||
.err { color: #a61717; background-color: #e3d2d2 } // Error
|
|
||||||
.k { font-weight: bold } // Keyword
|
|
||||||
.o { font-weight: bold } // Operator
|
|
||||||
.cm { color: #998; font-style: italic } // Comment.Multiline
|
|
||||||
.cp { color: #999; font-weight: bold } // Comment.Preproc
|
|
||||||
.c1 { color: #998; font-style: italic } // Comment.Single
|
|
||||||
.cs { color: #999; font-weight: bold; font-style: italic } // Comment.Special
|
|
||||||
.gd { color: #000; background-color: #fdd } // Generic.Deleted
|
|
||||||
.gd .x { color: #000; background-color: #faa } // Generic.Deleted.Specific
|
|
||||||
.ge { font-style: italic } // Generic.Emph
|
|
||||||
.gr { color: #a00 } // Generic.Error
|
|
||||||
.gh { color: #999 } // Generic.Heading
|
|
||||||
.gi { color: #000; background-color: #dfd } // Generic.Inserted
|
|
||||||
.gi .x { color: #000; background-color: #afa } // Generic.Inserted.Specific
|
|
||||||
.go { color: #888 } // Generic.Output
|
|
||||||
.gp { color: #555 } // Generic.Prompt
|
|
||||||
.gs { font-weight: bold } // Generic.Strong
|
|
||||||
.gu { color: #aaa } // Generic.Subheading
|
|
||||||
.gt { color: #a00 } // Generic.Traceback
|
|
||||||
.kc { font-weight: bold } // Keyword.Constant
|
|
||||||
.kd { font-weight: bold } // Keyword.Declaration
|
|
||||||
.kp { font-weight: bold } // Keyword.Pseudo
|
|
||||||
.kr { font-weight: bold } // Keyword.Reserved
|
|
||||||
.kt { color: #458; font-weight: bold } // Keyword.Type
|
|
||||||
.m { color: #099 } // Literal.Number
|
|
||||||
.s { color: #d14 } // Literal.String
|
|
||||||
.na { color: #008080 } // Name.Attribute
|
|
||||||
.nb { color: #0086B3 } // Name.Builtin
|
|
||||||
.nc { color: #458; font-weight: bold } // Name.Class
|
|
||||||
.no { color: #008080 } // Name.Constant
|
|
||||||
.ni { color: #800080 } // Name.Entity
|
|
||||||
.ne { color: #900; font-weight: bold } // Name.Exception
|
|
||||||
.nf { color: #900; font-weight: bold } // Name.Function
|
|
||||||
.nn { color: #555 } // Name.Namespace
|
|
||||||
.nt { color: #000080 } // Name.Tag
|
|
||||||
.nv { color: #008080 } // Name.Variable
|
|
||||||
.ow { font-weight: bold } // Operator.Word
|
|
||||||
.w { color: #bbb } // Text.Whitespace
|
|
||||||
.mf { color: #099 } // Literal.Number.Float
|
|
||||||
.mh { color: #099 } // Literal.Number.Hex
|
|
||||||
.mi { color: #099 } // Literal.Number.Integer
|
|
||||||
.mo { color: #099 } // Literal.Number.Oct
|
|
||||||
.sb { color: #d14 } // Literal.String.Backtick
|
|
||||||
.sc { color: #d14 } // Literal.String.Char
|
|
||||||
.sd { color: #d14 } // Literal.String.Doc
|
|
||||||
.s2 { color: #d14 } // Literal.String.Double
|
|
||||||
.se { color: #d14 } // Literal.String.Escape
|
|
||||||
.sh { color: #d14 } // Literal.String.Heredoc
|
|
||||||
.si { color: #d14 } // Literal.String.Interpol
|
|
||||||
.sx { color: #d14 } // Literal.String.Other
|
|
||||||
.sr { color: #009926 } // Literal.String.Regex
|
|
||||||
.s1 { color: #d14 } // Literal.String.Single
|
|
||||||
.ss { color: #990073 } // Literal.String.Symbol
|
|
||||||
.bp { color: #999 } // Name.Builtin.Pseudo
|
|
||||||
.vc { color: #008080 } // Name.Variable.Class
|
|
||||||
.vg { color: #008080 } // Name.Variable.Global
|
|
||||||
.vi { color: #008080 } // Name.Variable.Instance
|
|
||||||
.il { color: #099 } // Literal.Number.Integer.Long
|
|
||||||
}
|
|
@ -1,11 +0,0 @@
|
|||||||
---
|
|
||||||
layout: page
|
|
||||||
title: About
|
|
||||||
permalink: /about/
|
|
||||||
---
|
|
||||||
|
|
||||||
This is the base Jekyll theme. You can find out more info about customizing your Jekyll theme, as well as basic Jekyll usage documentation at [jekyllrb.com](http://jekyllrb.com/)
|
|
||||||
|
|
||||||
You can find the source code for the Jekyll new theme at: [github.com/jglovier/jekyll-new](https://github.com/jglovier/jekyll-new)
|
|
||||||
|
|
||||||
You can find the source code for Jekyll at [github.com/jekyll/jekyll](https://github.com/jekyll/jekyll)
|
|
@ -1,512 +0,0 @@
|
|||||||
/*
|
|
||||||
* basic.css
|
|
||||||
* ~~~~~~~~~
|
|
||||||
*
|
|
||||||
* Sphinx stylesheet -- basic theme.
|
|
||||||
*
|
|
||||||
* :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
|
|
||||||
* :license: BSD, see LICENSE for details.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* -- main layout ----------------------------------------------------------- */
|
|
||||||
|
|
||||||
div.clearer {
|
|
||||||
clear: both;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* -- relbar ---------------------------------------------------------------- */
|
|
||||||
|
|
||||||
div.related {
|
|
||||||
width: 100%;
|
|
||||||
font-size: 90%;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.related h3 {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.related ul {
|
|
||||||
margin: 0;
|
|
||||||
padding: 0 0 0 10px;
|
|
||||||
list-style: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.related li {
|
|
||||||
display: inline;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.related li.right {
|
|
||||||
float: right;
|
|
||||||
margin-right: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* -- sidebar --------------------------------------------------------------- */
|
|
||||||
|
|
||||||
div.sphinxsidebarwrapper {
|
|
||||||
padding: 10px 5px 0 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.sphinxsidebar {
|
|
||||||
float: left;
|
|
||||||
width: 230px;
|
|
||||||
margin-left: -100%;
|
|
||||||
font-size: 90%;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.sphinxsidebar ul {
|
|
||||||
list-style: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.sphinxsidebar ul ul,
|
|
||||||
div.sphinxsidebar ul.want-points {
|
|
||||||
margin-left: 20px;
|
|
||||||
list-style: square;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.sphinxsidebar ul ul {
|
|
||||||
margin-top: 0;
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.sphinxsidebar form {
|
|
||||||
margin-top: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.sphinxsidebar input {
|
|
||||||
border: 1px solid #98dbcc;
|
|
||||||
font-family: sans-serif;
|
|
||||||
font-size: 1em;
|
|
||||||
}
|
|
||||||
|
|
||||||
img {
|
|
||||||
border: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* -- search page ----------------------------------------------------------- */
|
|
||||||
|
|
||||||
ul.search {
|
|
||||||
margin: 10px 0 0 20px;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
ul.search li {
|
|
||||||
padding: 5px 0 5px 20px;
|
|
||||||
background-image: url(file.png);
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
background-position: 0 7px;
|
|
||||||
}
|
|
||||||
|
|
||||||
ul.search li a {
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
ul.search li div.context {
|
|
||||||
color: #888;
|
|
||||||
margin: 2px 0 0 30px;
|
|
||||||
text-align: left;
|
|
||||||
}
|
|
||||||
|
|
||||||
ul.keywordmatches li.goodmatch a {
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* -- index page ------------------------------------------------------------ */
|
|
||||||
|
|
||||||
table.contentstable {
|
|
||||||
width: 90%;
|
|
||||||
}
|
|
||||||
|
|
||||||
table.contentstable p.biglink {
|
|
||||||
line-height: 150%;
|
|
||||||
}
|
|
||||||
|
|
||||||
a.biglink {
|
|
||||||
font-size: 1.3em;
|
|
||||||
}
|
|
||||||
|
|
||||||
span.linkdescr {
|
|
||||||
font-style: italic;
|
|
||||||
padding-top: 5px;
|
|
||||||
font-size: 90%;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* -- general index --------------------------------------------------------- */
|
|
||||||
|
|
||||||
table.indextable {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
table.indextable td {
|
|
||||||
text-align: left;
|
|
||||||
vertical-align: top;
|
|
||||||
}
|
|
||||||
|
|
||||||
table.indextable dl, table.indextable dd {
|
|
||||||
margin-top: 0;
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
table.indextable tr.pcap {
|
|
||||||
height: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
table.indextable tr.cap {
|
|
||||||
margin-top: 10px;
|
|
||||||
background-color: #f2f2f2;
|
|
||||||
}
|
|
||||||
|
|
||||||
img.toggler {
|
|
||||||
margin-right: 3px;
|
|
||||||
margin-top: 3px;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.modindex-jumpbox {
|
|
||||||
border-top: 1px solid #ddd;
|
|
||||||
border-bottom: 1px solid #ddd;
|
|
||||||
margin: 1em 0 1em 0;
|
|
||||||
padding: 0.4em;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.genindex-jumpbox {
|
|
||||||
border-top: 1px solid #ddd;
|
|
||||||
border-bottom: 1px solid #ddd;
|
|
||||||
margin: 1em 0 1em 0;
|
|
||||||
padding: 0.4em;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* -- general body styles --------------------------------------------------- */
|
|
||||||
|
|
||||||
a.headerlink {
|
|
||||||
visibility: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
h1:hover > a.headerlink,
|
|
||||||
h2:hover > a.headerlink,
|
|
||||||
h3:hover > a.headerlink,
|
|
||||||
h4:hover > a.headerlink,
|
|
||||||
h5:hover > a.headerlink,
|
|
||||||
h6:hover > a.headerlink,
|
|
||||||
dt:hover > a.headerlink {
|
|
||||||
visibility: visible;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.document p.caption {
|
|
||||||
text-align: inherit;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.document td {
|
|
||||||
text-align: left;
|
|
||||||
}
|
|
||||||
|
|
||||||
.field-list ul {
|
|
||||||
padding-left: 1em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.first {
|
|
||||||
margin-top: 0 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
p.rubric {
|
|
||||||
margin-top: 30px;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
.align-left {
|
|
||||||
text-align: left;
|
|
||||||
}
|
|
||||||
|
|
||||||
.align-center {
|
|
||||||
clear: both;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.align-right {
|
|
||||||
text-align: right;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* -- sidebars -------------------------------------------------------------- */
|
|
||||||
|
|
||||||
div.sidebar {
|
|
||||||
margin: 0 0 0.5em 1em;
|
|
||||||
border: 1px solid #ddb;
|
|
||||||
padding: 7px 7px 0 7px;
|
|
||||||
background-color: #ffe;
|
|
||||||
width: 40%;
|
|
||||||
float: right;
|
|
||||||
}
|
|
||||||
|
|
||||||
p.sidebar-title {
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* -- topics ---------------------------------------------------------------- */
|
|
||||||
|
|
||||||
div.topic {
|
|
||||||
border: 1px solid #ccc;
|
|
||||||
padding: 7px 7px 0 7px;
|
|
||||||
margin: 10px 0 10px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
p.topic-title {
|
|
||||||
font-size: 1.1em;
|
|
||||||
font-weight: bold;
|
|
||||||
margin-top: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* -- admonitions ----------------------------------------------------------- */
|
|
||||||
|
|
||||||
div.admonition {
|
|
||||||
margin-top: 10px;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
padding: 7px;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.admonition dt {
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.admonition dl {
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
p.admonition-title {
|
|
||||||
margin: 0px 10px 5px 0px;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.document p.centered {
|
|
||||||
text-align: center;
|
|
||||||
margin-top: 25px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* -- tables ---------------------------------------------------------------- */
|
|
||||||
|
|
||||||
table.docutils {
|
|
||||||
border: 0;
|
|
||||||
border-collapse: collapse;
|
|
||||||
}
|
|
||||||
|
|
||||||
table.docutils td, table.docutils th {
|
|
||||||
padding: 1px 8px 1px 5px;
|
|
||||||
border-top: 0;
|
|
||||||
border-left: 0;
|
|
||||||
border-right: 0;
|
|
||||||
border-bottom: 1px solid #aaa;
|
|
||||||
}
|
|
||||||
|
|
||||||
table.field-list td, table.field-list th {
|
|
||||||
border: 0 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
table.footnote td, table.footnote th {
|
|
||||||
border: 0 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
th {
|
|
||||||
text-align: left;
|
|
||||||
padding-right: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
table.citation {
|
|
||||||
border-left: solid 1px gray;
|
|
||||||
margin-left: 1px;
|
|
||||||
}
|
|
||||||
|
|
||||||
table.citation td {
|
|
||||||
border-bottom: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* -- other body styles ----------------------------------------------------- */
|
|
||||||
|
|
||||||
ol.arabic {
|
|
||||||
list-style: decimal;
|
|
||||||
}
|
|
||||||
|
|
||||||
ol.loweralpha {
|
|
||||||
list-style: lower-alpha;
|
|
||||||
}
|
|
||||||
|
|
||||||
ol.upperalpha {
|
|
||||||
list-style: upper-alpha;
|
|
||||||
}
|
|
||||||
|
|
||||||
ol.lowerroman {
|
|
||||||
list-style: lower-roman;
|
|
||||||
}
|
|
||||||
|
|
||||||
ol.upperroman {
|
|
||||||
list-style: upper-roman;
|
|
||||||
}
|
|
||||||
|
|
||||||
dl {
|
|
||||||
margin-bottom: 15px;
|
|
||||||
}
|
|
||||||
|
|
||||||
dd p {
|
|
||||||
margin-top: 0px;
|
|
||||||
}
|
|
||||||
|
|
||||||
dd ul, dd table {
|
|
||||||
margin-bottom: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
dd {
|
|
||||||
margin-top: 3px;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
margin-left: 30px;
|
|
||||||
}
|
|
||||||
|
|
||||||
dt:target, .highlighted {
|
|
||||||
background-color: #fbe54e;
|
|
||||||
}
|
|
||||||
|
|
||||||
dl.glossary dt {
|
|
||||||
font-weight: bold;
|
|
||||||
font-size: 1.1em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.field-list ul {
|
|
||||||
margin: 0;
|
|
||||||
padding-left: 1em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.field-list p {
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.refcount {
|
|
||||||
color: #060;
|
|
||||||
}
|
|
||||||
|
|
||||||
.optional {
|
|
||||||
font-size: 1.3em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.versionmodified {
|
|
||||||
font-style: italic;
|
|
||||||
}
|
|
||||||
|
|
||||||
.system-message {
|
|
||||||
background-color: #fda;
|
|
||||||
padding: 5px;
|
|
||||||
border: 3px solid red;
|
|
||||||
}
|
|
||||||
|
|
||||||
.footnote:target {
|
|
||||||
background-color: #ffa
|
|
||||||
}
|
|
||||||
|
|
||||||
.line-block {
|
|
||||||
display: block;
|
|
||||||
margin-top: 1em;
|
|
||||||
margin-bottom: 1em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.line-block .line-block {
|
|
||||||
margin-top: 0;
|
|
||||||
margin-bottom: 0;
|
|
||||||
margin-left: 1.5em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.guilabel, .menuselection {
|
|
||||||
font-family: sans-serif;
|
|
||||||
}
|
|
||||||
|
|
||||||
.accelerator {
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
|
|
||||||
.classifier {
|
|
||||||
font-style: oblique;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* -- code displays --------------------------------------------------------- */
|
|
||||||
|
|
||||||
pre {
|
|
||||||
overflow: auto;
|
|
||||||
font-size: 1.1em;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
td.linenos pre {
|
|
||||||
padding: 5px 0px;
|
|
||||||
border: 0;
|
|
||||||
background-color: transparent;
|
|
||||||
color: #aaa;
|
|
||||||
}
|
|
||||||
|
|
||||||
table.highlighttable {
|
|
||||||
margin-left: 0.5em;
|
|
||||||
}
|
|
||||||
|
|
||||||
table.highlighttable td {
|
|
||||||
padding: 0 0.5em 0 0.5em;
|
|
||||||
}
|
|
||||||
|
|
||||||
tt.descname {
|
|
||||||
background-color: transparent;
|
|
||||||
font-weight: bold;
|
|
||||||
font-size: 1.2em;
|
|
||||||
}
|
|
||||||
|
|
||||||
tt.descclassname {
|
|
||||||
background-color: transparent;
|
|
||||||
}
|
|
||||||
|
|
||||||
tt.xref, a tt {
|
|
||||||
background-color: transparent;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
h1 tt, h2 tt, h3 tt, h4 tt, h5 tt, h6 tt {
|
|
||||||
background-color: transparent;
|
|
||||||
}
|
|
||||||
|
|
||||||
.viewcode-link {
|
|
||||||
float: right;
|
|
||||||
}
|
|
||||||
|
|
||||||
.viewcode-back {
|
|
||||||
float: right;
|
|
||||||
font-family: sans-serif;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.viewcode-block:target {
|
|
||||||
margin: -1px -10px;
|
|
||||||
padding: 0 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* -- math display ---------------------------------------------------------- */
|
|
||||||
|
|
||||||
img.math {
|
|
||||||
vertical-align: middle;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.document div.math p {
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
span.eqno {
|
|
||||||
float: right;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* -- printout stylesheet --------------------------------------------------- */
|
|
||||||
|
|
||||||
@media print {
|
|
||||||
div.document,
|
|
||||||
div.documentwrapper,
|
|
||||||
div.bodywrapper {
|
|
||||||
margin: 0 !important;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.sphinxsidebar,
|
|
||||||
div.related,
|
|
||||||
div.footer,
|
|
||||||
#top-link {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,40 +0,0 @@
|
|||||||
.toc {
|
|
||||||
background: #f9f9f9;
|
|
||||||
border: 1px solid #aaaaaa;
|
|
||||||
display: table;
|
|
||||||
padding-top: 16px;
|
|
||||||
padding-right: 16px;
|
|
||||||
padding-bottom: 16px;
|
|
||||||
list-style: none;
|
|
||||||
color: #2EA3F2;
|
|
||||||
}
|
|
||||||
|
|
||||||
ul {
|
|
||||||
padding-left: 30px;
|
|
||||||
}
|
|
||||||
|
|
||||||
p {
|
|
||||||
padding-left: 30px;
|
|
||||||
color: #666;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
h3 {
|
|
||||||
padding-top: 48px;
|
|
||||||
color: #333;
|
|
||||||
}
|
|
||||||
|
|
||||||
h4, h5 {
|
|
||||||
border-top: 40px solid transparent;
|
|
||||||
margin-top: -40px;
|
|
||||||
-webkit-background-clip: padding-box;
|
|
||||||
-moz-background-clip: padding;
|
|
||||||
background-clip: padding-box;
|
|
||||||
font-style: italic;
|
|
||||||
font-size: 16px;
|
|
||||||
padding-top: 16px;
|
|
||||||
color: #333;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
@ -1,52 +0,0 @@
|
|||||||
---
|
|
||||||
# Only the main Sass file needs front matter (the dashes are enough)
|
|
||||||
---
|
|
||||||
@charset "utf-8";
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Our variables
|
|
||||||
$base-font-family: Helvetica, Arial, sans-serif;
|
|
||||||
$base-font-size: 16px;
|
|
||||||
$small-font-size: $base-font-size * 0.875;
|
|
||||||
$base-line-height: 1.5;
|
|
||||||
|
|
||||||
$spacing-unit: 30px;
|
|
||||||
|
|
||||||
$text-color: #111;
|
|
||||||
$background-color: #fdfdfd;
|
|
||||||
$brand-color: #2a7ae2;
|
|
||||||
|
|
||||||
$grey-color: #828282;
|
|
||||||
$grey-color-light: lighten($grey-color, 40%);
|
|
||||||
$grey-color-dark: darken($grey-color, 25%);
|
|
||||||
|
|
||||||
// Width of the content area
|
|
||||||
$content-width: 1340px;
|
|
||||||
|
|
||||||
$on-palm: 600px;
|
|
||||||
$on-laptop: 800px;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Using media queries with like this:
|
|
||||||
// @include media-query($on-palm) {
|
|
||||||
// .wrapper {
|
|
||||||
// padding-right: $spacing-unit / 2;
|
|
||||||
// padding-left: $spacing-unit / 2;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
@mixin media-query($device) {
|
|
||||||
@media screen and (max-width: $device) {
|
|
||||||
@content;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Import partials from `sass_dir` (defaults to `_sass`)
|
|
||||||
@import
|
|
||||||
"base",
|
|
||||||
"layout",
|
|
||||||
"syntax-highlighting"
|
|
||||||
;
|
|
@ -1,285 +0,0 @@
|
|||||||
/*
|
|
||||||
* nature.css_t
|
|
||||||
* ~~~~~~~~~~~~
|
|
||||||
*
|
|
||||||
* Sphinx stylesheet -- nature theme.
|
|
||||||
*
|
|
||||||
* :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
|
|
||||||
* :license: BSD, see LICENSE for details.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* -- page layout ----------------------------------------------------------- */
|
|
||||||
|
|
||||||
body {
|
|
||||||
font-family: Arial, sans-serif;
|
|
||||||
font-size: 100%;
|
|
||||||
/*background-color: #111;*/
|
|
||||||
color: #555;
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.documentwrapper {
|
|
||||||
float: left;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.bodywrapper {
|
|
||||||
margin: 0 0 0 230px;
|
|
||||||
}
|
|
||||||
|
|
||||||
hr {
|
|
||||||
border: 1px solid #B1B4B6;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
div.document {
|
|
||||||
background-color: #eee;
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
div.document {
|
|
||||||
background-color: #ffffff;
|
|
||||||
color: #3E4349;
|
|
||||||
padding: 0 30px 30px 30px;
|
|
||||||
font-size: 0.9em;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.footer {
|
|
||||||
color: #555;
|
|
||||||
width: 100%;
|
|
||||||
padding: 13px 0;
|
|
||||||
text-align: center;
|
|
||||||
font-size: 75%;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.footer a {
|
|
||||||
color: #444;
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.related {
|
|
||||||
background-color: #6BA81E;
|
|
||||||
line-height: 32px;
|
|
||||||
color: #fff;
|
|
||||||
text-shadow: 0px 1px 0 #444;
|
|
||||||
font-size: 0.9em;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.related a {
|
|
||||||
color: #E2F3CC;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.sphinxsidebar {
|
|
||||||
font-size: 0.75em;
|
|
||||||
line-height: 1.5em;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.sphinxsidebarwrapper{
|
|
||||||
padding: 20px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.sphinxsidebar h3,
|
|
||||||
div.sphinxsidebar h4 {
|
|
||||||
font-family: Arial, sans-serif;
|
|
||||||
color: #222;
|
|
||||||
font-size: 1.2em;
|
|
||||||
font-weight: normal;
|
|
||||||
margin: 0;
|
|
||||||
padding: 5px 10px;
|
|
||||||
background-color: #ddd;
|
|
||||||
text-shadow: 1px 1px 0 white
|
|
||||||
}
|
|
||||||
|
|
||||||
div.sphinxsidebar h4{
|
|
||||||
font-size: 1.1em;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.sphinxsidebar h3 a {
|
|
||||||
color: #444;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
div.sphinxsidebar p {
|
|
||||||
color: #888;
|
|
||||||
padding: 5px 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.sphinxsidebar p.topless {
|
|
||||||
}
|
|
||||||
|
|
||||||
div.sphinxsidebar ul {
|
|
||||||
margin: 10px 20px;
|
|
||||||
padding: 0;
|
|
||||||
color: #000;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.sphinxsidebar a {
|
|
||||||
color: #444;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.sphinxsidebar input {
|
|
||||||
border: 1px solid #ccc;
|
|
||||||
font-family: sans-serif;
|
|
||||||
font-size: 1em;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.sphinxsidebar input[type=text]{
|
|
||||||
margin-left: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* -- body styles ----------------------------------------------------------- */
|
|
||||||
|
|
||||||
a {
|
|
||||||
color: #005B81;
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
a:hover {
|
|
||||||
color: #E32E00;
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.document h1,
|
|
||||||
div.document h2,
|
|
||||||
div.document h3,
|
|
||||||
div.document h4,
|
|
||||||
div.document h5,
|
|
||||||
div.document h6 {
|
|
||||||
font-family: Arial, sans-serif;
|
|
||||||
background-color: #BED4EB;
|
|
||||||
font-weight: normal;
|
|
||||||
color: #212224;
|
|
||||||
margin: 30px 0px 10px 0px;
|
|
||||||
padding: 5px 0 5px 10px;
|
|
||||||
text-shadow: 0px 1px 0 white
|
|
||||||
}
|
|
||||||
|
|
||||||
div.document h1 { border-top: 20px solid white; margin-top: 0; font-size: 200%; }
|
|
||||||
div.document h2 { font-size: 150%; background-color: #C8D5E3; }
|
|
||||||
div.document h3 { font-size: 120%; background-color: #D8DEE3; }
|
|
||||||
div.document h4 { font-size: 110%; background-color: #D8DEE3; }
|
|
||||||
div.document h5 { font-size: 100%; background-color: #D8DEE3; }
|
|
||||||
div.document h6 { font-size: 100%; background-color: #D8DEE3; }
|
|
||||||
|
|
||||||
a.headerlink {
|
|
||||||
color: #c60f0f;
|
|
||||||
font-size: 0.8em;
|
|
||||||
padding: 0 4px 0 4px;
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
a.headerlink:hover {
|
|
||||||
background-color: #c60f0f;
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.document p, div.document dd, div.document li {
|
|
||||||
line-height: 1.5em;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.admonition p.admonition-title + p {
|
|
||||||
display: inline;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.highlight{
|
|
||||||
background-color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.note {
|
|
||||||
background-color: #eee;
|
|
||||||
border: 1px solid #ccc;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.seealso {
|
|
||||||
background-color: #ffc;
|
|
||||||
border: 1px solid #ff6;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.topic {
|
|
||||||
background-color: #eee;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.warning {
|
|
||||||
background-color: #ffe4e4;
|
|
||||||
border: 1px solid #f66;
|
|
||||||
}
|
|
||||||
|
|
||||||
p.admonition-title {
|
|
||||||
display: inline;
|
|
||||||
}
|
|
||||||
|
|
||||||
p.admonition-title:after {
|
|
||||||
content: ":";
|
|
||||||
}
|
|
||||||
|
|
||||||
pre {
|
|
||||||
padding: 10px;
|
|
||||||
background-color: White;
|
|
||||||
color: #222;
|
|
||||||
line-height: 1.2em;
|
|
||||||
border: 1px solid #C6C9CB;
|
|
||||||
font-size: 1.1em;
|
|
||||||
margin: 1.5em 0 1.5em 0;
|
|
||||||
-webkit-box-shadow: 1px 1px 1px #d8d8d8;
|
|
||||||
-moz-box-shadow: 1px 1px 1px #d8d8d8;
|
|
||||||
}
|
|
||||||
|
|
||||||
tt {
|
|
||||||
background-color: #ecf0f3;
|
|
||||||
color: #222;
|
|
||||||
/* padding: 1px 2px; */
|
|
||||||
font-size: 1.1em;
|
|
||||||
font-family: monospace;
|
|
||||||
}
|
|
||||||
|
|
||||||
.viewcode-back {
|
|
||||||
font-family: Arial, sans-serif;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.viewcode-block:target {
|
|
||||||
background-color: #f4debf;
|
|
||||||
border-top: 1px solid #ac9;
|
|
||||||
border-bottom: 1px solid #ac9;
|
|
||||||
}
|
|
||||||
|
|
||||||
p {
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
ul li dd {
|
|
||||||
margin-top: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
ul li dl {
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
li dl dd {
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
dd ul {
|
|
||||||
padding-left: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
li dd ul {
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
table {
|
|
||||||
margin-top: 10px;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
border: 0;
|
|
||||||
border-collapse: collapse;
|
|
||||||
}
|
|
||||||
|
|
||||||
td[colspan]:not([colspan="1"]) {
|
|
||||||
background: #eeeeee;
|
|
||||||
}
|
|
||||||
|
|
||||||
thead {
|
|
||||||
background: #eeeeee;
|
|
||||||
}
|
|
||||||
|
|
@ -1,62 +0,0 @@
|
|||||||
p {
|
|
||||||
line-height: 1.5;
|
|
||||||
color: #666;
|
|
||||||
}
|
|
||||||
|
|
||||||
a.anchor {
|
|
||||||
display: block;
|
|
||||||
position: relative;
|
|
||||||
top: -50px;
|
|
||||||
visibility: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
a:link {
|
|
||||||
color: #2EA3F2;
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
a:visited {
|
|
||||||
color: #2EA3F2;
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
a:active {
|
|
||||||
color: #2EA3F2;
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
a:hover {
|
|
||||||
color: #2EA3F2;
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
|
|
||||||
h1,h2,h3,h4,h5 {
|
|
||||||
color: #333;
|
|
||||||
}
|
|
||||||
|
|
||||||
.page-content {
|
|
||||||
min-height: 100%;
|
|
||||||
height: auto !important; /* This line and the next line are not necessary unless you need IE6 support */
|
|
||||||
height: 100%;
|
|
||||||
margin: 0 auto -33px; /* the bottom margin is the negative value of the footer's height */
|
|
||||||
padding-bottom: 0;
|
|
||||||
padding-top: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.push {
|
|
||||||
height: 33px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#page {
|
|
||||||
margin-bottom: -33px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#document {
|
|
||||||
margin-top: 10px; /* We want a little whitespace before the page content starts */
|
|
||||||
width: 1080px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#wrapper {
|
|
||||||
max-width: 1080px;
|
|
||||||
}
|
|
||||||
|
|
@ -1,30 +0,0 @@
|
|||||||
---
|
|
||||||
layout: null
|
|
||||||
---
|
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
|
|
||||||
<channel>
|
|
||||||
<title>{{ site.title | xml_escape }}</title>
|
|
||||||
<description>{{ site.description | xml_escape }}</description>
|
|
||||||
<link>{{ site.url }}{{ site.baseurl }}/</link>
|
|
||||||
<atom:link href="{{ "/feed.xml" | prepend: site.baseurl | prepend: site.url }}" rel="self" type="application/rss+xml"/>
|
|
||||||
<pubDate>{{ site.time | date_to_rfc822 }}</pubDate>
|
|
||||||
<lastBuildDate>{{ site.time | date_to_rfc822 }}</lastBuildDate>
|
|
||||||
<generator>Jekyll v{{ jekyll.version }}</generator>
|
|
||||||
{% for post in site.posts limit:10 %}
|
|
||||||
<item>
|
|
||||||
<title>{{ post.title | xml_escape }}</title>
|
|
||||||
<description>{{ post.content | xml_escape }}</description>
|
|
||||||
<pubDate>{{ post.date | date_to_rfc822 }}</pubDate>
|
|
||||||
<link>{{ post.url | prepend: site.baseurl | prepend: site.url }}</link>
|
|
||||||
<guid isPermaLink="true">{{ post.url | prepend: site.baseurl | prepend: site.url }}</guid>
|
|
||||||
{% for tag in post.tags %}
|
|
||||||
<category>{{ tag | xml_escape }}</category>
|
|
||||||
{% endfor %}
|
|
||||||
{% for cat in post.categories %}
|
|
||||||
<category>{{ cat | xml_escape }}</category>
|
|
||||||
{% endfor %}
|
|
||||||
</item>
|
|
||||||
{% endfor %}
|
|
||||||
</channel>
|
|
||||||
</rss>
|
|
@ -1,14 +0,0 @@
|
|||||||
---
|
|
||||||
layout: default
|
|
||||||
---
|
|
||||||
<div class="home">
|
|
||||||
|
|
||||||
<h1>Guides</h1>
|
|
||||||
|
|
||||||
<p>Here is a collection of guides that might help you get involved with Matrix.</p>
|
|
||||||
<p>First, there is the <a href="./getting_involved.html" title="Getting Involved">Getting Involved</a> guide, which explains various ways of getting started with Matrix.</p>
|
|
||||||
<p>The <a href="./client-server.html" title="Client-Server API">Client-Server API</a> guide explains in detail how to use the CS API, which is useful if you want to write a client (or modify an existing one) - or if you're just interested in how it works "under the hood".</p>
|
|
||||||
<p>The <a href="./application_services.html" title="Application services">Application services</a> guide introduces and explains Application services, and what they can be used for.
|
|
||||||
<p>Finally there is the <a href="./faq.html" title="FAQ">FAQ</a>, which tries to answer all your questions relating to Matrix!</p>
|
|
||||||
|
|
||||||
</div>
|
|
Loading…
Reference in New Issue