forked from sch/Rivista
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:
parent
040d532fb9
commit
cb4507bb78
8 changed files with 510 additions and 322 deletions
26
README.md
26
README.md
|
@ -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
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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')
|
||||
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
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
}
|
||||
|
|
10
xsl/atom.xsl
10
xsl/atom.xsl
|
@ -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'/>
|
||||
|
|
|
@ -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() <21]' -->
|
||||
<ul>
|
||||
<ol>
|
||||
<xsl:for-each select='atom:entry[not(position() > 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,10 +223,29 @@ xmlns:atom='http://www.w3.org/2005/Atom'>
|
|||
</span>
|
||||
</xsl:when>
|
||||
</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 -->
|
||||
<!-- xsl:if test='atom:author'>
|
||||
<span class='author'>
|
||||
<xsl:if test='atom:author'>
|
||||
<h4 class='author'>
|
||||
<xsl:choose>
|
||||
<xsl:when test='atom:author/atom:email'>
|
||||
<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:element>
|
||||
</xsl:when>
|
||||
<xsl:otherwise>
|
||||
<xsl:value-of select='atom:name'/>
|
||||
</xsl:otherwise>
|
||||
</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 test='atom:author/atom:name'>
|
||||
<xsl:text>By </xsl:text>
|
||||
<xsl:value-of select='atom:author/atom:name'/>
|
||||
</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>
|
||||
<!-- /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 summary of GitLab Atom Syndication Feeds -->
|
||||
<xsl:if test='atom:content or atom:summary'>
|
||||
<div class='content'>
|
||||
<xsl:choose>
|
||||
|
@ -331,9 +360,11 @@ xmlns:atom='http://www.w3.org/2005/Atom'>
|
|||
</div>
|
||||
</xsl:if>
|
||||
<!-- 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…'>
|
||||
<xsl:for-each select='atom:link[contains(@rel,"enclosure")]'>
|
||||
<xsl:element name='span'>
|
||||
<xsl:attribute name='icon'>
|
||||
<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: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 > 0'>
|
||||
<xsl:call-template name='transform-filesize'>
|
||||
<xsl:with-param name='length' select='@fileSize' />
|
||||
</xsl:call-template>
|
||||
</xsl:if>
|
||||
<xsl:element name='br'/>
|
||||
<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>
|
||||
|
|
10
xsl/opml.xsl
10
xsl/opml.xsl
|
@ -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'/>
|
||||
|
|
|
@ -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) > 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() <21]' -->
|
||||
<ul>
|
||||
<ol>
|
||||
<xsl:for-each select='//outline[not(position() > 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>
|
||||
|
|
Loading…
Reference in a new issue