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
@ -19,14 +19,14 @@ XJP was inspired from Tigase and was motivated by Movim.
## 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.
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.
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
@ -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.
```shell
$ git clone https://git.xmpp-it.net/sch/XJP
$ cd XJP/
$ git clone https://git.xmpp-it.net/sch/Rivista
$ cd Rivista/
```
### Configure
@ -60,7 +60,7 @@ Add account credentials to file `configuration.toml`.
### Start
Execute XMPP Journal Publisher with one of the following commands:
Execute Rivista with one of the following commands:
```shell
$ 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.
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

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;
max-width: 100%;
}
/*
a {
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
*/
body {
background: #000;
}
@ -12,26 +34,26 @@ code, pre {
max-height: 100%;
max-width: 100%; }
img, video {
img, svg, video {
display: block;
max-height: 500px;
width: 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 {
text-align: center;
text-transform: uppercase;
line-height: 140%;
}
h5.related > a {
margin-right: 5px;
text-decoration: none;
user-select: none;
}
h3.title > a {
display: block;
padding-top: 50px;
@ -59,24 +81,17 @@ h3.title > a {
padding-left: 2%;
}
#menu > ul > li {
#menu > ol > li {
padding: 5px;
}
#note {
line-height: 30px;
margin: auto;
margin-top: 0.67em;
max-width: 70%;
padding: 10px;
text-align: center;
}
#references {
margin-top: 5em;
border-top: 1px solid #eee;
}
#articles {
min-height: 80vh;
display: flex;
}
@ -91,10 +106,12 @@ h3.title > a {
#journal {
margin: auto;
padding-top: 50px;
}
#articles > ul {
margin: auto;
margin-left: 2%;
margin-top: 2%;
}
@ -103,10 +120,7 @@ h3.title > a {
/* height: 500px; */
line-height: 160%;
overflow: auto;
}
#articles #journal > a {
font-weight: bold;
word-wrap: break-word;
}
#articles div.content {
@ -174,7 +188,91 @@ h3.title > a {
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 {
display: unset;
}

View file

@ -8,6 +8,7 @@ from fastapi.staticfiles import StaticFiles
import json
from slixmpp import ClientXMPP
from slixmpp.exceptions import IqError, IqTimeout
import xml
import xml.etree.ElementTree as ET
#import importlib.resources
@ -56,17 +57,15 @@ async def view_pubsub_nodes(request: Request):
if iq:
link = 'xmpp:{pubsub}'.format(pubsub=pubsub)
xml_opml = generate_opml(iq)
result = append_stylesheet(xml_opml, 'opml.xsl')
result = append_stylesheet(xml_opml, 'opml')
else:
text = 'Please check that PubSub Jabber ID is valid and accessible.'
xml_atom = error_message(text)
result = append_stylesheet(
xml_atom, 'atom.xsl', namespace='http://www.w3.org/2005/Atom')
result = append_stylesheet(xml_atom, 'atom')
else:
text = 'The given domain {} is not allowed.'.format(pubsub)
xml_atom = error_message(text)
result = append_stylesheet(
xml_atom, 'atom.xsl', namespace='http://www.w3.org/2005/Atom')
result = append_stylesheet(xml_atom, 'atom')
default = get_configuration('default')
if not result:
if default['pubsub']:
@ -75,17 +74,16 @@ async def view_pubsub_nodes(request: Request):
iq = await get_nodes(pubsub)
link = 'xmpp:{pubsub}'.format(pubsub=pubsub)
xml_opml = generate_opml(iq)
result = append_stylesheet(xml_opml, 'opml.xsl')
result = append_stylesheet(xml_opml, 'opml')
elif not settings['service']:
pubsub = default['pubsub']
link = 'xmpp:{pubsub}'.format(pubsub=pubsub)
xml_opml = generate_opml(iq)
result = append_stylesheet(xml_opml, 'opml.xsl')
result = append_stylesheet(xml_opml, 'opml')
else:
text = 'Please contact the administrator and ask him to set default PubSub and Node ID.'
xml_atom = error_message(text)
result = append_stylesheet(
xml_atom, 'atom.xsl', namespace='http://www.w3.org/2005/Atom')
result = append_stylesheet(xml_atom, 'atom')
response = Response(content=result, media_type="application/xml")
return response
@ -115,11 +113,12 @@ async def view_node_items(request: Request):
else:
operator = get_configuration('settings')['operator']
json_data = [{'title' : 'Error retrieving items list.',
'link' : ('javascript:alert("XJP: XMPP Journal Publisher has experienced an error '
'while attempting to retrieve the list of items for Node {} of PubSub {}.")')
'link' : ('javascript:alert("Rivista has experienced an error '
'while attempting to retrieve the list of items for '
'Node {} of PubSub {}.")')
.format(node, pubsub)},
{'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 '
'node items for Node {} on PubSub {}.').format(operator, node, pubsub)}]
filename = 'data/{}.json'.format(node)
@ -128,8 +127,7 @@ async def view_node_items(request: Request):
else:
text = 'Please check that PubSub node and item are valid and accessible.'
xml_atom = error_message(text)
result = append_stylesheet(
xml_atom, 'atom.xsl', namespace='http://www.w3.org/2005/Atom')
result = append_stylesheet(xml_atom, 'atom')
# try:
# iq = await get_node_items(pubsub, node)
@ -149,26 +147,22 @@ async def view_node_items(request: Request):
else:
text = 'Please check that PubSub node is valid and accessible.'
xml_atom = error_message(text)
result = append_stylesheet(
xml_atom, 'atom.xsl', namespace='http://www.w3.org/2005/Atom')
result = append_stylesheet(xml_atom, 'atom')
elif pubsub:
text = 'Node parameter is missing.'
xml_atom = error_message(text)
result = append_stylesheet(
xml_atom, 'atom.xsl', namespace='http://www.w3.org/2005/Atom')
result = append_stylesheet(xml_atom, 'atom')
elif node:
text = 'PubSub parameter is missing.'
xml_atom = error_message(text)
result = append_stylesheet(
xml_atom, 'atom.xsl', namespace='http://www.w3.org/2005/Atom')
result = append_stylesheet(xml_atom, 'atom')
# else:
# result = ('Mandatory parameter PubSub and '
# 'optional parameter Node are missing.')
else:
text = 'The given domain {} is not allowed.'.format(pubsub)
xml_atom = error_message(text)
result = append_stylesheet(
xml_atom, 'atom.xsl', namespace='http://www.w3.org/2005/Atom')
result = append_stylesheet(xml_atom, 'atom')
default = get_configuration('default')
if not result:
if default['pubsub'] and default['nodeid']:
@ -178,21 +172,18 @@ async def view_node_items(request: Request):
iq = await get_node_items(pubsub, node)
link = form_a_node_link(pubsub, node)
xml_atom = generate_atom(iq, link)
result = append_stylesheet(
xml_atom, 'atom.xsl', namespace='http://www.w3.org/2005/Atom')
result = append_stylesheet(xml_atom, 'atom')
elif not settings['service']:
pubsub = default['pubsub']
node = default['nodeid']
iq = await get_node_items(pubsub, node)
link = form_a_node_link(pubsub, node)
xml_atom = generate_atom(iq, link)
result = append_stylesheet(
xml_atom, 'atom.xsl', namespace='http://www.w3.org/2005/Atom')
result = append_stylesheet(xml_atom, 'atom')
else:
text = 'Please contact the administrator and ask him to set default PubSub and Node ID.'
xml_atom = error_message(text)
result = append_stylesheet(
xml_atom, 'atom.xsl', namespace='http://www.w3.org/2005/Atom')
result = append_stylesheet(xml_atom, 'atom')
response = Response(content=result, media_type="application/xml")
return response
@ -234,24 +225,25 @@ def form_an_item_link(pubsub, node, item_id):
def error_message(text):
"""Error message in RFC 4287: The Atom Syndication Format."""
title = 'StreamBurner'
title = 'Rivista'
subtitle = 'XMPP Journal Publisher'
description = ('This is a syndication feed generated with XMPP Journal '
'Publisher, which conveys XEP-0060: Publish-Subscribe '
'nodes to standard RFC 4287: The Atom Syndication Format.')
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 = 'en'
feed = ET.Element("feed")
feed.set('xmlns', 'http://www.w3.org/2005/Atom')
ET.SubElement(feed, 'title', {'type': 'text'}).text = title
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', {
'uri': 'https://git.xmpp-it.net/sch/XJP',
'version': '0.1'}).text = 'XMPP Journal Publisher (XJP)'
'uri': 'https://git.xmpp-it.net/sch/Rivista',
'version': '0.1'}).text = 'Rivista'
ET.SubElement(feed, 'updated').text = datetime.datetime.now(datetime.UTC).isoformat()
entry = ET.SubElement(feed, 'entry')
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, 'published').text = datetime.datetime.now(datetime.UTC).isoformat()
# 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)
# subtitle = 'XMPP PubSub Syndication Feed'
subtitle = pubsub
description = ('This is a syndication feed generated with XMPP Journal '
'Publisher, which conveys XEP-0060: Publish-Subscribe '
'nodes to standard RFC 4287: The Atom Syndication Format.')
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']
feed = ET.Element("feed")
feed.set('xmlns', 'http://www.w3.org/2005/Atom')
ET.SubElement(feed, 'title', {'type': 'text'}).text = title
ET.SubElement(feed, 'subtitle', {'type': 'text'}).text = subtitle
ET.SubElement(feed, 'link', {'rel': 'self', 'href': link})
ET.SubElement(feed, 'generator', {
'uri': 'https://git.xmpp-it.net/sch/XJP',
'version': '0.1'}).text = 'XMPP Journal Publisher (XJP)'
ET.SubElement(feed, 'updated').text = datetime.datetime.now(datetime.UTC).isoformat()
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'
ET.SubElement(e_feed, 'updated').text = datetime.datetime.now(datetime.UTC).isoformat()
for item in items:
item_id = item['id']
item_payload = item['payload']
namespace = '{http://www.w3.org/2005/Atom}'
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
# link = item_payload.find(namespace + 'link')
# link_href = '' if link == None else link.attrib['href']
link_href = form_an_item_link(pubsub, node, item_id)
if not title_text or not link_href: continue
content = item_payload.find(namespace + 'content')
content_text = 'No content' if content == None else content.text
if content:
content_type = content.attrib['type'] if 'type' in content.attrib else 'text'
content_type_text = 'html' if 'html' in content_type else 'text'
e_entry = ET.SubElement(e_feed, 'entry')
ET.SubElement(e_entry, 'title').text = title_text
if isinstance(links, xml.etree.ElementTree.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:
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_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
author = item_payload.find(namespace + 'author')
if author and author.attrib: print(author.attrib)
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)
identifier = item_payload.find(namespace + 'id')
if identifier and identifier.attrib: print(identifier.attrib)
identifier_text = 'None' if identifier == None else identifier.text
entry = ET.SubElement(feed, 'entry')
ET.SubElement(entry, 'title').text = title_text
ET.SubElement(entry, 'link', {'href': link_href})
ET.SubElement(entry, 'id').text = identifier_text
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')
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."""
@ -344,25 +358,11 @@ def generate_json(iq):
with open(filename, 'w', encoding='utf-8') as f:
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"""
"""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:
if namespace: ET.register_namespace("", namespace)
if type == 'atom': ET.register_namespace('', 'http://www.w3.org/2005/Atom')
# Load XML from string
tree = ET.fromstring(xml_data)
# The following direction removes the XML declaration
@ -370,7 +370,7 @@ def append_stylesheet(xml_data, filename, namespace=None):
# Add XML declaration and stylesheet
xml_data_declaration = (
'<?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)
return xml_data_declaration
@ -380,11 +380,11 @@ def generate_opml(iq):
opml = ET.Element("opml")
opml.set("version", "1.0")
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 = (
"PubSub Nodes of {}").format(pubsub)
ET.SubElement(head, "generator").text = 'XMPP Journal Publisher (XJP)'
ET.SubElement(head, "urlPublic").text = 'https://git.xmpp-it.net/sch/XJP'
ET.SubElement(head, "generator").text = 'Rivista'
ET.SubElement(head, "urlPublic").text = 'https://git.xmpp-it.net/sch/Rivista'
time_stamp = datetime.datetime.now(datetime.UTC).isoformat()
ET.SubElement(head, "dateCreated").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 node = locationHref.searchParams.get('node')
let pubsub = locationHref.searchParams.get('pubsub')
// Fix button follow
// Set button follow
let follow = document.querySelector('#follow');
if (follow) {
//let feedUrl = location.href.replace(/^https?:/, 'feed:');
@ -14,6 +15,7 @@ window.onload = async function(){
// Fix button subtome
document.querySelector('#subtome').href='https://www.subtome.com/#/subscribe?feeds=' + feedUrl;
}
// Convert ISO8601 To UTC
for (let element of document.querySelectorAll(
'#articles > ul > li > div > h4.published,' +
@ -22,11 +24,62 @@ window.onload = async function(){
let timeStamp = new Date(element.textContent);
element.textContent = timeStamp.toUTCString();
}
// Parse Markdown
for (let element of document.querySelectorAll('#articles div[type="text"]')) {
let markDown = element.textContent
element.innerHTML = marked.parse(markDown);
element.innerHTML = marked.parse(element.textContent);
}
// 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
if (node) {
itemsList = await openJson(node)
@ -79,8 +132,11 @@ window.onload = async function(){
document.querySelector('#articles').appendChild(elementDiv);
}
}
// 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);
let parameters = xmppUri.search.split(';');
try {
@ -98,6 +154,7 @@ window.onload = async function(){
console.warn(err)
}
}
// Display a selection of suggested software.
const selection = {
'akregator' : {
@ -144,14 +201,14 @@ window.onload = async function(){
elementH1.textContent = 'Get A News Reader';
elementDiv.appendChild(elementH1);
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);
const brands = Object.keys(selection);
let elementDivSel = document.createElement('div');
elementDivSel.id = 'selection';
for (let i = 0; i < brands.length; i++) {
let brand = brands[i];
elementSpan = document.createElement('span');
let elementSpan = document.createElement('span');
let elementA = document.createElement('a');
elementA.href = selection[brand].url;
elementA.textContent = selection[brand].name;
@ -162,20 +219,24 @@ window.onload = async function(){
elementDivSel.appendChild(elementSpan);
elementDiv.appendChild(elementDivSel);
}
let elementP = document.createElement('p');
elementP.textContent = '' +
'This is a selection of desktop applications, mobile apps and online ' +
'services for you to choose from. This selection includes news ' +
'readers, podcast managers, torrent clients, chat bots, HTML browsers ' +
'and plugins which support syndication feeds.';
elementDiv.appendChild(elementP);
let elementDivReturn = document.createElement('div');
elementDivReturn.id = 'return';
elementDivReturn.textContent = 'Return To PubSub...';
elementDivReturn.addEventListener ('click', function() {
let elementP1 = document.createElement('p');
elementP1.textContent = '' +
'This is a selection of desktop, mobile and HTML (sometimes referred to ' +
'as "online") News Readers for you to choose from.';
elementDiv.appendChild(elementP1);
let elementP2 = document.createElement('p');
elementP2.textContent = '' +
'This selection includes: Podcast Managers, Torrent ' +
'Clients, Chat Bots, HTML Browsers and Plugins which support ' +
'syndication feeds.';
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();
});
elementDiv.appendChild(elementDivReturn);
elementDiv.appendChild(elementSpan);
document.body.appendChild(elementDiv);
});
}

View file

@ -1,17 +1,19 @@
<?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
include https://schimon.i2p/ in attribute name='generator' of
element <meta/> inside of html element </head>
-->
<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'
omit-xml-decleration='no' />
<!-- Atom 1.0 Syndication Format -->
media-type = 'application/atom+xml'
method = 'html'
version = '4.01' />
<!-- Atom Syndication Format 1.0 -->
<xsl:include href='atom_as_xhtml.xsl'/>
<!-- extract filename from given url string -->
<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:atom10='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'>
<!-- index right-to-left language codes -->
<!-- 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 name='metadata'>
<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 name='metadata'>
<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:value-of select='atom:entry/atom:title'/>
</xsl:when>
<xsl:otherwise>StreamBurner</xsl:otherwise>
<xsl:otherwise>Rivista</xsl:otherwise>
</xsl:choose>
</title>
<!-- TODO media='print' -->
@ -90,15 +87,6 @@ xmlns:atom='http://www.w3.org/2005/Atom'>
</xsl:attribute>
SubToMe
</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/'
title='Of the benefits of XMPP.'>
XMPP
@ -107,6 +95,11 @@ xmlns:atom='http://www.w3.org/2005/Atom'>
title='Syndictaion and PubSub.'>
Groupchat
</a>
<a href='https://aboutfeeds.com/'
title='Of the benefits of syndication feed.'
id='aboutfeeds'>
Help
</a>
</div>
<div id='feed'>
<div id='header'>
@ -120,7 +113,7 @@ xmlns:atom='http://www.w3.org/2005/Atom'>
<xsl:value-of select='atom:entry/atom:title'/>
</xsl:when>
<xsl:otherwise>
StreamBurner
No title
</xsl:otherwise>
</xsl:choose>
</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:value-of select='atom:entry/atom:published'/>
</xsl:when>
<xsl:otherwise>
Rivista XMPP Journal Publisher
</xsl:otherwise>
</xsl:choose>
</h2>
</div>
@ -145,12 +141,12 @@ xmlns:atom='http://www.w3.org/2005/Atom'>
<div id='menu'>
<h3>Latest Posts</h3>
<!-- xsl:for-each select='atom:entry[position() &lt;21]' -->
<ul>
<ol>
<xsl:for-each select='atom:entry[not(position() &gt; 20)]'>
<li>
<xsl:element name='a'>
<xsl:attribute name='href'>
<xsl:text>#stremburner-</xsl:text>
<xsl:text>#rivista-</xsl:text>
<xsl:value-of select='position()'/>
</xsl:attribute>
<xsl:choose>
@ -164,7 +160,7 @@ xmlns:atom='http://www.w3.org/2005/Atom'>
</xsl:element>
</li>
</xsl:for-each>
</ul>
</ol>
</div>
</xsl:if>
<div id='articles'>
@ -180,19 +176,16 @@ xmlns:atom='http://www.w3.org/2005/Atom'>
<xsl:element name='a'>
<xsl:attribute name='href'>
<xsl:choose>
<xsl:when test='atom:link[contains(@rel,"alternate")]'>
<xsl:value-of select='atom:link[contains(@rel,"alternate")]/@href'/>
<xsl:when test='atom:link[@rel="self"]'>
<xsl:value-of select='atom:link[@rel="self"]/@href'/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select='atom:link/@href'/>
</xsl:otherwise>
</xsl:choose>
</xsl:attribute>
<xsl:attribute name='title'>
<xsl:value-of select='atom:title'/>
</xsl:attribute>
<xsl:attribute name='id'>
<xsl:text>stremburner-</xsl:text>
<xsl:text>rivista-</xsl:text>
<xsl:value-of select='position()'/>
</xsl:attribute>
<xsl:choose>
@ -230,60 +223,96 @@ xmlns:atom='http://www.w3.org/2005/Atom'>
</span>
</xsl:when>
</xsl:choose>
<!-- div class='posted' -->
<!-- entry author -->
<!-- xsl:if test='atom:author'>
<span class='author'>
<xsl:choose>
<xsl:when test='atom:author/atom:email'>
<xsl:element name='a'>
<xsl:attribute name='href'>
<xsl:text>mailto:</xsl:text>
<xsl:value-of select='atom:author/atom:email'/>
</xsl:attribute>
<xsl:attribute name='title'>
<xsl:text>Send an Email to </xsl:text>
<xsl:value-of select='atom:author/atom:email'/>
</xsl:attribute>
<xsl:value-of select='atom:author/atom:name'/>
</xsl:element>
</xsl:when>
<xsl:when test='atom:author/atom:uri'>
<xsl:element name='a'>
<xsl:attribute name='href'>
<xsl:value-of select='atom:author/atom:uri'/>
</xsl:attribute>
<xsl:attribute name='title'>
<xsl:value-of select='atom:author/atom:summary'/>
</xsl:attribute>
<xsl:value-of select='atom:author/atom:name'/>
</xsl:element>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select='atom:name'/>
</xsl:otherwise>
</xsl:choose>
</span>
</xsl:if -->
<!-- entry date -->
<!-- entry date -->
<xsl:element name='h4'>
<xsl:choose>
<xsl:when test='atom:updated'>
<h4 class='updated'>
<xsl:value-of select='atom:updated'/>
</h4>
<xsl:attribute name='class'>
<xsl:text>updated</xsl:text>
</xsl:attribute>
<xsl:value-of select='atom:updated'/>
</xsl:when>
<xsl:when test='atom:published'>
<h4 class='published'>
<xsl:value-of select='atom:published'/>
</h4>
<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>
<!-- /div -->
</xsl:element>
<!-- entry author -->
<xsl:if test='atom:author'>
<h4 class='author'>
<xsl:choose>
<xsl:when test='atom:author/atom:email'>
<xsl:element name='a'>
<xsl:attribute name='href'>
<xsl:text>mailto:</xsl:text>
<xsl:value-of select='atom:author/atom:email'/>
</xsl:attribute>
<xsl:attribute name='title'>
<xsl:text>Send an Email to </xsl:text>
<xsl:value-of select='atom:author/atom:email'/>
</xsl:attribute>
<xsl:value-of select='atom:author/atom:name'/>
</xsl:element>
</xsl:when>
<xsl:when test='atom:author/atom:uri'>
<xsl:element name='a'>
<xsl:attribute name='href'>
<xsl:value-of select='atom:author/atom:uri'/>
</xsl:attribute>
<xsl:attribute name='title'>
<xsl:value-of select='atom:author/atom:summary'/>
</xsl:attribute>
<xsl:value-of select='atom:author/atom:name'/>
</xsl:element>
</xsl:when>
<xsl:when test='atom:author/atom:name'>
<xsl:text>By </xsl:text>
<xsl:value-of select='atom:author/atom:name'/>
</xsl:when>
</xsl:choose>
</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 summary of GitLab Atom Syndication Feeds -->
<xsl:if test='atom:content or atom:summary'>
<div class='content'>
<xsl:choose>
@ -331,75 +360,63 @@ xmlns:atom='http://www.w3.org/2005/Atom'>
</div>
</xsl:if>
<!-- entry enclosure -->
<xsl:if test='atom:link[contains(@rel,"enclosure")]'>
<div class='enclosure' title='Right-click and Save link as…'>
<xsl:for-each select='atom:link[contains(@rel,"enclosure")]'>
<xsl:element name='span'>
<xsl:attribute name='icon'>
<xsl:value-of select='substring-before(@type,"/")'/>
</xsl:attribute>
</xsl:element>
<xsl:element name='a'>
<xsl:attribute name='href'>
<xsl:value-of select='@href'/>
</xsl:attribute>
<xsl:attribute name='download'/>
<xsl:call-template name='extract-filename'>
<xsl:with-param name='url' select='@href' />
</xsl:call-template>
</xsl:element>
<xsl:element name='span'>
<xsl:attribute name='class'>
<xsl:value-of select='substring-before(@type,"/")'/>
</xsl:attribute>
</xsl:element>
<xsl:if test='@length &gt; 0'>
<xsl:call-template name='transform-filesize'>
<xsl:with-param name='length' select='@length' />
</xsl:call-template>
</xsl:if>
<xsl:element name='br'/>
</xsl:for-each>
<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: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…'>
<xsl:element name='span'>
<xsl:attribute name='icon'>
<xsl:value-of select='substring-before(@type,"/")'/>
</xsl:attribute>
</xsl:element>
<xsl:element name='a'>
<xsl:attribute name='href'>
<xsl:value-of select='@href'/>
</xsl:attribute>
<xsl:attribute name='download'/>
<xsl:call-template name='extract-filename'>
<xsl:with-param name='url' select='@href' />
</xsl:call-template>
</xsl:element>
<xsl:element name='span'>
<xsl:attribute name='class'>
<xsl:value-of select='substring-before(@type,"/")'/>
</xsl:attribute>
</xsl:element>
<xsl:if test='@length &gt; 0'>
<xsl:call-template name='transform-filesize'>
<xsl:with-param name='length' select='@length' />
</xsl:call-template>
</xsl:if>
<br/>
</div>
</xsl:for-each>
</div>
</xsl:if>
</div>
<!-- entry id -->
<xsl:if test='not(atom:id)'>
<div class='warning atom1 id'></div>
<div class='warning atom1 id'>No entry ID</div>
</xsl:if>
</li>
</xsl:for-each>
</ul>
</xsl:when>
<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:choose>
</div>
@ -417,8 +434,16 @@ xmlns:atom='http://www.w3.org/2005/Atom'>
title='The Social Platform Shaped For Your Community.'>
Movim
</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/'
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
</a>
<a href='https://xmpp.org/'
@ -433,12 +458,15 @@ xmlns:atom='http://www.w3.org/2005/Atom'>
<!-- note -->
<p id='note'>
<i>
This is an XMPP news feed which is conveyed as an XHTML
document, and it can even be viewed by a syndication
feed reader which provides automated notifications on
desktop and mobile. <span id="selection-link">Click
here</span> for a selection of software and pick the
ones that would fit you best!
This is an Atom document which is conveyed as an XHTML
document;
This document can also be viewed with a Syndication Feed
Reader (also referred to as News Reader or RSS Reader)
which provides automated news updates and notifications
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>
</p>
</body>

View file

@ -1,17 +1,19 @@
<?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
include https://schimon.i2p/ in attribute name='generator' of
element <meta/> inside of html element </head>
-->
<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'
omit-xml-decleration='no' />
<!-- Atom 1.0 Syndication Format -->
media-type = 'text/x-opml'
method = 'html'
version = '4.01' />
<!-- Outline Processor Markup Language 1.0 -->
<xsl:include href='opml_as_xhtml.xsl'/>
<!-- set page metadata -->
<xsl:include href='metadata.xsl'/>

View file

@ -10,9 +10,6 @@ element <meta/> inside of html element </head>
<xsl:stylesheet version='1.0'
xmlns:xsl='http://www.w3.org/1999/XSL/Transform'
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'>
<!-- index right-to-left language codes -->
<!-- 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 name='metadata'>
<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 name='metadata'>
<xsl:with-param name='name' select='"mimetype"' />
@ -41,10 +38,10 @@ xmlns:xml='http://www.w3.org/XML/1998/namespace'>
</xsl:call-template>
<title>
<xsl:choose>
<xsl:when test='title and not(title="")'>
<xsl:value-of select='title'/>
<xsl:when test='//head/title and not(//head/title="")'>
<xsl:value-of select='//head/title'/>
</xsl:when>
<xsl:otherwise>StreamBurner</xsl:otherwise>
<xsl:otherwise>Rivista OPML</xsl:otherwise>
</xsl:choose>
</title>
<!-- TODO media='print' -->
@ -54,20 +51,9 @@ xmlns:xml='http://www.w3.org/XML/1998/namespace'>
<xsl:if test='$rtl'>
<link id='semitic' href='/css/stylesheet-rtl.css' rel='stylesheet' type='text/css' />
</xsl:if>
<script type='text/javascript' src='/script/marked.min.js'/>
<script type='text/javascript' src='/script/postprocess.js'/>
</head>
<body>
<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/'
title='Of the benefits of XMPP.'>
XMPP
@ -76,6 +62,11 @@ xmlns:xml='http://www.w3.org/XML/1998/namespace'>
title='Syndictaion and PubSub.'>
Groupchat
</a>
<a href='https://aboutfeeds.com/'
title='Of the benefits of syndication feed.'
id='aboutfeeds'>
Help
</a>
</div>
<div id='feed'>
<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:value-of select='//head/title'/>
</xsl:when>
<xsl:when test='//outline'>
<xsl:value-of select='//outline/@text'/>
</xsl:when>
<xsl:otherwise>
StreamBurner OPML Feed
Rivista OPML Collection
</xsl:otherwise>
</xsl:choose>
</h1>
@ -109,12 +97,12 @@ xmlns:xml='http://www.w3.org/XML/1998/namespace'>
<div id='menu'>
<h3>Subscriptions</h3>
<!-- xsl:for-each select='outline[position() &lt;21]' -->
<ul>
<ol>
<xsl:for-each select='//outline[not(position() &gt; 20)]'>
<li>
<xsl:element name='a'>
<xsl:attribute name='href'>
<xsl:text>#stremburner-</xsl:text>
<xsl:text>#rivista-</xsl:text>
<xsl:value-of select='position()'/>
</xsl:attribute>
<xsl:choose>
@ -128,7 +116,7 @@ xmlns:xml='http://www.w3.org/XML/1998/namespace'>
</xsl:element>
</li>
</xsl:for-each>
</ul>
</ol>
</div>
</xsl:if>
<div id='articles'>
@ -149,7 +137,7 @@ xmlns:xml='http://www.w3.org/XML/1998/namespace'>
<xsl:value-of select='@text'/>
</xsl:attribute>
<xsl:attribute name='id'>
<xsl:text>stremburner-</xsl:text>
<xsl:text>rivista-</xsl:text>
<xsl:value-of select='position()'/>
</xsl:attribute>
<xsl:choose>
@ -193,8 +181,16 @@ xmlns:xml='http://www.w3.org/XML/1998/namespace'>
title='The Social Platform Shaped For Your Community.'>
Movim
</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/'
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
</a>
<a href='https://xmpp.org/'
@ -209,15 +205,16 @@ xmlns:xml='http://www.w3.org/XML/1998/namespace'>
<!-- note -->
<p id='note'>
<i>
This is an
<b title ='Outline Processor Markup Language'>OPML</b>
document which is conveyed as an XHTML document; This
document includes a list of subscriptionsis and is
intended to be imported to a syndication feed reader
which provides automated notifications on desktop and
mobile. <span id="selection-link">Click here</span> for
a selection of software and pick the ones that would fit
you best!
This is an OPML document which is conveyed as an XHTML
document;
This document includes a collection of subscriptions,
and it can be imported to a Syndication Feed Reader
(also referred to as News Reader or RSS Reader) reader
which provides automated news updates and notifications
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>
</p>
</body>