dc09453a77
* renamed FeedSub plugin to OStatus * now setting avatar on subscriptions * general fixes for subscription * integrated PuSH hub to handle only user timelines on canonical ID url; sends updates directly * set $config['feedsub']['nohub'] = true to test w/ foreign feeds that don't have hubs (won't actually receive updates though) * a few bits of code documentation * HMAC support for verified distributions (safest if sub setup is on HTTPS) And a couple core changes: * minimizing HTML output for exceptions in API requests to aid in debugging * fix for rel=self link in apitimelineuser when id given This does not not yet include any of the individual subscription management (Salmon notifications for sub/unsub, etc) nor a nice UI for user subscriptions. Needs some further cleanup to treat posts as status updates instead of link references.
338 lines
6.7 KiB
Plaintext
Executable File
338 lines
6.7 KiB
Plaintext
Executable File
# -*- rnc -*-
|
|
# RELAX NG Compact Syntax Grammar for the
|
|
# Atom Format Specification Version 11
|
|
|
|
namespace atom = "http://www.w3.org/2005/Atom"
|
|
namespace xhtml = "http://www.w3.org/1999/xhtml"
|
|
namespace s = "http://www.ascc.net/xml/schematron"
|
|
namespace local = ""
|
|
|
|
start = atomFeed | atomEntry
|
|
|
|
# Common attributes
|
|
|
|
atomCommonAttributes =
|
|
attribute xml:base { atomUri }?,
|
|
attribute xml:lang { atomLanguageTag }?,
|
|
undefinedAttribute*
|
|
|
|
# Text Constructs
|
|
|
|
atomPlainTextConstruct =
|
|
atomCommonAttributes,
|
|
attribute type { "text" | "html" }?,
|
|
text
|
|
|
|
atomXHTMLTextConstruct =
|
|
atomCommonAttributes,
|
|
attribute type { "xhtml" },
|
|
xhtmlDiv
|
|
|
|
atomTextConstruct = atomPlainTextConstruct | atomXHTMLTextConstruct
|
|
|
|
# Person Construct
|
|
|
|
atomPersonConstruct =
|
|
atomCommonAttributes,
|
|
(element atom:name { text }
|
|
& element atom:uri { atomUri }?
|
|
& element atom:email { atomEmailAddress }?
|
|
& extensionElement*)
|
|
|
|
# Date Construct
|
|
|
|
atomDateConstruct =
|
|
atomCommonAttributes,
|
|
xsd:dateTime
|
|
|
|
# atom:feed
|
|
|
|
atomFeed =
|
|
[
|
|
s:rule [
|
|
context = "atom:feed"
|
|
s:assert [
|
|
test = "atom:author or not(atom:entry[not(atom:author)])"
|
|
"An atom:feed must have an atom:author unless all "
|
|
~ "of its atom:entry children have an atom:author."
|
|
]
|
|
]
|
|
]
|
|
element atom:feed {
|
|
atomCommonAttributes,
|
|
(atomAuthor*
|
|
& atomCategory*
|
|
& atomContributor*
|
|
& atomGenerator?
|
|
& atomIcon?
|
|
& atomId
|
|
& atomLink*
|
|
& atomLogo?
|
|
& atomRights?
|
|
& atomSubtitle?
|
|
& atomTitle
|
|
& atomUpdated
|
|
& extensionElement*),
|
|
atomEntry*
|
|
}
|
|
|
|
# atom:entry
|
|
|
|
atomEntry =
|
|
[
|
|
s:rule [
|
|
context = "atom:entry"
|
|
s:assert [
|
|
test = "atom:link[@rel='alternate'] "
|
|
~ "or atom:link[not(@rel)] "
|
|
~ "or atom:content"
|
|
"An atom:entry must have at least one atom:link element "
|
|
~ "with a rel attribute of 'alternate' "
|
|
~ "or an atom:content."
|
|
]
|
|
]
|
|
s:rule [
|
|
context = "atom:entry"
|
|
s:assert [
|
|
test = "atom:author or "
|
|
~ "../atom:author or atom:source/atom:author"
|
|
"An atom:entry must have an atom:author "
|
|
~ "if its feed does not."
|
|
]
|
|
]
|
|
]
|
|
element atom:entry {
|
|
atomCommonAttributes,
|
|
(atomAuthor*
|
|
& atomCategory*
|
|
& atomContent?
|
|
& atomContributor*
|
|
& atomId
|
|
& atomLink*
|
|
& atomPublished?
|
|
& atomRights?
|
|
& atomSource?
|
|
& atomSummary?
|
|
& atomTitle
|
|
& atomUpdated
|
|
& extensionElement*)
|
|
}
|
|
|
|
# atom:content
|
|
|
|
atomInlineTextContent =
|
|
element atom:content {
|
|
atomCommonAttributes,
|
|
attribute type { "text" | "html" }?,
|
|
(text)*
|
|
}
|
|
|
|
atomInlineXHTMLContent =
|
|
element atom:content {
|
|
atomCommonAttributes,
|
|
attribute type { "xhtml" },
|
|
xhtmlDiv
|
|
}
|
|
|
|
atomInlineOtherContent =
|
|
element atom:content {
|
|
atomCommonAttributes,
|
|
attribute type { atomMediaType }?,
|
|
(text|anyElement)*
|
|
}
|
|
|
|
atomOutOfLineContent =
|
|
element atom:content {
|
|
atomCommonAttributes,
|
|
attribute type { atomMediaType }?,
|
|
attribute src { atomUri },
|
|
empty
|
|
}
|
|
|
|
atomContent = atomInlineTextContent
|
|
| atomInlineXHTMLContent
|
|
| atomInlineOtherContent
|
|
| atomOutOfLineContent
|
|
|
|
# atom:author
|
|
|
|
atomAuthor = element atom:author { atomPersonConstruct }
|
|
|
|
# atom:category
|
|
|
|
atomCategory =
|
|
element atom:category {
|
|
atomCommonAttributes,
|
|
attribute term { text },
|
|
attribute scheme { atomUri }?,
|
|
attribute label { text }?,
|
|
undefinedContent
|
|
}
|
|
|
|
# atom:contributor
|
|
|
|
atomContributor = element atom:contributor { atomPersonConstruct }
|
|
|
|
# atom:generator
|
|
|
|
atomGenerator = element atom:generator {
|
|
atomCommonAttributes,
|
|
attribute uri { atomUri }?,
|
|
attribute version { text }?,
|
|
text
|
|
}
|
|
|
|
# atom:icon
|
|
|
|
atomIcon = element atom:icon {
|
|
atomCommonAttributes,
|
|
(atomUri)
|
|
}
|
|
|
|
# atom:id
|
|
|
|
atomId = element atom:id {
|
|
atomCommonAttributes,
|
|
(atomUri)
|
|
}
|
|
|
|
# atom:logo
|
|
|
|
atomLogo = element atom:logo {
|
|
atomCommonAttributes,
|
|
(atomUri)
|
|
}
|
|
|
|
# atom:link
|
|
|
|
atomLink =
|
|
element atom:link {
|
|
atomCommonAttributes,
|
|
attribute href { atomUri },
|
|
attribute rel { atomNCName | atomUri }?,
|
|
attribute type { atomMediaType }?,
|
|
attribute hreflang { atomLanguageTag }?,
|
|
attribute title { text }?,
|
|
attribute length { text }?,
|
|
undefinedContent
|
|
}
|
|
|
|
# atom:published
|
|
|
|
atomPublished = element atom:published { atomDateConstruct }
|
|
|
|
# atom:rights
|
|
|
|
atomRights = element atom:rights { atomTextConstruct }
|
|
|
|
# atom:source
|
|
|
|
atomSource =
|
|
element atom:source {
|
|
atomCommonAttributes,
|
|
(atomAuthor*
|
|
& atomCategory*
|
|
& atomContributor*
|
|
& atomGenerator?
|
|
& atomIcon?
|
|
& atomId?
|
|
& atomLink*
|
|
& atomLogo?
|
|
& atomRights?
|
|
& atomSubtitle?
|
|
& atomTitle?
|
|
& atomUpdated?
|
|
& extensionElement*)
|
|
}
|
|
|
|
# atom:subtitle
|
|
|
|
atomSubtitle = element atom:subtitle { atomTextConstruct }
|
|
|
|
# atom:summary
|
|
|
|
atomSummary = element atom:summary { atomTextConstruct }
|
|
|
|
# atom:title
|
|
|
|
atomTitle = element atom:title { atomTextConstruct }
|
|
|
|
# atom:updated
|
|
|
|
atomUpdated = element atom:updated { atomDateConstruct }
|
|
|
|
# Low-level simple types
|
|
|
|
atomNCName = xsd:string { minLength = "1" pattern = "[^:]*" }
|
|
|
|
# Whatever a media type is, it contains at least one slash
|
|
atomMediaType = xsd:string { pattern = ".+/.+" }
|
|
|
|
# As defined in RFC 3066
|
|
atomLanguageTag = xsd:string {
|
|
pattern = "[A-Za-z]{1,8}(-[A-Za-z0-9]{1,8})*"
|
|
}
|
|
|
|
# Unconstrained; it's not entirely clear how IRI fit into
|
|
# xsd:anyURI so let's not try to constrain it here
|
|
atomUri = text
|
|
|
|
# Whatever an email address is, it contains at least one @
|
|
atomEmailAddress = xsd:string { pattern = ".+@.+" }
|
|
|
|
# Simple Extension
|
|
|
|
simpleExtensionElement =
|
|
element * - atom:* {
|
|
text
|
|
}
|
|
|
|
# Structured Extension
|
|
|
|
structuredExtensionElement =
|
|
element * - atom:* {
|
|
(attribute * { text }+,
|
|
(text|anyElement)*)
|
|
| (attribute * { text }*,
|
|
(text?, anyElement+, (text|anyElement)*))
|
|
}
|
|
|
|
# Other Extensibility
|
|
|
|
extensionElement =
|
|
simpleExtensionElement | structuredExtensionElement
|
|
|
|
undefinedAttribute =
|
|
attribute * - (xml:base | xml:lang | local:*) { text }
|
|
|
|
undefinedContent = (text|anyForeignElement)*
|
|
|
|
anyElement =
|
|
element * {
|
|
(attribute * { text }
|
|
| text
|
|
| anyElement)*
|
|
}
|
|
|
|
anyForeignElement =
|
|
element * - atom:* {
|
|
(attribute * { text }
|
|
| text
|
|
| anyElement)*
|
|
}
|
|
|
|
# XHTML
|
|
|
|
anyXHTML = element xhtml:* {
|
|
(attribute * { text }
|
|
| text
|
|
| anyXHTML)*
|
|
}
|
|
|
|
xhtmlDiv = element xhtml:div {
|
|
(attribute * { text }
|
|
| text
|
|
| anyXHTML)*
|
|
}
|
|
|
|
# EOF |