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 [berlin-xmpp-meetup](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 + +