diff --git a/README.md b/README.md
index d3ff9fd..c77529a 100644
--- a/README.md
+++ b/README.md
@@ -10,6 +10,10 @@ Rivista includes [XSLT ](https://www.w3.org/TR/xslt/) stylesheets that transform
Rivista was inspired from Tigase and was motivated by Movim.
+## Instances
+
+* https://rivista.woodpeckersnest.eu/
+
## Preview
[
](screenshot/berlin-xmpp-meetup.png)
diff --git a/pubsub_to_atom.py b/pubsub_to_atom.py
index 61d19f2..cc9d220 100644
--- a/pubsub_to_atom.py
+++ b/pubsub_to_atom.py
@@ -108,7 +108,7 @@ async def view_node_items(request: Request):
iq = await get_node_item(pubsub, node, item_id)
if iq:
link = form_an_item_link(pubsub, node, item_id)
- xml_atom = generate_atom(iq, link)
+ xml_atom = generate_atom_comment(iq, link) if 'urn:xmpp:microblog:0:comments/' in node else generate_atom_post(iq, link)
iq = await get_node_items(pubsub, node)
if not '/' in node:
if iq:
@@ -146,7 +146,7 @@ async def view_node_items(request: Request):
iq = await get_node_items(pubsub, node)
if iq:
link = form_a_node_link(pubsub, node)
- xml_atom = generate_atom(iq, link)
+ xml_atom = generate_atom_comment(iq, link) if 'urn:xmpp:microblog:0:comments/' in node else generate_atom_post(iq, link)
else:
text = 'Please ensure that PubSub node "{}" is valid and accessible.'.format(node)
xml_atom = error_message(text)
@@ -177,7 +177,7 @@ async def view_node_items(request: Request):
iq = await get_node_items(pubsub, node)
if iq:
link = form_a_node_link(pubsub, node)
- xml_atom = generate_atom(iq, link)
+ xml_atom = generate_atom_post(iq, link)
else:
text = 'Please ensure that PubSub node "{}" is valid and accessible.'.format(node)
xml_atom = error_message(text)
@@ -187,7 +187,7 @@ async def view_node_items(request: Request):
iq = await get_node_items(pubsub, node)
if iq:
link = form_a_node_link(pubsub, node)
- xml_atom = generate_atom(iq, link)
+ xml_atom = generate_atom_post(iq, link)
else:
text = 'Please ensure that PubSub node "{}" is valid and accessible.'.format(node)
xml_atom = error_message(text)
@@ -263,7 +263,7 @@ def error_message(text):
return ET.tostring(feed, encoding='unicode')
# generate_rfc_4287
-def generate_atom(iq, link):
+def generate_atom_post(iq, link):
"""Generate an Atom Syndication Format (RFC 4287) from a Publish-Subscribe (XEP-0060) node items."""
pubsub = iq['from'].bare
node = iq['pubsub']['items']['node']
@@ -295,8 +295,8 @@ def generate_atom(iq, link):
links = item_payload.find(namespace + 'link')
if (not isinstance(title, ET.Element) and
not isinstance(links, ET.Element)): continue
- title_text = None if title == None else title.text
e_entry = ET.SubElement(e_feed, 'entry')
+ title_text = None if title == None else title.text
ET.SubElement(e_entry, 'title').text = title_text
if isinstance(links, ET.Element):
for link in item_payload.findall(namespace + 'link'):
@@ -372,6 +372,139 @@ def generate_atom(iq, link):
# ET.SubElement(e_entry, 'summary', {'type': summary_type_text}).text = summary_text
return ET.tostring(e_feed, encoding='unicode')
+# generate_rfc_4287
+def generate_atom_comment(iq, link):
+ """Generate an Atom Syndication Format (RFC 4287) from a Publish-Subscribe (XEP-0060) node items."""
+ pubsub = iq['from'].bare
+ node = iq['pubsub']['items']['node']
+ title = node
+ link = link
+ # link = form_a_node_link(pubsub, node)
+ # subtitle = 'XMPP PubSub Syndication Feed'
+ subtitle = pubsub
+ description = ('This is a syndication feed generated with Rivista, an XMPP '
+ 'Journal Publisher, which conveys XEP-0060: Publish-'
+ 'Subscribe nodes to standard RFC 4287: The Atom Syndication '
+ 'Format.')
+ language = iq['pubsub']['items']['lang']
+ items = iq['pubsub']['items']
+ e_feed = ET.Element("feed")
+ e_feed.set('xmlns', 'http://www.w3.org/2005/Atom')
+ ET.SubElement(e_feed, 'title', {'type': 'text'}).text = title
+ ET.SubElement(e_feed, 'subtitle', {'type': 'text'}).text = subtitle
+ ET.SubElement(e_feed, 'link', {'rel': 'self', 'href': link})
+ ET.SubElement(e_feed, 'generator', {
+ 'uri': 'https://git.xmpp-it.net/sch/Rivista',
+ 'version': '0.1'}).text = 'Rivista XJP'
+ ET.SubElement(e_feed, 'updated').text = datetime.datetime.now(datetime.UTC).isoformat()
+ for item in list(items)[::-1]:
+ item_id = item['id']
+ item_payload = item['payload']
+ namespace = '{http://www.w3.org/2005/Atom}'
+ authors = item_payload.find(namespace + 'author')
+ links = item_payload.find(namespace + 'link')
+ if (not isinstance(authors, ET.Element) and
+ not isinstance(links, ET.Element)): continue
+ e_entry = ET.SubElement(e_feed, 'entry')
+ author_text = None
+ for author in item_payload.findall(namespace + 'author'):
+ author_email = author.find(namespace + 'email')
+ if author_email is not None:
+ author_text = author_email.text
+ if not author_text:
+ author_uri = author.find(namespace + 'uri')
+ if author_uri is not None:
+ author_text = author_uri.text
+ if not author_text:
+ author_name = author.find(namespace + 'name')
+ if author_name is not None and author_name.text:
+ author_text = author_name.text
+ if not author_text:
+ for uri in item_payload.iter(namespace + 'author'):
+ author_text = uri.text
+ if author_text:
+ ET.SubElement(e_entry, 'title').text = author_text
+ break
+ if isinstance(links, ET.Element):
+ for link in item_payload.findall(namespace + 'link'):
+ link_href = link.attrib['href'] if 'href' in link.attrib else ''
+ link_type = link.attrib['type'] if 'type' in link.attrib else ''
+ link_rel = link.attrib['rel'] if 'rel' in link.attrib else ''
+ ET.SubElement(e_entry, 'link', {'href': link_href, 'rel': link_rel, 'type': link_type})
+ else:
+ ET.SubElement(e_entry, 'content', {'href': ''})
+ link_xmpp = form_an_item_link(pubsub, node, item_id)
+ ET.SubElement(e_entry, 'link', {'href': link_xmpp, 'rel': 'alternate', 'type': 'x-scheme-handler/xmpp'})
+ contents = item_payload.find(namespace + 'content')
+ if isinstance(contents, ET.Element):
+ for content in item_payload.findall(namespace + 'content'):
+ if not content.text: continue
+ content_text = content.text
+ content_type = content.attrib['type'] if 'type' in content.attrib else 'html'
+ content_type_text = 'html' if 'html' in content_type else 'text'
+ ET.SubElement(e_entry, 'content', {'type': content_type_text}).text = content_text
+ else:
+ summary = item_payload.find(namespace + 'summary')
+ summary_text = summary.text if summary else None
+ if summary_text:
+ summary_type = summary.attrib['type'] if 'type' in summary.attrib else 'html'
+ summary_type_text = 'html' if 'html' in summary_type else 'text'
+ ET.SubElement(e_entry, 'content', {'type': summary_type_text}).text = summary_text
+ else:
+ title = item_payload.find(namespace + 'title')
+ title_text = None if title == None else title.text
+ if title_text:
+ ET.SubElement(e_entry, 'content').text = title_text
+ else:
+ ET.SubElement(e_entry, 'content').text = 'No content.'
+ published = item_payload.find(namespace + 'published')
+ published_text = None if published == None else published.text
+ ET.SubElement(e_entry, 'published').text = published_text
+ updated = item_payload.find(namespace + 'updated')
+ updated_text = None if updated == None else updated.text
+ ET.SubElement(e_entry, 'updated').text = updated_text
+ authors = item_payload.find(namespace + 'author')
+ if isinstance(authors, ET.Element):
+ contact_text = None
+ for author in item_payload.findall(namespace + 'author'):
+ author_email = author.find(namespace + 'email')
+ if author_email is not None:
+ contact_text = author_email.text
+ if not contact_text:
+ author_uri = author.find(namespace + 'uri')
+ if author_uri is not None:
+ contact_text = author_uri.text
+ if not contact_text:
+ for uri in item_payload.iter(namespace + 'author'):
+ contact_text = uri.text
+ if contact_text:
+ if contact_text.startswith('xmpp:'):
+ contact_type = 'x-scheme-handler/xmpp'
+ elif contact_text.startswith('mailto:'):
+ contact_type = 'x-scheme-handler/mailto'
+ else:
+ contact_type = None
+ if contact_type:
+ ET.SubElement(e_entry, 'link', {'href': contact_text, 'rel': 'contact', 'type': contact_type})
+ else:
+ ET.SubElement(e_entry, 'link', {'href': contact_text, 'rel': 'contact'})
+ break
+ categories = item_payload.find(namespace + 'category')
+ if isinstance(categories, ET.Element):
+ for category in item_payload.findall(namespace + 'category'):
+ if 'term' in category.attrib and category.attrib['term']:
+ category_term = category.attrib['term']
+ ET.SubElement(e_entry, 'category', {'term': category_term})
+
+
+ identifier = item_payload.find(namespace + 'id')
+ if identifier and identifier.attrib: print(identifier.attrib)
+
+ identifier_text = None if identifier == None else identifier.text
+ ET.SubElement(e_entry, 'id').text = identifier_text
+ # ET.SubElement(e_entry, 'summary', {'type': summary_type_text}).text = summary_text
+ return ET.tostring(e_feed, encoding='unicode')
+
def generate_json(iq):
"""Create a JSON file from node items."""
json_data = []
diff --git a/xsl/atom_as_xhtml.xsl b/xsl/atom_as_xhtml.xsl
index 89ed3bd..03047f5 100644
--- a/xsl/atom_as_xhtml.xsl
+++ b/xsl/atom_as_xhtml.xsl
@@ -299,6 +299,17 @@ xmlns:atom='http://www.w3.org/2005/Atom'>
(XMPP)
+
+
+
+
+
+
+ contact-uri
+
+ 🪪️ Contact
+
+