Rename project to Rivista;

CSS: Improve position of elements;
JS: Improve handling of content in erroneous cases;
JS: Render post as HTML when XSLT does not render correctly;
JS: Handle posts which were tagged as Text instead of HTML and vice versa;
XSLT: Adapt to be viewed on browsers with a lack of support;
XSLT: Add Support for link elements of attribute "rel";
Python: Add elements author and link of attribute "rel".
This commit is contained in:
Schimon Jehudah, Adv. 2024-07-13 22:16:25 +03:00
parent 040d532fb9
commit cb4507bb78
8 changed files with 510 additions and 322 deletions

View file

@ -1,14 +1,14 @@
# XMPP Journal Publisher # Rivista
Previously, XMPP PubSub To Atom ("XPTA"). Previously, XMPP Journal Publisher ("XJP") and XMPP PubSub To Atom ("XPTA").
XMPP Journal Publisher ("XJP") is a software that parses XMPP Pubsub Nodes and sends them as Atom Syndication Format or OPML over HTTP. Rivista is a software that parses XMPP Pubsub Nodes and sends them as Atom Syndication Format or OPML over HTTP.
XJP generates Atom syndication feeds ([RFC 4287](https://www.rfc-editor.org/rfc/rfc4287)) from XMPP PubSub nodes ([XEP-0060](http://xmpp.org/extensions/xep-0060.html)). Rivista generates Atom syndication feeds ([RFC 4287](https://www.rfc-editor.org/rfc/rfc4287)) from XMPP PubSub nodes ([XEP-0060](http://xmpp.org/extensions/xep-0060.html)).
XJP includes [XSLT ](https://www.w3.org/TR/xslt/) stylesheets that transform PubSub nodes into static XHTML journal sites. Rivista includes [XSLT ](https://www.w3.org/TR/xslt/) stylesheets that transform PubSub nodes into static XHTML journal sites.
XJP was inspired from Tigase and was motivated by Movim. Rivista was inspired from Tigase and was motivated by Movim.
## Preview ## Preview
@ -19,14 +19,14 @@ XJP was inspired from Tigase and was motivated by Movim.
## Motivation ## Motivation
XMPP Journal Publisher is a syndication project which makes journals and publications that are hosted on XMPP PubSub nodes, available Rivista is a syndication project which makes journals and publications that are hosted on XMPP PubSub nodes, available
from HTTP to both, XML news readers and even HTML browsers. from HTTP to both, XML news readers and even HTML browsers.
This means that instead of hosting a journal or publication site in the old fashion (i.e. HTML documents hosted on an HTTP server), one only has to have an HTTP server to operate XMPP Journal Publisher, and the rest of the content is delivered from an XMPP server (i.e. PubSub nodes). This means that instead of hosting a journal or publication site in the old fashion (i.e. HTML documents hosted on an HTTP server), one only has to have an HTTP server to operate Rivista, and the rest of the content is delivered from an XMPP server (i.e. PubSub nodes).
The project also showcases the non-necessity of HTML, as it automatically generates valid XHTML pages by HTML browsers (client-side) from XSLT stylesheets. The project also showcases the non-necessity of HTML, as it automatically generates valid XHTML pages by HTML browsers (client-side) from XSLT stylesheets.
Because XMPP Journal Publisher reads XMPP PubSub nodes, it is possible to view a complete set of node items, and even a single node item, which means, that a considered and carefully earnest use of XMPP Journal Publisher would save bandwidth and system overhead, which includes CPU, I/O and RAM usage. Because Rivista reads XMPP PubSub nodes, it is possible to view a complete set of node items, and even a single node item, which means, that a considered and carefully earnest use of Rivista would save bandwidth and system overhead, which includes CPU, I/O and RAM usage.
## Requirements ## Requirements
@ -50,8 +50,8 @@ Because XMPP Journal Publisher reads XMPP PubSub nodes, it is possible to view a
Extract the source package to a directory that you have permission to run software. Extract the source package to a directory that you have permission to run software.
```shell ```shell
$ git clone https://git.xmpp-it.net/sch/XJP $ git clone https://git.xmpp-it.net/sch/Rivista
$ cd XJP/ $ cd Rivista/
``` ```
### Configure ### Configure
@ -60,7 +60,7 @@ Add account credentials to file `configuration.toml`.
### Start ### Start
Execute XMPP Journal Publisher with one of the following commands: Execute Rivista with one of the following commands:
```shell ```shell
$ python -m uvicorn main:app --reload $ python -m uvicorn main:app --reload
@ -148,7 +148,7 @@ Thank you to to Mr. Timothée Jaussoin ([edhelas](https://edhelas.movim.eu/)) wh
A special thank you to the gentlemen "d3x" and "cchianel" from IRC channel #python on irc.libera.chat for initial references concerning code, servers and FastAPI. A special thank you to the gentlemen "d3x" and "cchianel" from IRC channel #python on irc.libera.chat for initial references concerning code, servers and FastAPI.
And an important thank you to Mr. Simone Canaletti ([roughnecks](https://blog.woodpeckersnest.space/)) for testing and deploying XMPP Journal Publisher into production. And an important thank you to Mr. Simone Canaletti ([roughnecks](https://blog.woodpeckersnest.space/)) for testing and deploying Rivista into production.
## Similar Projects ## Similar Projects

View file

@ -1,8 +1,30 @@
/*
TODO
pubsub: news.movim.eu
node: fake-news
item: fdef84f5-e3e1-41ea-9b68-af4bb9130f77
title: #14October2023EpochEclipse
date: Fri, 15 Jan 2021 20:24:46
*/
* { * {
color: #eee; color: #eee;
max-width: 100%; max-width: 100%;
} }
/*
a {
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
*/
body { body {
background: #000; background: #000;
} }
@ -12,26 +34,26 @@ code, pre {
max-height: 100%; max-height: 100%;
max-width: 100%; } max-width: 100%; }
img, video { img, svg, video {
display: block;
max-height: 500px;
width: auto; width: auto;
height: auto; height: auto;
} }
.notice.no-entry {
margin: auto;
/* margin-left: 2%; */
/* margin-top: 2%; */
text-align: center;
padding-top: 100px;
padding-bottom: 150px;
}
h1#title, h2#subtitle, #actions, #references { h1#title, h2#subtitle, #actions, #references {
text-align: center; text-align: center;
text-transform: uppercase; text-transform: uppercase;
line-height: 140%; line-height: 140%;
} }
h5.related > a {
margin-right: 5px;
text-decoration: none;
user-select: none;
}
h3.title > a { h3.title > a {
display: block; display: block;
padding-top: 50px; padding-top: 50px;
@ -59,24 +81,17 @@ h3.title > a {
padding-left: 2%; padding-left: 2%;
} }
#menu > ul > li { #menu > ol > li {
padding: 5px; padding: 5px;
} }
#note {
line-height: 30px;
margin: auto;
margin-top: 0.67em;
max-width: 70%;
padding: 10px;
text-align: center;
}
#references { #references {
margin-top: 5em;
border-top: 1px solid #eee; border-top: 1px solid #eee;
} }
#articles { #articles {
min-height: 80vh;
display: flex; display: flex;
} }
@ -91,10 +106,12 @@ h3.title > a {
#journal { #journal {
margin: auto; margin: auto;
padding-top: 50px;
} }
#articles > ul { #articles > ul {
margin: auto; margin: auto;
margin-left: 2%;
margin-top: 2%; margin-top: 2%;
} }
@ -103,10 +120,7 @@ h3.title > a {
/* height: 500px; */ /* height: 500px; */
line-height: 160%; line-height: 160%;
overflow: auto; overflow: auto;
} word-wrap: break-word;
#articles #journal > a {
font-weight: bold;
} }
#articles div.content { #articles div.content {
@ -174,7 +188,91 @@ h3.title > a {
text-decoration: underline; text-decoration: underline;
} }
@media (max-width: 1525px) { .enclosures {
cursor: help;
direction: ltr;
margin: 5px auto 15px 1%;
padding: 15px;
padding: 1em;
/* background: #222;
border: 1px solid GrayText;
border-radius: 4px;
border-radius: .5em;
color: #525c66;
border-left: double;
max-width: 40%; */
}
.enclosure a {
margin: 3px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.enclosure > span[icon='atom']:after,
.enclosure > span[icon='html5']:after,
.enclosure > span[icon='rss']:after {
content: '📰';
margin:3px;
}
.enclosure > span.audio:after {
content: ' (Audio file) ';
}
.enclosure > span[icon='audio']:after{
content: '🎼️';
margin: 3px;
}
.enclosure > span:after {
content: ' (Document file) ';
}
.enclosure > span[icon]:after {
content: '📄️';
margin: 3px;
}
.enclosure > span.executable:after{
content: ' (Executable file) ';
}
.enclosure > span[icon='executable']:after {
content: '📦️';
margin: 3px;
}
.enclosure > span.image:after {
content: ' (Image file) ';
}
.enclosure > span[icon='image']:after {
content: '🖼️';
margin: 3px;
}
.enclosure > span.video:after {
content: ' (Video file) ';
}
.enclosure > span[icon='video']:after {
content: '📽️';
margin:3px;
}
#note {
line-height: 30px;
margin: auto;
margin-top: 0.67em;
max-width: 70%;
padding: 10px;
text-align: center;
user-select: none;
}
@media (max-width: 1550px) {
#articles { #articles {
display: unset; display: unset;
} }

View file

@ -8,6 +8,7 @@ from fastapi.staticfiles import StaticFiles
import json import json
from slixmpp import ClientXMPP from slixmpp import ClientXMPP
from slixmpp.exceptions import IqError, IqTimeout from slixmpp.exceptions import IqError, IqTimeout
import xml
import xml.etree.ElementTree as ET import xml.etree.ElementTree as ET
#import importlib.resources #import importlib.resources
@ -56,17 +57,15 @@ async def view_pubsub_nodes(request: Request):
if iq: if iq:
link = 'xmpp:{pubsub}'.format(pubsub=pubsub) link = 'xmpp:{pubsub}'.format(pubsub=pubsub)
xml_opml = generate_opml(iq) xml_opml = generate_opml(iq)
result = append_stylesheet(xml_opml, 'opml.xsl') result = append_stylesheet(xml_opml, 'opml')
else: else:
text = 'Please check that PubSub Jabber ID is valid and accessible.' text = 'Please check that PubSub Jabber ID is valid and accessible.'
xml_atom = error_message(text) xml_atom = error_message(text)
result = append_stylesheet( result = append_stylesheet(xml_atom, 'atom')
xml_atom, 'atom.xsl', namespace='http://www.w3.org/2005/Atom')
else: else:
text = 'The given domain {} is not allowed.'.format(pubsub) text = 'The given domain {} is not allowed.'.format(pubsub)
xml_atom = error_message(text) xml_atom = error_message(text)
result = append_stylesheet( result = append_stylesheet(xml_atom, 'atom')
xml_atom, 'atom.xsl', namespace='http://www.w3.org/2005/Atom')
default = get_configuration('default') default = get_configuration('default')
if not result: if not result:
if default['pubsub']: if default['pubsub']:
@ -75,17 +74,16 @@ async def view_pubsub_nodes(request: Request):
iq = await get_nodes(pubsub) iq = await get_nodes(pubsub)
link = 'xmpp:{pubsub}'.format(pubsub=pubsub) link = 'xmpp:{pubsub}'.format(pubsub=pubsub)
xml_opml = generate_opml(iq) xml_opml = generate_opml(iq)
result = append_stylesheet(xml_opml, 'opml.xsl') result = append_stylesheet(xml_opml, 'opml')
elif not settings['service']: elif not settings['service']:
pubsub = default['pubsub'] pubsub = default['pubsub']
link = 'xmpp:{pubsub}'.format(pubsub=pubsub) link = 'xmpp:{pubsub}'.format(pubsub=pubsub)
xml_opml = generate_opml(iq) xml_opml = generate_opml(iq)
result = append_stylesheet(xml_opml, 'opml.xsl') result = append_stylesheet(xml_opml, 'opml')
else: else:
text = 'Please contact the administrator and ask him to set default PubSub and Node ID.' text = 'Please contact the administrator and ask him to set default PubSub and Node ID.'
xml_atom = error_message(text) xml_atom = error_message(text)
result = append_stylesheet( result = append_stylesheet(xml_atom, 'atom')
xml_atom, 'atom.xsl', namespace='http://www.w3.org/2005/Atom')
response = Response(content=result, media_type="application/xml") response = Response(content=result, media_type="application/xml")
return response return response
@ -115,11 +113,12 @@ async def view_node_items(request: Request):
else: else:
operator = get_configuration('settings')['operator'] operator = get_configuration('settings')['operator']
json_data = [{'title' : 'Error retrieving items list.', json_data = [{'title' : 'Error retrieving items list.',
'link' : ('javascript:alert("XJP: XMPP Journal Publisher has experienced an error ' 'link' : ('javascript:alert("Rivista has experienced an error '
'while attempting to retrieve the list of items for Node {} of PubSub {}.")') 'while attempting to retrieve the list of items for '
'Node {} of PubSub {}.")')
.format(node, pubsub)}, .format(node, pubsub)},
{'title' : 'Contact the operator.', {'title' : 'Contact the operator.',
'link' : ('xmpp:{}?message;subject=XJP: XMPP Journal Publisher;body=Greetings! ' 'link' : ('xmpp:{}?message;subject=Rivista;body=Greetings! '
'I am contacting you to inform you that there is an error listing ' 'I am contacting you to inform you that there is an error listing '
'node items for Node {} on PubSub {}.').format(operator, node, pubsub)}] 'node items for Node {} on PubSub {}.').format(operator, node, pubsub)}]
filename = 'data/{}.json'.format(node) filename = 'data/{}.json'.format(node)
@ -128,8 +127,7 @@ async def view_node_items(request: Request):
else: else:
text = 'Please check that PubSub node and item are valid and accessible.' text = 'Please check that PubSub node and item are valid and accessible.'
xml_atom = error_message(text) xml_atom = error_message(text)
result = append_stylesheet( result = append_stylesheet(xml_atom, 'atom')
xml_atom, 'atom.xsl', namespace='http://www.w3.org/2005/Atom')
# try: # try:
# iq = await get_node_items(pubsub, node) # iq = await get_node_items(pubsub, node)
@ -149,26 +147,22 @@ async def view_node_items(request: Request):
else: else:
text = 'Please check that PubSub node is valid and accessible.' text = 'Please check that PubSub node is valid and accessible.'
xml_atom = error_message(text) xml_atom = error_message(text)
result = append_stylesheet( result = append_stylesheet(xml_atom, 'atom')
xml_atom, 'atom.xsl', namespace='http://www.w3.org/2005/Atom')
elif pubsub: elif pubsub:
text = 'Node parameter is missing.' text = 'Node parameter is missing.'
xml_atom = error_message(text) xml_atom = error_message(text)
result = append_stylesheet( result = append_stylesheet(xml_atom, 'atom')
xml_atom, 'atom.xsl', namespace='http://www.w3.org/2005/Atom')
elif node: elif node:
text = 'PubSub parameter is missing.' text = 'PubSub parameter is missing.'
xml_atom = error_message(text) xml_atom = error_message(text)
result = append_stylesheet( result = append_stylesheet(xml_atom, 'atom')
xml_atom, 'atom.xsl', namespace='http://www.w3.org/2005/Atom')
# else: # else:
# result = ('Mandatory parameter PubSub and ' # result = ('Mandatory parameter PubSub and '
# 'optional parameter Node are missing.') # 'optional parameter Node are missing.')
else: else:
text = 'The given domain {} is not allowed.'.format(pubsub) text = 'The given domain {} is not allowed.'.format(pubsub)
xml_atom = error_message(text) xml_atom = error_message(text)
result = append_stylesheet( result = append_stylesheet(xml_atom, 'atom')
xml_atom, 'atom.xsl', namespace='http://www.w3.org/2005/Atom')
default = get_configuration('default') default = get_configuration('default')
if not result: if not result:
if default['pubsub'] and default['nodeid']: if default['pubsub'] and default['nodeid']:
@ -178,21 +172,18 @@ async def view_node_items(request: Request):
iq = await get_node_items(pubsub, node) iq = await get_node_items(pubsub, node)
link = form_a_node_link(pubsub, node) link = form_a_node_link(pubsub, node)
xml_atom = generate_atom(iq, link) xml_atom = generate_atom(iq, link)
result = append_stylesheet( result = append_stylesheet(xml_atom, 'atom')
xml_atom, 'atom.xsl', namespace='http://www.w3.org/2005/Atom')
elif not settings['service']: elif not settings['service']:
pubsub = default['pubsub'] pubsub = default['pubsub']
node = default['nodeid'] node = default['nodeid']
iq = await get_node_items(pubsub, node) iq = await get_node_items(pubsub, node)
link = form_a_node_link(pubsub, node) link = form_a_node_link(pubsub, node)
xml_atom = generate_atom(iq, link) xml_atom = generate_atom(iq, link)
result = append_stylesheet( result = append_stylesheet(xml_atom, 'atom')
xml_atom, 'atom.xsl', namespace='http://www.w3.org/2005/Atom')
else: else:
text = 'Please contact the administrator and ask him to set default PubSub and Node ID.' text = 'Please contact the administrator and ask him to set default PubSub and Node ID.'
xml_atom = error_message(text) xml_atom = error_message(text)
result = append_stylesheet( result = append_stylesheet(xml_atom, 'atom')
xml_atom, 'atom.xsl', namespace='http://www.w3.org/2005/Atom')
response = Response(content=result, media_type="application/xml") response = Response(content=result, media_type="application/xml")
return response return response
@ -234,24 +225,25 @@ def form_an_item_link(pubsub, node, item_id):
def error_message(text): def error_message(text):
"""Error message in RFC 4287: The Atom Syndication Format.""" """Error message in RFC 4287: The Atom Syndication Format."""
title = 'StreamBurner' title = 'Rivista'
subtitle = 'XMPP Journal Publisher' subtitle = 'XMPP Journal Publisher'
description = ('This is a syndication feed generated with XMPP Journal ' description = ('This is a syndication feed generated with Rivista, an XMPP '
'Publisher, which conveys XEP-0060: Publish-Subscribe ' 'Journal Publisher, which conveys XEP-0060: Publish-'
'nodes to standard RFC 4287: The Atom Syndication Format.') 'Subscribe nodes to standard RFC 4287: The Atom Syndication '
'Format.')
language = 'en' language = 'en'
feed = ET.Element("feed") feed = ET.Element("feed")
feed.set('xmlns', 'http://www.w3.org/2005/Atom') feed.set('xmlns', 'http://www.w3.org/2005/Atom')
ET.SubElement(feed, 'title', {'type': 'text'}).text = title ET.SubElement(feed, 'title', {'type': 'text'}).text = title
ET.SubElement(feed, 'subtitle', {'type': 'text'}).text = subtitle ET.SubElement(feed, 'subtitle', {'type': 'text'}).text = subtitle
ET.SubElement(feed, 'author', {'name':'XMPP Journal Publisher','email':'xjp@schimon.i2p'}) ET.SubElement(feed, 'author', {'name':'Rivista','email':'rivista@schimon.i2p'})
ET.SubElement(feed, 'generator', { ET.SubElement(feed, 'generator', {
'uri': 'https://git.xmpp-it.net/sch/XJP', 'uri': 'https://git.xmpp-it.net/sch/Rivista',
'version': '0.1'}).text = 'XMPP Journal Publisher (XJP)' 'version': '0.1'}).text = 'Rivista'
ET.SubElement(feed, 'updated').text = datetime.datetime.now(datetime.UTC).isoformat() ET.SubElement(feed, 'updated').text = datetime.datetime.now(datetime.UTC).isoformat()
entry = ET.SubElement(feed, 'entry') entry = ET.SubElement(feed, 'entry')
ET.SubElement(entry, 'title').text = 'Error' ET.SubElement(entry, 'title').text = 'Error'
ET.SubElement(entry, 'id').text = 'xjp-error' ET.SubElement(entry, 'id').text = 'rivista-error'
ET.SubElement(entry, 'updated').text = datetime.datetime.now(datetime.UTC).isoformat() ET.SubElement(entry, 'updated').text = datetime.datetime.now(datetime.UTC).isoformat()
ET.SubElement(entry, 'published').text = datetime.datetime.now(datetime.UTC).isoformat() ET.SubElement(entry, 'published').text = datetime.datetime.now(datetime.UTC).isoformat()
# ET.SubElement(entry, 'summary', {'type': summary_type_text}).text = summary_text # ET.SubElement(entry, 'summary', {'type': summary_type_text}).text = summary_text
@ -268,55 +260,77 @@ def generate_atom(iq, link):
# link = form_a_node_link(pubsub, node) # link = form_a_node_link(pubsub, node)
# subtitle = 'XMPP PubSub Syndication Feed' # subtitle = 'XMPP PubSub Syndication Feed'
subtitle = pubsub subtitle = pubsub
description = ('This is a syndication feed generated with XMPP Journal ' description = ('This is a syndication feed generated with Rivista, an XMPP '
'Publisher, which conveys XEP-0060: Publish-Subscribe ' 'Journal Publisher, which conveys XEP-0060: Publish-'
'nodes to standard RFC 4287: The Atom Syndication Format.') 'Subscribe nodes to standard RFC 4287: The Atom Syndication '
'Format.')
language = iq['pubsub']['items']['lang'] language = iq['pubsub']['items']['lang']
items = iq['pubsub']['items'] items = iq['pubsub']['items']
feed = ET.Element("feed") e_feed = ET.Element("feed")
feed.set('xmlns', 'http://www.w3.org/2005/Atom') e_feed.set('xmlns', 'http://www.w3.org/2005/Atom')
ET.SubElement(feed, 'title', {'type': 'text'}).text = title ET.SubElement(e_feed, 'title', {'type': 'text'}).text = title
ET.SubElement(feed, 'subtitle', {'type': 'text'}).text = subtitle ET.SubElement(e_feed, 'subtitle', {'type': 'text'}).text = subtitle
ET.SubElement(feed, 'link', {'rel': 'self', 'href': link}) ET.SubElement(e_feed, 'link', {'rel': 'self', 'href': link})
ET.SubElement(feed, 'generator', { ET.SubElement(e_feed, 'generator', {
'uri': 'https://git.xmpp-it.net/sch/XJP', 'uri': 'https://git.xmpp-it.net/sch/Rivista',
'version': '0.1'}).text = 'XMPP Journal Publisher (XJP)' 'version': '0.1'}).text = 'Rivista'
ET.SubElement(feed, 'updated').text = datetime.datetime.now(datetime.UTC).isoformat() ET.SubElement(e_feed, 'updated').text = datetime.datetime.now(datetime.UTC).isoformat()
for item in items: for item in items:
item_id = item['id'] item_id = item['id']
item_payload = item['payload'] item_payload = item['payload']
namespace = '{http://www.w3.org/2005/Atom}' namespace = '{http://www.w3.org/2005/Atom}'
title = item_payload.find(namespace + 'title') title = item_payload.find(namespace + 'title')
links = item_payload.find(namespace + 'link')
if (not isinstance(title, xml.etree.ElementTree.Element) and
not isinstance(links, xml.etree.ElementTree.Element)): continue
title_text = None if title == None else title.text title_text = None if title == None else title.text
# link = item_payload.find(namespace + 'link') e_entry = ET.SubElement(e_feed, 'entry')
# link_href = '' if link == None else link.attrib['href'] ET.SubElement(e_entry, 'title').text = title_text
link_href = form_an_item_link(pubsub, node, item_id) if isinstance(links, xml.etree.ElementTree.Element):
if not title_text or not link_href: continue for link in item_payload.findall(namespace + 'link'):
content = item_payload.find(namespace + 'content') link_href = link.attrib['href'] if 'href' in link.attrib else ''
content_text = 'No content' if content == None else content.text link_type = link.attrib['type'] if 'type' in link.attrib else ''
if content: link_rel = link.attrib['rel'] if 'rel' in link.attrib else ''
content_type = content.attrib['type'] if 'type' in content.attrib else 'text' ET.SubElement(e_entry, 'link', {'href': link_href, 'rel': link_rel, 'type': link_type})
content_type_text = 'html' if 'html' in content_type else 'text'
else: else:
content_type_text = 'html' 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, xml.etree.ElementTree.Element):
for content in item_payload.findall(namespace + 'content'):
content_text = content.text if content.text else 'No content.'
content_type = content.attrib['type'] if 'type' in content.attrib else 'html'
content_type_text = 'html' if 'html' in content_type else 'html'
ET.SubElement(e_entry, 'content', {'type': content_type_text}).text = content_text
else:
ET.SubElement(e_entry, 'content').text = 'No content.'
published = item_payload.find(namespace + 'published') published = item_payload.find(namespace + 'published')
published_text = None if published == None else published.text published_text = None if published == None else published.text
ET.SubElement(e_entry, 'published').text = published_text
updated = item_payload.find(namespace + 'updated') updated = item_payload.find(namespace + 'updated')
updated_text = None if updated == None else updated.text updated_text = None if updated == None else updated.text
author = item_payload.find(namespace + 'author') ET.SubElement(e_entry, 'updated').text = updated_text
e_author = ET.SubElement(e_entry, 'author')
authors = item_payload.find(namespace + 'author')
if isinstance(authors, xml.etree.ElementTree.Element):
for author in item_payload.findall(namespace + 'author'):
if not author.text: continue
author_text = author.text
author_email = link.attrib['href'] if 'href' in link.attrib else ''
author_uri = link.attrib['type'] if 'type' in link.attrib else ''
author_summary = link.attrib['rel'] if 'rel' in link.attrib else ''
ET.SubElement(e_author, 'name').text = author_text
if author and author.attrib: print(author.attrib) if author and author.attrib: print(author.attrib)
identifier = item_payload.find(namespace + 'id') identifier = item_payload.find(namespace + 'id')
if identifier and identifier.attrib: print(identifier.attrib) if identifier and identifier.attrib: print(identifier.attrib)
identifier_text = 'None' if identifier == None else identifier.text
entry = ET.SubElement(feed, 'entry') identifier_text = None if identifier == None else identifier.text
ET.SubElement(entry, 'title').text = title_text ET.SubElement(e_entry, 'id').text = identifier_text
ET.SubElement(entry, 'link', {'href': link_href}) # ET.SubElement(e_entry, 'summary', {'type': summary_type_text}).text = summary_text
ET.SubElement(entry, 'id').text = identifier_text return ET.tostring(e_feed, encoding='unicode')
ET.SubElement(entry, 'updated').text = updated_text
ET.SubElement(entry, 'published').text = published_text
# ET.SubElement(entry, 'summary', {'type': summary_type_text}).text = summary_text
ET.SubElement(entry, 'content', {'type': content_type_text}).text = content_text
return ET.tostring(feed, encoding='unicode')
def generate_json(iq): def generate_json(iq):
"""Create a JSON file from node items.""" """Create a JSON file from node items."""
@ -344,25 +358,11 @@ def generate_json(iq):
with open(filename, 'w', encoding='utf-8') as f: with open(filename, 'w', encoding='utf-8') as f:
json.dump(json_data, f, ensure_ascii=False, indent=4) json.dump(json_data, f, ensure_ascii=False, indent=4)
"""Patch function to append elements which are not provided by feedgenerator"""
def append_element(xml_data, element, text):
root = ET.fromstring(xml_data)
# Create the generator element
generator_element = ET.Element(element)
generator_element.text = text
# Append the generator element to the root
root.append(generator_element)
# Return the modified XML as a string
return ET.tostring(root, encoding='unicode')
"""Patch function to append XSLT reference to XML""" """Patch function to append XSLT reference to XML"""
"""Why is not this a built-in function of ElementTree or LXML""" """Why is not this a built-in function of ElementTree or LXML"""
def append_stylesheet(xml_data, filename, namespace=None): def append_stylesheet(xml_data, type):
# Register namespace in order to avoide ns0: # Register namespace in order to avoide ns0:
if namespace: ET.register_namespace("", namespace) if type == 'atom': ET.register_namespace('', 'http://www.w3.org/2005/Atom')
# Load XML from string # Load XML from string
tree = ET.fromstring(xml_data) tree = ET.fromstring(xml_data)
# The following direction removes the XML declaration # The following direction removes the XML declaration
@ -370,7 +370,7 @@ def append_stylesheet(xml_data, filename, namespace=None):
# Add XML declaration and stylesheet # Add XML declaration and stylesheet
xml_data_declaration = ( xml_data_declaration = (
'<?xml version="1.0" encoding="utf-8"?>' '<?xml version="1.0" encoding="utf-8"?>'
'<?xml-stylesheet type="text/xsl" href="xsl/{}"?>'.format(filename) + '<?xml-stylesheet type="text/xsl" href="xsl/{}.xsl"?>'.format(type) +
xml_data_without_a_declaration) xml_data_without_a_declaration)
return xml_data_declaration return xml_data_declaration
@ -380,11 +380,11 @@ def generate_opml(iq):
opml = ET.Element("opml") opml = ET.Element("opml")
opml.set("version", "1.0") opml.set("version", "1.0")
head = ET.SubElement(opml, "head") head = ET.SubElement(opml, "head")
ET.SubElement(head, "title").text = pubsub ET.SubElement(head, "title").text = 'An OPML of ' + pubsub
ET.SubElement(head, "description").text = ( ET.SubElement(head, "description").text = (
"PubSub Nodes of {}").format(pubsub) "PubSub Nodes of {}").format(pubsub)
ET.SubElement(head, "generator").text = 'XMPP Journal Publisher (XJP)' ET.SubElement(head, "generator").text = 'Rivista'
ET.SubElement(head, "urlPublic").text = 'https://git.xmpp-it.net/sch/XJP' ET.SubElement(head, "urlPublic").text = 'https://git.xmpp-it.net/sch/Rivista'
time_stamp = datetime.datetime.now(datetime.UTC).isoformat() time_stamp = datetime.datetime.now(datetime.UTC).isoformat()
ET.SubElement(head, "dateCreated").text = time_stamp ET.SubElement(head, "dateCreated").text = time_stamp
ET.SubElement(head, "dateModified").text = time_stamp ET.SubElement(head, "dateModified").text = time_stamp

View file

@ -2,7 +2,8 @@ window.onload = async function(){
let locationHref = new URL(location.href); let locationHref = new URL(location.href);
let node = locationHref.searchParams.get('node') let node = locationHref.searchParams.get('node')
let pubsub = locationHref.searchParams.get('pubsub') let pubsub = locationHref.searchParams.get('pubsub')
// Fix button follow
// Set button follow
let follow = document.querySelector('#follow'); let follow = document.querySelector('#follow');
if (follow) { if (follow) {
//let feedUrl = location.href.replace(/^https?:/, 'feed:'); //let feedUrl = location.href.replace(/^https?:/, 'feed:');
@ -14,6 +15,7 @@ window.onload = async function(){
// Fix button subtome // Fix button subtome
document.querySelector('#subtome').href='https://www.subtome.com/#/subscribe?feeds=' + feedUrl; document.querySelector('#subtome').href='https://www.subtome.com/#/subscribe?feeds=' + feedUrl;
} }
// Convert ISO8601 To UTC // Convert ISO8601 To UTC
for (let element of document.querySelectorAll( for (let element of document.querySelectorAll(
'#articles > ul > li > div > h4.published,' + '#articles > ul > li > div > h4.published,' +
@ -22,11 +24,62 @@ window.onload = async function(){
let timeStamp = new Date(element.textContent); let timeStamp = new Date(element.textContent);
element.textContent = timeStamp.toUTCString(); element.textContent = timeStamp.toUTCString();
} }
// Parse Markdown // Parse Markdown
for (let element of document.querySelectorAll('#articles div[type="text"]')) { for (let element of document.querySelectorAll('#articles div[type="text"]')) {
let markDown = element.textContent element.innerHTML = marked.parse(element.textContent);
element.innerHTML = marked.parse(markDown);
} }
// NOTE Report this issue to Movim. See node "deltachat" of pubsub "news.movim.eu".
for (let element of document.querySelectorAll('#articles div[type="html"]')) {
if (!element.children.length) {
element.innerHTML = marked.parse(element.textContent);
}
}
/*
NOTE
The reason for the following code to parse HTML inside an software which
already parses HTML, is that some people who influence the so called Gecko
HTML Engine product (mostly people from the advertisement industry with whom
I shamefully used to work with) have intentions to eliminate standards and
technologies such as Syndication (Atom/RDF/RSS), XSLT and many other
technologies that actually empower people and their privacy.
Recently, some changes were made to the XSLT parser of Gecko which result in
noncompliance with the XSLT standard.
The XSLT feature that was removed is "disable-output-escaping" which upon the
value "yes" the XSLT engine should transform and treat a subject string into
HTML, yet the Gecko HTML Engine ignores this XSLT direction.
This change was probably made in order to:
* Frustrate new XSLT developers; or
* Influence new XSLT developers to think that XSLT has a limited set of
features and believing of some sort of inability to parse HTML from a
retrieved HTML string; and
* Consequently cause developers to abstain from using the XSLT technology.
Do not use HTML browsers or use Ladybird, Pale Moon or browsers that are
powered by KHTML (WebKit) instead of anti-privacy software such as Chromium
and Gecko.
*/
// Parse HTML
//if (navigator.userAgent.includes(') Gecko/')) {
// for (let element of document.querySelectorAll('#articles div[type="html"]')) {
// element.innerHTML = element.textContent;
// }
//}
for (let element of document.querySelectorAll('#articles div[type="html"]')) {
if (!element.children.length) {
element.innerHTML = element.textContent;
}
}
// Build a journal list // Build a journal list
if (node) { if (node) {
itemsList = await openJson(node) itemsList = await openJson(node)
@ -79,8 +132,11 @@ window.onload = async function(){
document.querySelector('#articles').appendChild(elementDiv); document.querySelector('#articles').appendChild(elementDiv);
} }
} }
// Convert URI xmpp: to URI http: links. // Convert URI xmpp: to URI http: links.
for (let xmppLink of document.querySelectorAll('#articles > ul a[href^="xmpp:"], ol a[href^="xmpp:"]')) { for (let xmppLink of document.querySelectorAll(
'#articles > ul > li > div > h3 > a[href^="xmpp:"],' +
'#journal > ol > li > a[href^="xmpp:"]')) {
xmppUri = new URL(xmppLink); xmppUri = new URL(xmppLink);
let parameters = xmppUri.search.split(';'); let parameters = xmppUri.search.split(';');
try { try {
@ -98,6 +154,7 @@ window.onload = async function(){
console.warn(err) console.warn(err)
} }
} }
// Display a selection of suggested software. // Display a selection of suggested software.
const selection = { const selection = {
'akregator' : { 'akregator' : {
@ -144,14 +201,14 @@ window.onload = async function(){
elementH1.textContent = 'Get A News Reader'; elementH1.textContent = 'Get A News Reader';
elementDiv.appendChild(elementH1); elementDiv.appendChild(elementH1);
let elementH2 = document.createElement('h2'); let elementH2 = document.createElement('h2');
elementH2.textContent = 'Install Feed Reader Apps For Desktop And Mobile'; elementH2.textContent = 'Install A Feed Reader For Desktop And Mobile';
elementDiv.appendChild(elementH2); elementDiv.appendChild(elementH2);
const brands = Object.keys(selection); const brands = Object.keys(selection);
let elementDivSel = document.createElement('div'); let elementDivSel = document.createElement('div');
elementDivSel.id = 'selection'; elementDivSel.id = 'selection';
for (let i = 0; i < brands.length; i++) { for (let i = 0; i < brands.length; i++) {
let brand = brands[i]; let brand = brands[i];
elementSpan = document.createElement('span'); let elementSpan = document.createElement('span');
let elementA = document.createElement('a'); let elementA = document.createElement('a');
elementA.href = selection[brand].url; elementA.href = selection[brand].url;
elementA.textContent = selection[brand].name; elementA.textContent = selection[brand].name;
@ -162,20 +219,24 @@ window.onload = async function(){
elementDivSel.appendChild(elementSpan); elementDivSel.appendChild(elementSpan);
elementDiv.appendChild(elementDivSel); elementDiv.appendChild(elementDivSel);
} }
let elementP = document.createElement('p'); let elementP1 = document.createElement('p');
elementP.textContent = '' + elementP1.textContent = '' +
'This is a selection of desktop applications, mobile apps and online ' + 'This is a selection of desktop, mobile and HTML (sometimes referred to ' +
'services for you to choose from. This selection includes news ' + 'as "online") News Readers for you to choose from.';
'readers, podcast managers, torrent clients, chat bots, HTML browsers ' + elementDiv.appendChild(elementP1);
'and plugins which support syndication feeds.'; let elementP2 = document.createElement('p');
elementDiv.appendChild(elementP); elementP2.textContent = '' +
let elementDivReturn = document.createElement('div'); 'This selection includes: Podcast Managers, Torrent ' +
elementDivReturn.id = 'return'; 'Clients, Chat Bots, HTML Browsers and Plugins which support ' +
elementDivReturn.textContent = 'Return To PubSub...'; 'syndication feeds.';
elementDivReturn.addEventListener ('click', function() { elementDiv.appendChild(elementP2);
let elementSpan = document.createElement('span');
elementSpan.id = 'return';
elementSpan.textContent = 'Return To PubSub';
elementSpan.addEventListener ('click', function() {
document.querySelector('#selection-page').remove(); document.querySelector('#selection-page').remove();
}); });
elementDiv.appendChild(elementDivReturn); elementDiv.appendChild(elementSpan);
document.body.appendChild(elementDiv); document.body.appendChild(elementDiv);
}); });
} }

View file

@ -1,17 +1,19 @@
<?xml version="1.0" encoding="UTF-8" ?> <?xml version="1.0" encoding="UTF-8" ?>
<!-- <!--
Copyright (C) 2016 - 2017 Schimon Jehuda. Released under MIT license Copyright (C) 2016 - 2024 Schimon Jehuda. Released under MIT license
Feeds rendered using this XSLT stylesheet, or it's derivatives, must Feeds rendered using this XSLT stylesheet, or it's derivatives, must
include https://schimon.i2p/ in attribute name='generator' of include https://schimon.i2p/ in attribute name='generator' of
element <meta/> inside of html element </head> element <meta/> inside of html element </head>
--> -->
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method = 'html' <xsl:output encoding = 'UTF-8'
indent = 'yes' indent = 'yes'
omit-xml-decleration='no' /> media-type = 'application/atom+xml'
<!-- Atom 1.0 Syndication Format --> method = 'html'
version = '4.01' />
<!-- Atom Syndication Format 1.0 -->
<xsl:include href='atom_as_xhtml.xsl'/> <xsl:include href='atom_as_xhtml.xsl'/>
<!-- extract filename from given url string --> <!-- extract filename from given url string -->
<xsl:include href='extract-filename.xsl'/> <xsl:include href='extract-filename.xsl'/>

View file

@ -15,9 +15,6 @@ xmlns:georss='http://www.georss.org/georss'
xmlns:geo='http://www.w3.org/2003/01/geo/wgs84_pos#' xmlns:geo='http://www.w3.org/2003/01/geo/wgs84_pos#'
xmlns:atom10='http://www.w3.org/2005/Atom' xmlns:atom10='http://www.w3.org/2005/Atom'
xmlns:atom='http://www.w3.org/2005/Atom'> xmlns:atom='http://www.w3.org/2005/Atom'>
<!-- Atom 1.0 Syndication Format -->
<xsl:output
media-type='application/atom+xml' />
<xsl:template match='/atom:feed'> <xsl:template match='/atom:feed'>
<!-- index right-to-left language codes --> <!-- index right-to-left language codes -->
<!-- TODO http://www.w3.org/TR/xpath/#function-lang --> <!-- TODO http://www.w3.org/TR/xpath/#function-lang -->
@ -38,7 +35,7 @@ xmlns:atom='http://www.w3.org/2005/Atom'>
</xsl:call-template> </xsl:call-template>
<xsl:call-template name='metadata'> <xsl:call-template name='metadata'>
<xsl:with-param name='name' select='"generator"' /> <xsl:with-param name='name' select='"generator"' />
<xsl:with-param name='content' select='StreamBurner' /> <xsl:with-param name='content' select='Rivista' />
</xsl:call-template> </xsl:call-template>
<xsl:call-template name='metadata'> <xsl:call-template name='metadata'>
<xsl:with-param name='name' select='"mimetype"' /> <xsl:with-param name='name' select='"mimetype"' />
@ -52,7 +49,7 @@ xmlns:atom='http://www.w3.org/2005/Atom'>
<xsl:when test='atom:entry'> <xsl:when test='atom:entry'>
<xsl:value-of select='atom:entry/atom:title'/> <xsl:value-of select='atom:entry/atom:title'/>
</xsl:when> </xsl:when>
<xsl:otherwise>StreamBurner</xsl:otherwise> <xsl:otherwise>Rivista</xsl:otherwise>
</xsl:choose> </xsl:choose>
</title> </title>
<!-- TODO media='print' --> <!-- TODO media='print' -->
@ -90,15 +87,6 @@ xmlns:atom='http://www.w3.org/2005/Atom'>
</xsl:attribute> </xsl:attribute>
SubToMe SubToMe
</a> </a>
<a href='https://git.xmpp-it.net/sch/PubSubToAtom'
title='About PubSub To Atom.'>
About
</a>
<a href='https://aboutfeeds.com/'
title='Of the benefits of syndication feed.'
id='aboutfeeds'>
Feeds
</a>
<a href='https://xmpp.org/about/technology-overview/' <a href='https://xmpp.org/about/technology-overview/'
title='Of the benefits of XMPP.'> title='Of the benefits of XMPP.'>
XMPP XMPP
@ -107,6 +95,11 @@ xmlns:atom='http://www.w3.org/2005/Atom'>
title='Syndictaion and PubSub.'> title='Syndictaion and PubSub.'>
Groupchat Groupchat
</a> </a>
<a href='https://aboutfeeds.com/'
title='Of the benefits of syndication feed.'
id='aboutfeeds'>
Help
</a>
</div> </div>
<div id='feed'> <div id='feed'>
<div id='header'> <div id='header'>
@ -120,7 +113,7 @@ xmlns:atom='http://www.w3.org/2005/Atom'>
<xsl:value-of select='atom:entry/atom:title'/> <xsl:value-of select='atom:entry/atom:title'/>
</xsl:when> </xsl:when>
<xsl:otherwise> <xsl:otherwise>
StreamBurner No title
</xsl:otherwise> </xsl:otherwise>
</xsl:choose> </xsl:choose>
</h1> </h1>
@ -138,6 +131,9 @@ xmlns:atom='http://www.w3.org/2005/Atom'>
<xsl:attribute name='class'><xsl:text>date</xsl:text></xsl:attribute> <xsl:attribute name='class'><xsl:text>date</xsl:text></xsl:attribute>
<xsl:value-of select='atom:entry/atom:published'/> <xsl:value-of select='atom:entry/atom:published'/>
</xsl:when> </xsl:when>
<xsl:otherwise>
Rivista XMPP Journal Publisher
</xsl:otherwise>
</xsl:choose> </xsl:choose>
</h2> </h2>
</div> </div>
@ -145,12 +141,12 @@ xmlns:atom='http://www.w3.org/2005/Atom'>
<div id='menu'> <div id='menu'>
<h3>Latest Posts</h3> <h3>Latest Posts</h3>
<!-- xsl:for-each select='atom:entry[position() &lt;21]' --> <!-- xsl:for-each select='atom:entry[position() &lt;21]' -->
<ul> <ol>
<xsl:for-each select='atom:entry[not(position() &gt; 20)]'> <xsl:for-each select='atom:entry[not(position() &gt; 20)]'>
<li> <li>
<xsl:element name='a'> <xsl:element name='a'>
<xsl:attribute name='href'> <xsl:attribute name='href'>
<xsl:text>#stremburner-</xsl:text> <xsl:text>#rivista-</xsl:text>
<xsl:value-of select='position()'/> <xsl:value-of select='position()'/>
</xsl:attribute> </xsl:attribute>
<xsl:choose> <xsl:choose>
@ -164,7 +160,7 @@ xmlns:atom='http://www.w3.org/2005/Atom'>
</xsl:element> </xsl:element>
</li> </li>
</xsl:for-each> </xsl:for-each>
</ul> </ol>
</div> </div>
</xsl:if> </xsl:if>
<div id='articles'> <div id='articles'>
@ -180,19 +176,16 @@ xmlns:atom='http://www.w3.org/2005/Atom'>
<xsl:element name='a'> <xsl:element name='a'>
<xsl:attribute name='href'> <xsl:attribute name='href'>
<xsl:choose> <xsl:choose>
<xsl:when test='atom:link[contains(@rel,"alternate")]'> <xsl:when test='atom:link[@rel="self"]'>
<xsl:value-of select='atom:link[contains(@rel,"alternate")]/@href'/> <xsl:value-of select='atom:link[@rel="self"]/@href'/>
</xsl:when> </xsl:when>
<xsl:otherwise> <xsl:otherwise>
<xsl:value-of select='atom:link/@href'/> <xsl:value-of select='atom:link/@href'/>
</xsl:otherwise> </xsl:otherwise>
</xsl:choose> </xsl:choose>
</xsl:attribute> </xsl:attribute>
<xsl:attribute name='title'>
<xsl:value-of select='atom:title'/>
</xsl:attribute>
<xsl:attribute name='id'> <xsl:attribute name='id'>
<xsl:text>stremburner-</xsl:text> <xsl:text>rivista-</xsl:text>
<xsl:value-of select='position()'/> <xsl:value-of select='position()'/>
</xsl:attribute> </xsl:attribute>
<xsl:choose> <xsl:choose>
@ -230,10 +223,29 @@ xmlns:atom='http://www.w3.org/2005/Atom'>
</span> </span>
</xsl:when> </xsl:when>
</xsl:choose> </xsl:choose>
<!-- div class='posted' --> <!-- entry date -->
<xsl:element name='h4'>
<xsl:choose>
<xsl:when test='atom:updated'>
<xsl:attribute name='class'>
<xsl:text>updated</xsl:text>
</xsl:attribute>
<xsl:value-of select='atom:updated'/>
</xsl:when>
<xsl:when test='atom:published'>
<xsl:attribute name='class'>
<xsl:text>published</xsl:text>
</xsl:attribute>
<xsl:value-of select='atom:published'/>
</xsl:when>
<xsl:otherwise>
<h4 class='warning atom1 published'></h4>
</xsl:otherwise>
</xsl:choose>
</xsl:element>
<!-- entry author --> <!-- entry author -->
<!-- xsl:if test='atom:author'> <xsl:if test='atom:author'>
<span class='author'> <h4 class='author'>
<xsl:choose> <xsl:choose>
<xsl:when test='atom:author/atom:email'> <xsl:when test='atom:author/atom:email'>
<xsl:element name='a'> <xsl:element name='a'>
@ -259,31 +271,48 @@ xmlns:atom='http://www.w3.org/2005/Atom'>
<xsl:value-of select='atom:author/atom:name'/> <xsl:value-of select='atom:author/atom:name'/>
</xsl:element> </xsl:element>
</xsl:when> </xsl:when>
<xsl:otherwise> <xsl:when test='atom:author/atom:name'>
<xsl:value-of select='atom:name'/> <xsl:text>By </xsl:text>
</xsl:otherwise> <xsl:value-of select='atom:author/atom:name'/>
</xsl:choose>
</span>
</xsl:if -->
<!-- entry date -->
<xsl:choose>
<xsl:when test='atom:updated'>
<h4 class='updated'>
<xsl:value-of select='atom:updated'/>
</h4>
</xsl:when> </xsl:when>
<xsl:when test='atom:published'>
<h4 class='published'>
<xsl:value-of select='atom:published'/>
</h4>
</xsl:when>
<xsl:otherwise>
<h4 class='warning atom1 published'></h4>
</xsl:otherwise>
</xsl:choose> </xsl:choose>
<!-- /div --> </h4>
</xsl:if>
<h5 class='related'>
<xsl:if test='atom:link[@rel="alternate" and @type="x-scheme-handler/xmpp"]'>
<xsl:element name='a'>
<xsl:attribute name='href'>
<xsl:value-of select='atom:link[@rel="alternate" and @type="x-scheme-handler/xmpp"]/@href'/>
</xsl:attribute>
💡️ XMPP
</xsl:element>
</xsl:if>
<xsl:if test='atom:link[@rel="replies"]'>
<xsl:element name='a'>
<xsl:attribute name='href'>
<xsl:value-of select='atom:link[@rel="replies"]/@href'/>
</xsl:attribute>
💬 Discussion
</xsl:element>
</xsl:if>
<xsl:if test='atom:link[@rel="alternate" and @type="text/html"]'>
<xsl:element name='a'>
<xsl:attribute name='href'>
<xsl:value-of select='atom:link[@rel="alternate" and @type="text/html"]/@href'/>
</xsl:attribute>
📜 HTML
</xsl:element>
</xsl:if>
<xsl:if test='atom:link[@rel="related" and @type="text/html"]'>
<xsl:element name='a'>
<xsl:attribute name='href'>
<xsl:value-of select='atom:link[@rel="related" and @type="text/html"]/@href'/>
</xsl:attribute>
📜 HTML (Related)
</xsl:element>
</xsl:if>
</h5>
<!-- entry content --> <!-- entry content -->
<!-- entry summary of GitLab Atom Syndication Feeds -->
<xsl:if test='atom:content or atom:summary'> <xsl:if test='atom:content or atom:summary'>
<div class='content'> <div class='content'>
<xsl:choose> <xsl:choose>
@ -331,9 +360,11 @@ xmlns:atom='http://www.w3.org/2005/Atom'>
</div> </div>
</xsl:if> </xsl:if>
<!-- entry enclosure --> <!-- entry enclosure -->
<xsl:if test='atom:link[contains(@rel,"enclosure")]'> <xsl:if test='atom:link[@rel="enclosure"]'>
<h4>Enclosures</h4>
<div class='enclosures' title='Right-click and Save link as…'>
<xsl:for-each select='atom:link[@rel="enclosure"]'>
<div class='enclosure' title='Right-click and Save link as…'> <div class='enclosure' title='Right-click and Save link as…'>
<xsl:for-each select='atom:link[contains(@rel,"enclosure")]'>
<xsl:element name='span'> <xsl:element name='span'>
<xsl:attribute name='icon'> <xsl:attribute name='icon'>
<xsl:value-of select='substring-before(@type,"/")'/> <xsl:value-of select='substring-before(@type,"/")'/>
@ -358,48 +389,34 @@ xmlns:atom='http://www.w3.org/2005/Atom'>
<xsl:with-param name='length' select='@length' /> <xsl:with-param name='length' select='@length' />
</xsl:call-template> </xsl:call-template>
</xsl:if> </xsl:if>
<xsl:element name='br'/> <br/>
</xsl:for-each> </div>
<xsl:for-each select='media:content'>
<xsl:element name='span'>
<xsl:attribute name='icon'>
<xsl:value-of select='@medium'/>
</xsl:attribute>
</xsl:element>
<xsl:element name='a'>
<xsl:attribute name='href'>
<xsl:value-of select='@url'/>
</xsl:attribute>
<xsl:attribute name='download'/>
<xsl:call-template name='extract-filename'>
<xsl:with-param name='url' select='@url' />
</xsl:call-template>
</xsl:element>
<xsl:element name='span'>
<xsl:attribute name='class'>
<xsl:value-of select='@medium'/>
</xsl:attribute>
</xsl:element>
<xsl:if test='@fileSize &gt; 0'>
<xsl:call-template name='transform-filesize'>
<xsl:with-param name='length' select='@fileSize' />
</xsl:call-template>
</xsl:if>
<xsl:element name='br'/>
</xsl:for-each> </xsl:for-each>
</div> </div>
</xsl:if> </xsl:if>
</div> </div>
<!-- entry id --> <!-- entry id -->
<xsl:if test='not(atom:id)'> <xsl:if test='not(atom:id)'>
<div class='warning atom1 id'></div> <div class='warning atom1 id'>No entry ID</div>
</xsl:if> </xsl:if>
</li> </li>
</xsl:for-each> </xsl:for-each>
</ul> </ul>
</xsl:when> </xsl:when>
<xsl:otherwise> <xsl:otherwise>
<h3 class='notice no-entry'>No content.</h3> <ul>
<li>
<div class='entry'>
<h3 class='title'>
<a href='javascript:alert("Please check that the mother PubSub node is populated with content.")'>
No content
</a>
</h3>
<h4>This entry is empty</h4>
<div class='content'>Please check that the mother PubSub node is populated with content.</div>
</div>
</li>
</ul>
</xsl:otherwise> </xsl:otherwise>
</xsl:choose> </xsl:choose>
</div> </div>
@ -417,8 +434,16 @@ xmlns:atom='http://www.w3.org/2005/Atom'>
title='The Social Platform Shaped For Your Community.'> title='The Social Platform Shaped For Your Community.'>
Movim Movim
</a> </a>
<a href='https://git.xmpp-it.net/sch/Rivista'
title='A Journal Publisher And Browser For XMPP.'>
Rivista
</a>
<a href='https://github.com/SeveFP/Reeder'
Title='An XMPP-Based Feed Reader.'>
Reeder
</a>
<a href='https://modernxmpp.org/' <a href='https://modernxmpp.org/'
title='A Project To Improve The Quality Of User-To-User Messaging Applications That Use Xmpp.'> title='A Project To Improve The Quality Of User-To-User Messaging Applications That Use XMPP.'>
Modern Modern
</a> </a>
<a href='https://xmpp.org/' <a href='https://xmpp.org/'
@ -433,12 +458,15 @@ xmlns:atom='http://www.w3.org/2005/Atom'>
<!-- note --> <!-- note -->
<p id='note'> <p id='note'>
<i> <i>
This is an XMPP news feed which is conveyed as an XHTML This is an Atom document which is conveyed as an XHTML
document, and it can even be viewed by a syndication document;
feed reader which provides automated notifications on This document can also be viewed with a Syndication Feed
desktop and mobile. <span id="selection-link">Click Reader (also referred to as News Reader or RSS Reader)
here</span> for a selection of software and pick the which provides automated news updates and notifications
ones that would fit you best! on desktop and mobile.
<span id="selection-link">Click here</span> for a
selection of software and pick the ones that would fit
to you best!
</i> </i>
</p> </p>
</body> </body>

View file

@ -1,17 +1,19 @@
<?xml version="1.0" encoding="UTF-8" ?> <?xml version="1.0" encoding="UTF-8" ?>
<!-- <!--
Copyright (C) 2016 - 2017 Schimon Jehuda. Released under MIT license Copyright (C) 2016 - 2024 Schimon Jehuda. Released under MIT license
Feeds rendered using this XSLT stylesheet, or it's derivatives, must Feeds rendered using this XSLT stylesheet, or it's derivatives, must
include https://schimon.i2p/ in attribute name='generator' of include https://schimon.i2p/ in attribute name='generator' of
element <meta/> inside of html element </head> element <meta/> inside of html element </head>
--> -->
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method = 'html' <xsl:output encoding = 'UTF-8'
indent = 'yes' indent = 'yes'
omit-xml-decleration='no' /> media-type = 'text/x-opml'
<!-- Atom 1.0 Syndication Format --> method = 'html'
version = '4.01' />
<!-- Outline Processor Markup Language 1.0 -->
<xsl:include href='opml_as_xhtml.xsl'/> <xsl:include href='opml_as_xhtml.xsl'/>
<!-- set page metadata --> <!-- set page metadata -->
<xsl:include href='metadata.xsl'/> <xsl:include href='metadata.xsl'/>

View file

@ -10,9 +10,6 @@ element <meta/> inside of html element </head>
<xsl:stylesheet version='1.0' <xsl:stylesheet version='1.0'
xmlns:xsl='http://www.w3.org/1999/XSL/Transform' xmlns:xsl='http://www.w3.org/1999/XSL/Transform'
xmlns:xml='http://www.w3.org/XML/1998/namespace'> xmlns:xml='http://www.w3.org/XML/1998/namespace'>
<!-- Outline Processor Markup Language 1.0 -->
<xsl:output
media-type='text/x-opml' />
<xsl:template match='/opml'> <xsl:template match='/opml'>
<!-- index right-to-left language codes --> <!-- index right-to-left language codes -->
<!-- TODO http://www.w3.org/TR/xpath/#function-lang --> <!-- TODO http://www.w3.org/TR/xpath/#function-lang -->
@ -33,7 +30,7 @@ xmlns:xml='http://www.w3.org/XML/1998/namespace'>
</xsl:call-template> </xsl:call-template>
<xsl:call-template name='metadata'> <xsl:call-template name='metadata'>
<xsl:with-param name='name' select='"generator"' /> <xsl:with-param name='name' select='"generator"' />
<xsl:with-param name='content' select='StreamBurner' /> <xsl:with-param name='content' select='Rivista' />
</xsl:call-template> </xsl:call-template>
<xsl:call-template name='metadata'> <xsl:call-template name='metadata'>
<xsl:with-param name='name' select='"mimetype"' /> <xsl:with-param name='name' select='"mimetype"' />
@ -41,10 +38,10 @@ xmlns:xml='http://www.w3.org/XML/1998/namespace'>
</xsl:call-template> </xsl:call-template>
<title> <title>
<xsl:choose> <xsl:choose>
<xsl:when test='title and not(title="")'> <xsl:when test='//head/title and not(//head/title="")'>
<xsl:value-of select='title'/> <xsl:value-of select='//head/title'/>
</xsl:when> </xsl:when>
<xsl:otherwise>StreamBurner</xsl:otherwise> <xsl:otherwise>Rivista OPML</xsl:otherwise>
</xsl:choose> </xsl:choose>
</title> </title>
<!-- TODO media='print' --> <!-- TODO media='print' -->
@ -54,20 +51,9 @@ xmlns:xml='http://www.w3.org/XML/1998/namespace'>
<xsl:if test='$rtl'> <xsl:if test='$rtl'>
<link id='semitic' href='/css/stylesheet-rtl.css' rel='stylesheet' type='text/css' /> <link id='semitic' href='/css/stylesheet-rtl.css' rel='stylesheet' type='text/css' />
</xsl:if> </xsl:if>
<script type='text/javascript' src='/script/marked.min.js'/>
<script type='text/javascript' src='/script/postprocess.js'/>
</head> </head>
<body> <body>
<div id='actions'> <div id='actions'>
<a href='https://git.xmpp-it.net/sch/PubSubToAtom'
title='About PubSub To Atom.'>
About
</a>
<a href='https://aboutfeeds.com/'
title='Of the benefits of syndication feed.'
id='aboutfeeds'>
Feeds
</a>
<a href='https://xmpp.org/about/technology-overview/' <a href='https://xmpp.org/about/technology-overview/'
title='Of the benefits of XMPP.'> title='Of the benefits of XMPP.'>
XMPP XMPP
@ -76,6 +62,11 @@ xmlns:xml='http://www.w3.org/XML/1998/namespace'>
title='Syndictaion and PubSub.'> title='Syndictaion and PubSub.'>
Groupchat Groupchat
</a> </a>
<a href='https://aboutfeeds.com/'
title='Of the benefits of syndication feed.'
id='aboutfeeds'>
Help
</a>
</div> </div>
<div id='feed'> <div id='feed'>
<div id='header'> <div id='header'>
@ -85,11 +76,8 @@ xmlns:xml='http://www.w3.org/XML/1998/namespace'>
<xsl:when test='//head/title and not(//head/title="") and count(//outline) &gt; 1'> <xsl:when test='//head/title and not(//head/title="") and count(//outline) &gt; 1'>
<xsl:value-of select='//head/title'/> <xsl:value-of select='//head/title'/>
</xsl:when> </xsl:when>
<xsl:when test='//outline'>
<xsl:value-of select='//outline/@text'/>
</xsl:when>
<xsl:otherwise> <xsl:otherwise>
StreamBurner OPML Feed Rivista OPML Collection
</xsl:otherwise> </xsl:otherwise>
</xsl:choose> </xsl:choose>
</h1> </h1>
@ -109,12 +97,12 @@ xmlns:xml='http://www.w3.org/XML/1998/namespace'>
<div id='menu'> <div id='menu'>
<h3>Subscriptions</h3> <h3>Subscriptions</h3>
<!-- xsl:for-each select='outline[position() &lt;21]' --> <!-- xsl:for-each select='outline[position() &lt;21]' -->
<ul> <ol>
<xsl:for-each select='//outline[not(position() &gt; 20)]'> <xsl:for-each select='//outline[not(position() &gt; 20)]'>
<li> <li>
<xsl:element name='a'> <xsl:element name='a'>
<xsl:attribute name='href'> <xsl:attribute name='href'>
<xsl:text>#stremburner-</xsl:text> <xsl:text>#rivista-</xsl:text>
<xsl:value-of select='position()'/> <xsl:value-of select='position()'/>
</xsl:attribute> </xsl:attribute>
<xsl:choose> <xsl:choose>
@ -128,7 +116,7 @@ xmlns:xml='http://www.w3.org/XML/1998/namespace'>
</xsl:element> </xsl:element>
</li> </li>
</xsl:for-each> </xsl:for-each>
</ul> </ol>
</div> </div>
</xsl:if> </xsl:if>
<div id='articles'> <div id='articles'>
@ -149,7 +137,7 @@ xmlns:xml='http://www.w3.org/XML/1998/namespace'>
<xsl:value-of select='@text'/> <xsl:value-of select='@text'/>
</xsl:attribute> </xsl:attribute>
<xsl:attribute name='id'> <xsl:attribute name='id'>
<xsl:text>stremburner-</xsl:text> <xsl:text>rivista-</xsl:text>
<xsl:value-of select='position()'/> <xsl:value-of select='position()'/>
</xsl:attribute> </xsl:attribute>
<xsl:choose> <xsl:choose>
@ -193,8 +181,16 @@ xmlns:xml='http://www.w3.org/XML/1998/namespace'>
title='The Social Platform Shaped For Your Community.'> title='The Social Platform Shaped For Your Community.'>
Movim Movim
</a> </a>
<a href='https://git.xmpp-it.net/sch/Rivista'
title='A Journal Publisher And Browser For XMPP.'>
Rivista
</a>
<a href='https://github.com/SeveFP/Reeder'
Title='An XMPP-Based Feed Reader.'>
Reeder
</a>
<a href='https://modernxmpp.org/' <a href='https://modernxmpp.org/'
title='A Project To Improve The Quality Of User-To-User Messaging Applications That Use Xmpp.'> title='A Project To Improve The Quality Of User-To-User Messaging Applications That Use XMPP.'>
Modern Modern
</a> </a>
<a href='https://xmpp.org/' <a href='https://xmpp.org/'
@ -209,15 +205,16 @@ xmlns:xml='http://www.w3.org/XML/1998/namespace'>
<!-- note --> <!-- note -->
<p id='note'> <p id='note'>
<i> <i>
This is an This is an OPML document which is conveyed as an XHTML
<b title ='Outline Processor Markup Language'>OPML</b> document;
document which is conveyed as an XHTML document; This This document includes a collection of subscriptions,
document includes a list of subscriptionsis and is and it can be imported to a Syndication Feed Reader
intended to be imported to a syndication feed reader (also referred to as News Reader or RSS Reader) reader
which provides automated notifications on desktop and which provides automated news updates and notifications
mobile. <span id="selection-link">Click here</span> for on desktop and mobile.
a selection of software and pick the ones that would fit <span id="selection-link">Click here</span> for a
you best! selection of software and pick the ones that would fit
to you best!
</i> </i>
</p> </p>
</body> </body>