forked from sch/Rivista
Add OPML support;
Set a new default node (Thank you roughnecks); Improve CSS, JS, XSLT; Neglect external libraries to produce syndications.
This commit is contained in:
parent
e07ff6e838
commit
d1f1edbaca
8 changed files with 519 additions and 185 deletions
|
@ -5,8 +5,8 @@ pass = "" # Password.
|
||||||
|
|
||||||
# A default node, when no arguments are set.
|
# A default node, when no arguments are set.
|
||||||
[default]
|
[default]
|
||||||
pubsub = "blog.jmp.chat" # Jabber ID.
|
pubsub = "pubsub.woodpeckersnest.space" # Jabber ID.
|
||||||
nodeid = "urn:xmpp:microblog:0" # Node ID.
|
nodeid = "PlanetJabber" # Node ID.
|
||||||
|
|
||||||
# Settings
|
# Settings
|
||||||
[settings]
|
[settings]
|
||||||
|
|
|
@ -7,6 +7,17 @@ body {
|
||||||
background: #000;
|
background: #000;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
code, pre {
|
||||||
|
overflow: auto;
|
||||||
|
display: inline-block;
|
||||||
|
max-height: 500px;
|
||||||
|
max-width: 100%; }
|
||||||
|
|
||||||
|
img, video {
|
||||||
|
width: auto;
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
|
||||||
h1#title, h2#subtitle, #actions, #references {
|
h1#title, h2#subtitle, #actions, #references {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
|
@ -64,6 +75,7 @@ h1#title, h2#subtitle, #actions, #references {
|
||||||
width: 20%;
|
width: 20%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#articles #journal ol,
|
||||||
#articles #journal ul {
|
#articles #journal ul {
|
||||||
/* height: 500px; */
|
/* height: 500px; */
|
||||||
line-height: 160%;
|
line-height: 160%;
|
||||||
|
@ -74,24 +86,25 @@ h1#title, h2#subtitle, #actions, #references {
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
#articles > ul > li > div > p.content {
|
#articles div.content {
|
||||||
font-size: 120%;
|
font-size: 120%;
|
||||||
line-height: 30px;
|
line-height: 200%;
|
||||||
margin: auto;
|
margin: auto;
|
||||||
padding-left: 2%;
|
padding-left: 2%;
|
||||||
padding-right: 10%;
|
padding-right: 10%;
|
||||||
/* text-align: justify; */
|
/* text-align: justify; */
|
||||||
|
word-wrap: break-word;
|
||||||
}
|
}
|
||||||
|
|
||||||
#articles > ul > li > div.entry {
|
#articles div.entry {
|
||||||
padding-bottom: 0.67em;
|
padding-bottom: 0.67em;
|
||||||
}
|
}
|
||||||
|
|
||||||
#articles > ul > li > div.entry h1 {
|
#articles div.entry h1 {
|
||||||
font-size: 2vw;
|
font-size: 2vw;
|
||||||
}
|
}
|
||||||
|
|
||||||
#articles > ul > li > div.entry h2 {
|
#articles div.entry h2 {
|
||||||
font-size: 1.5vw;
|
font-size: 1.5vw;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -138,7 +151,7 @@ h1#title, h2#subtitle, #actions, #references {
|
||||||
text-decoration: underline;
|
text-decoration: underline;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 950px) {
|
@media (max-width: 1525px) {
|
||||||
#articles {
|
#articles {
|
||||||
display: unset;
|
display: unset;
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,10 +2,10 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
import datetime
|
import datetime
|
||||||
|
from dateutil import parser
|
||||||
from fastapi import FastAPI, Request, Response
|
from fastapi import FastAPI, Request, Response
|
||||||
from fastapi.responses import FileResponse
|
from fastapi.responses import FileResponse
|
||||||
from fastapi.staticfiles import StaticFiles
|
from fastapi.staticfiles import StaticFiles
|
||||||
import feedgenerator
|
|
||||||
import json
|
import json
|
||||||
from slixmpp import ClientXMPP
|
from slixmpp import ClientXMPP
|
||||||
from slixmpp.exceptions import IqError, IqTimeout
|
from slixmpp.exceptions import IqError, IqTimeout
|
||||||
|
@ -57,12 +57,11 @@ async def view_pubsub(request: Request):
|
||||||
if pubsub and node and item_id:
|
if pubsub and node and item_id:
|
||||||
iq = await get_node_item(pubsub, node, item_id)
|
iq = await get_node_item(pubsub, node, item_id)
|
||||||
if iq:
|
if iq:
|
||||||
link = 'xmpp:{pubsub}?;node={node};item={item}'.format(
|
link = form_an_item_link(pubsub, node, item_id)
|
||||||
pubsub=pubsub, node=node, item=item_id)
|
xml_atom = generate_atom(iq, link)
|
||||||
xml_atom = generate_rfc_4287(iq, link)
|
|
||||||
iq = await get_node_items(pubsub, node)
|
iq = await get_node_items(pubsub, node)
|
||||||
if iq:
|
if iq:
|
||||||
generate_json(iq, node)
|
generate_json(iq)
|
||||||
else:
|
else:
|
||||||
operator = get_configuration('settings')['operator']
|
operator = get_configuration('settings')['operator']
|
||||||
json_data = [{'title' : 'Error retrieving items list.',
|
json_data = [{'title' : 'Error retrieving items list.',
|
||||||
|
@ -79,7 +78,8 @@ async def view_pubsub(request: Request):
|
||||||
else:
|
else:
|
||||||
text = 'Please check that PubSub node and item are valid and accessible.'
|
text = 'Please check that PubSub node and item are valid and accessible.'
|
||||||
xml_atom = error_message(text)
|
xml_atom = error_message(text)
|
||||||
result = append_stylesheet(xml_atom)
|
result = append_stylesheet(
|
||||||
|
xml_atom, 'atom.xsl', namespace='http://www.w3.org/2005/Atom')
|
||||||
|
|
||||||
# try:
|
# try:
|
||||||
# iq = await get_node_items(pubsub, node)
|
# iq = await get_node_items(pubsub, node)
|
||||||
|
@ -94,32 +94,37 @@ async def view_pubsub(request: Request):
|
||||||
elif pubsub and node:
|
elif pubsub and node:
|
||||||
iq = await get_node_items(pubsub, node)
|
iq = await get_node_items(pubsub, node)
|
||||||
if iq:
|
if iq:
|
||||||
link = form_a_link(pubsub, node)
|
link = form_a_node_link(pubsub, node)
|
||||||
xml_atom = generate_rfc_4287(iq, link)
|
xml_atom = generate_atom(iq, link)
|
||||||
else:
|
else:
|
||||||
text = 'Please check that PubSub node is valid and accessible.'
|
text = 'Please check that PubSub node is valid and accessible.'
|
||||||
xml_atom = error_message(text)
|
xml_atom = error_message(text)
|
||||||
result = append_stylesheet(xml_atom)
|
result = append_stylesheet(
|
||||||
|
xml_atom, 'atom.xsl', namespace='http://www.w3.org/2005/Atom')
|
||||||
elif pubsub:
|
elif pubsub:
|
||||||
iq = await get_nodes(pubsub)
|
iq = await get_nodes(pubsub)
|
||||||
if iq:
|
if iq:
|
||||||
link = 'xmpp:{pubsub}'.format(pubsub=pubsub)
|
link = 'xmpp:{pubsub}'.format(pubsub=pubsub)
|
||||||
result = pubsub_to_opml(iq)
|
xml_opml = generate_opml(iq)
|
||||||
|
result = append_stylesheet(xml_opml, 'opml.xsl')
|
||||||
else:
|
else:
|
||||||
text = 'Please check that PubSub Jabber ID is valid and accessible.'
|
text = 'Please check that PubSub Jabber ID is valid and accessible.'
|
||||||
xml_atom = error_message(text)
|
xml_atom = error_message(text)
|
||||||
result = append_stylesheet(xml_atom)
|
result = append_stylesheet(
|
||||||
|
xml_atom, 'atom.xsl', namespace='http://www.w3.org/2005/Atom')
|
||||||
elif node:
|
elif node:
|
||||||
text = 'PubSub parameter is missing.'
|
text = 'PubSub parameter is missing.'
|
||||||
xml_atom = error_message(text)
|
xml_atom = error_message(text)
|
||||||
result = append_stylesheet(xml_atom)
|
result = append_stylesheet(
|
||||||
|
xml_atom, 'atom.xsl', namespace='http://www.w3.org/2005/Atom')
|
||||||
# else:
|
# else:
|
||||||
# result = ('Mandatory parameter PubSub and '
|
# result = ('Mandatory parameter PubSub and '
|
||||||
# 'optional parameter Node are missing.')
|
# 'optional parameter Node are missing.')
|
||||||
else:
|
else:
|
||||||
text = 'The given domain {} is not allowed.'.format(pubsub)
|
text = 'The given domain {} is not allowed.'.format(pubsub)
|
||||||
xml_atom = error_message(text)
|
xml_atom = error_message(text)
|
||||||
result = append_stylesheet(xml_atom)
|
result = append_stylesheet(
|
||||||
|
xml_atom, 'atom.xsl', namespace='http://www.w3.org/2005/Atom')
|
||||||
default = get_configuration('default')
|
default = get_configuration('default')
|
||||||
if not result:
|
if not result:
|
||||||
if default['pubsub'] and default['nodeid']:
|
if default['pubsub'] and default['nodeid']:
|
||||||
|
@ -127,20 +132,23 @@ async def view_pubsub(request: Request):
|
||||||
pubsub = default['pubsub']
|
pubsub = default['pubsub']
|
||||||
node = default['nodeid']
|
node = default['nodeid']
|
||||||
iq = await get_node_items(pubsub, node)
|
iq = await get_node_items(pubsub, node)
|
||||||
link = form_a_link(pubsub, node)
|
link = form_a_node_link(pubsub, node)
|
||||||
xml_atom = generate_rfc_4287(iq, link)
|
xml_atom = generate_rfc_4287(iq, link)
|
||||||
result = append_stylesheet(xml_atom)
|
result = append_stylesheet(
|
||||||
|
xml_atom, 'atom.xsl', namespace='http://www.w3.org/2005/Atom')
|
||||||
elif not settings['service']:
|
elif not settings['service']:
|
||||||
pubsub = default['pubsub']
|
pubsub = default['pubsub']
|
||||||
node = default['nodeid']
|
node = default['nodeid']
|
||||||
iq = await get_node_items(pubsub, node)
|
iq = await get_node_items(pubsub, node)
|
||||||
link = form_a_link(pubsub, node)
|
link = form_a_node_link(pubsub, node)
|
||||||
xml_atom = generate_rfc_4287(iq, link)
|
xml_atom = generate_rfc_4287(iq, link)
|
||||||
result = append_stylesheet(xml_atom)
|
result = append_stylesheet(
|
||||||
|
xml_atom, 'atom.xsl', namespace='http://www.w3.org/2005/Atom')
|
||||||
else:
|
else:
|
||||||
text = 'Please contact the administrator and ask him to set default PubSub and Node ID.'
|
text = 'Please contact the administrator and ask him to set default PubSub and Node ID.'
|
||||||
xml_atom = error_message(text)
|
xml_atom = error_message(text)
|
||||||
result = append_stylesheet(xml_atom)
|
result = append_stylesheet(
|
||||||
|
xml_atom, 'atom.xsl', namespace='http://www.w3.org/2005/Atom')
|
||||||
response = Response(content=result, media_type="application/xml")
|
response = Response(content=result, media_type="application/xml")
|
||||||
return response
|
return response
|
||||||
|
|
||||||
|
@ -166,104 +174,125 @@ async def get_node_items(pubsub, node):
|
||||||
|
|
||||||
async def get_nodes(pubsub):
|
async def get_nodes(pubsub):
|
||||||
try:
|
try:
|
||||||
await xmpp.plugin['xep_0060'].get_nodes(pubsub, timeout=5)
|
iq = await xmpp.plugin['xep_0060'].get_nodes(pubsub, timeout=5)
|
||||||
return iq
|
return iq
|
||||||
except (IqError, IqTimeout) as e:
|
except (IqError, IqTimeout) as e:
|
||||||
print(e)
|
print(e)
|
||||||
|
|
||||||
def form_a_link(pubsub, node):
|
def form_a_node_link(pubsub, node):
|
||||||
link = 'xmpp:{pubsub}?;node={node}'.format(pubsub=pubsub, node=node)
|
link = 'xmpp:{pubsub}?;node={node}'.format(pubsub=pubsub, node=node)
|
||||||
return link
|
return link
|
||||||
|
|
||||||
|
def form_an_item_link(pubsub, node, item_id):
|
||||||
|
link = 'xmpp:{pubsub}?;node={node};item={item}'.format(
|
||||||
|
pubsub=pubsub, node=node, item=item_id)
|
||||||
|
return link
|
||||||
|
|
||||||
def error_message(text):
|
def error_message(text):
|
||||||
"""Error message in RFC 4287: The Atom Syndication Format."""
|
"""Error message in RFC 4287: The Atom Syndication Format."""
|
||||||
feed = feedgenerator.Atom1Feed(
|
title = 'StreamBurner'
|
||||||
description = ('This is a syndication feed generated with XMPP Journal '
|
subtitle = 'XMPP Journal Publisher'
|
||||||
'Publisher (XJP), which conveys XEP-0060: Publish-'
|
description = ('This is a syndication feed generated with XMPP Journal '
|
||||||
'Subscribe nodes to standard RFC 4287: The Atom '
|
'Publisher, which conveys XEP-0060: Publish-Subscribe '
|
||||||
'Syndication Format.'),
|
'nodes to standard RFC 4287: The Atom Syndication Format.')
|
||||||
language = 'en',
|
language = 'en'
|
||||||
link = '',
|
feed = ET.Element("feed")
|
||||||
subtitle = 'XMPP Journal Publisher',
|
feed.set('xmlns', 'http://www.w3.org/2005/Atom')
|
||||||
title = 'StreamBurner')
|
ET.SubElement(feed, 'title', {'type': 'text'}).text = title
|
||||||
namespace = '{http://www.w3.org/2005/Atom}'
|
ET.SubElement(feed, 'subtitle', {'type': 'text'}).text = subtitle
|
||||||
feed_url = 'gemini://schimon.i2p/'
|
ET.SubElement(feed, 'author', {'name':'XMPP Journal Publisher','email':'xjp@schimon.i2p'})
|
||||||
# create entry
|
ET.SubElement(feed, 'generator', {
|
||||||
feed.add_item(
|
'uri': 'https://git.xmpp-it.net/sch/XMPPJournalPublisher',
|
||||||
description = text,
|
'version': '0.1'}).text = 'XMPP Journal Publisher (XJP)'
|
||||||
# enclosure = feedgenerator.Enclosure(enclosure, enclosure_size, enclosure_type) if args.entry_enclosure else None,
|
ET.SubElement(feed, 'updated').text = datetime.datetime.now(datetime.UTC).isoformat()
|
||||||
link = '',
|
entry = ET.SubElement(feed, 'entry')
|
||||||
# pubdate = updated,
|
ET.SubElement(entry, 'title').text = 'Error'
|
||||||
title = 'Error',
|
ET.SubElement(entry, 'id').text = 'xjp-error'
|
||||||
# unique_id = ''
|
ET.SubElement(entry, 'updated').text = datetime.datetime.now(datetime.UTC).isoformat()
|
||||||
)
|
ET.SubElement(entry, 'published').text = datetime.datetime.now(datetime.UTC).isoformat()
|
||||||
xml_atom = feed.writeString('utf-8')
|
# ET.SubElement(entry, 'summary', {'type': summary_type_text}).text = summary_text
|
||||||
xml_atom_extended = append_element(
|
ET.SubElement(entry, 'content', {'type': 'text'}).text = text
|
||||||
xml_atom,
|
return ET.tostring(feed, encoding='unicode')
|
||||||
'generator',
|
|
||||||
'XMPP Journal Publisher (XJP)')
|
|
||||||
return xml_atom_extended
|
|
||||||
|
|
||||||
def generate_rfc_4287(iq, link):
|
# generate_rfc_4287
|
||||||
"""Convert XEP-0060: Publish-Subscribe to RFC 4287: The Atom Syndication Format."""
|
def generate_atom(iq, link):
|
||||||
feed = feedgenerator.Atom1Feed(
|
"""Generate an Atom Syndication Format (RFC 4287) from a Publish-Subscribe (XEP-0060) node items."""
|
||||||
description = ('This is a syndication feed generated with PubSub To '
|
pubsub = iq['from'].bare
|
||||||
'Atom, which conveys XEP-0060: Publish-Subscribe nodes '
|
node = iq['pubsub']['items']['node']
|
||||||
'to standard RFC 4287: The Atom Syndication Format.'),
|
title = node
|
||||||
language = iq['pubsub']['items']['lang'],
|
link = link
|
||||||
link = link,
|
# link = form_a_node_link(pubsub, node)
|
||||||
subtitle = 'XMPP PubSub Syndication Feed',
|
subtitle = 'XMPP PubSub Syndication Feed'
|
||||||
title = iq['pubsub']['items']['node'])
|
description = ('This is a syndication feed generated with XMPP Journal '
|
||||||
# See also iq['pubsub']['items']['substanzas']
|
'Publisher, which conveys XEP-0060: Publish-Subscribe '
|
||||||
entries = iq['pubsub']['items']
|
'nodes to standard RFC 4287: The Atom Syndication Format.')
|
||||||
for entry in entries:
|
language = iq['pubsub']['items']['lang']
|
||||||
item = entry['payload']
|
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/XMPPJournalPublisher',
|
||||||
|
'version': '0.1'}).text = 'XMPP Journal Publisher (XJP)'
|
||||||
|
ET.SubElement(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}'
|
namespace = '{http://www.w3.org/2005/Atom}'
|
||||||
title = item.find(namespace + 'title')
|
title = item_payload.find(namespace + 'title')
|
||||||
title = None if title == None else title.text
|
title_text = None if title == None else title.text
|
||||||
updated = item.find(namespace + 'updated')
|
# link = item_payload.find(namespace + 'link')
|
||||||
updated = None if updated == None else updated.text
|
# link_href = '' if link == None else link.attrib['href']
|
||||||
published = item.find(namespace + 'published')
|
link_href = form_an_item_link(pubsub, node, item_id)
|
||||||
published = None if published == None else published.text
|
if not title_text or not link_href: continue
|
||||||
if not updated and not published: updated = datetime.datetime.utcnow().isoformat()
|
content = item_payload.find(namespace + 'content')
|
||||||
content = item.find(namespace + 'content')
|
content_text = 'No content' if content == None else content.text
|
||||||
content = 'No content' if content == None else content.text
|
if content.attrib:
|
||||||
link = item.find(namespace + 'link')
|
content_type = content.attrib['type'] if 'type' in content.attrib else 'text'
|
||||||
link = '' if link == None else link.attrib['href']
|
content_type_text = 'html' if 'html' in content_type else 'text'
|
||||||
author = item.find(namespace + 'author')
|
published = item_payload.find(namespace + 'published')
|
||||||
|
published_text = None if published == None else published.text
|
||||||
|
if published: published_dt = parser.parse(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)
|
if author and author.attrib: print(author.attrib)
|
||||||
author = 'None' if author == None else author.text
|
author_text = 'None' if author == None else author.text
|
||||||
# create entry
|
identifier = item_payload.find(namespace + 'id')
|
||||||
feed.add_item(
|
if identifier and identifier.attrib: print(identifier.attrib)
|
||||||
description = content,
|
identifier_text = 'None' if identifier == None else identifier.text
|
||||||
# enclosure = feedgenerator.Enclosure(enclosure, enclosure_size, enclosure_type) if args.entry_enclosure else None,
|
entry = ET.SubElement(feed, 'entry')
|
||||||
link = link,
|
ET.SubElement(entry, 'title').text = title_text
|
||||||
pubdate = published or updated,
|
ET.SubElement(entry, 'link', {'href': link_href})
|
||||||
title = title,
|
ET.SubElement(entry, 'id').text = identifier_text
|
||||||
unique_id = link)
|
ET.SubElement(entry, 'updated').text = updated_text
|
||||||
xml_atom = feed.writeString('utf-8')
|
ET.SubElement(entry, 'published').text = published_text
|
||||||
xml_atom_extended = append_element(
|
# ET.SubElement(entry, 'summary', {'type': summary_type_text}).text = summary_text
|
||||||
xml_atom,
|
ET.SubElement(entry, 'content', {'type': content_type_text}).text = content_text
|
||||||
'generator',
|
return ET.tostring(feed, encoding='unicode')
|
||||||
'XMPP Journal Publisher (XJP)')
|
|
||||||
return xml_atom_extended
|
|
||||||
|
|
||||||
def generate_json(iq, node):
|
def generate_json(iq):
|
||||||
"""Create a JSON file from node items."""
|
"""Create a JSON file from node items."""
|
||||||
json_data = []
|
json_data = []
|
||||||
|
pubsub = iq['from'].bare
|
||||||
|
node = iq['pubsub']['items']['node']
|
||||||
entries = iq['pubsub']['items']
|
entries = iq['pubsub']['items']
|
||||||
for entry in entries:
|
for entry in entries:
|
||||||
item = entry['payload']
|
item_id = entry['id']
|
||||||
|
item_payload = entry['payload']
|
||||||
namespace = '{http://www.w3.org/2005/Atom}'
|
namespace = '{http://www.w3.org/2005/Atom}'
|
||||||
title = item.find(namespace + 'title')
|
title = item_payload.find(namespace + 'title')
|
||||||
title = None if title == None else title.text
|
title_text = None if title == None else title.text
|
||||||
# updated = item.find(namespace + 'updated')
|
# updated = item.find(namespace + 'updated')
|
||||||
# updated = None if updated == None else updated.text
|
# updated = None if updated == None else updated.text
|
||||||
# if updated: updated = datetime.datetime.fromisoformat(updated)
|
# if updated: updated = datetime.datetime.fromisoformat(updated)
|
||||||
link = item.find(namespace + 'link')
|
link_href = form_an_item_link(pubsub, node, item_id)
|
||||||
link = '' if link == None else link.attrib['href']
|
# link = item.find(namespace + 'link')
|
||||||
json_data_entry = {'title' : title,
|
# link_href = '' if link == None else link.attrib['href']
|
||||||
'link' : link}
|
json_data_entry = {'title' : title_text,
|
||||||
|
'link' : link_href}
|
||||||
json_data.append(json_data_entry)
|
json_data.append(json_data_entry)
|
||||||
if len(json_data) > 6: break
|
if len(json_data) > 6: break
|
||||||
filename = 'data/{}.json'.format(node)
|
filename = 'data/{}.json'.format(node)
|
||||||
|
@ -286,15 +315,40 @@ def append_element(xml_data, element, text):
|
||||||
|
|
||||||
"""Patch function to append XSLT reference to XML"""
|
"""Patch function to append XSLT reference to XML"""
|
||||||
"""Why is not this a built-in function of ElementTree or LXML"""
|
"""Why is not this a built-in function of ElementTree or LXML"""
|
||||||
def append_stylesheet(xml_data):
|
def append_stylesheet(xml_data, filename, namespace=None):
|
||||||
# Register namespace in order to avoide ns0:
|
# Register namespace in order to avoide ns0:
|
||||||
ET.register_namespace("", "http://www.w3.org/2005/Atom")
|
if namespace: ET.register_namespace("", namespace)
|
||||||
# Load XML from string
|
# Load XML from string
|
||||||
tree = ET.fromstring(xml_data)
|
tree = ET.fromstring(xml_data)
|
||||||
# The following direction removes the XML declaration
|
# The following direction removes the XML declaration
|
||||||
xml_data_no_declaration = ET.tostring(tree, encoding='unicode')
|
xml_data_without_a_declaration = ET.tostring(tree, encoding='unicode')
|
||||||
# Add XML declaration and stylesheet
|
# Add XML declaration and stylesheet
|
||||||
xml_data_declaration = ('<?xml version="1.0" encoding="utf-8"?>'
|
xml_data_declaration = (
|
||||||
'<?xml-stylesheet type="text/xsl" href="xsl/stylesheet.xsl"?>' +
|
'<?xml version="1.0" encoding="utf-8"?>'
|
||||||
xml_data_no_declaration)
|
'<?xml-stylesheet type="text/xsl" href="xsl/{}"?>'.format(filename) +
|
||||||
|
xml_data_without_a_declaration)
|
||||||
return xml_data_declaration
|
return xml_data_declaration
|
||||||
|
|
||||||
|
def generate_opml(iq):
|
||||||
|
pubsub = iq['from'].bare
|
||||||
|
items = iq['disco_items']['items']
|
||||||
|
opml = ET.Element("opml")
|
||||||
|
opml.set("version", "1.0")
|
||||||
|
head = ET.SubElement(opml, "head")
|
||||||
|
ET.SubElement(head, "title").text = 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/XMPPJournalPublisher")
|
||||||
|
time_stamp = datetime.datetime.now(datetime.UTC).isoformat()
|
||||||
|
ET.SubElement(head, "dateCreated").text = time_stamp
|
||||||
|
ET.SubElement(head, "dateModified").text = time_stamp
|
||||||
|
body = ET.SubElement(opml, "body")
|
||||||
|
for item in items:
|
||||||
|
pubsub, node, title = item
|
||||||
|
uri = form_a_node_link(pubsub, node)
|
||||||
|
outline = ET.SubElement(body, "outline")
|
||||||
|
outline.set("text", title or node)
|
||||||
|
outline.set("xmlUrl", uri)
|
||||||
|
return ET.tostring(opml, encoding='unicode')
|
||||||
|
|
|
@ -1,84 +1,99 @@
|
||||||
window.onload = async function(){
|
window.onload = async function(){
|
||||||
// Fix button follow
|
|
||||||
let follow = document.querySelector('#follow');
|
|
||||||
//let feedUrl = location.href.replace(/^https?:/, 'feed:');
|
|
||||||
let locationHref = new URL(location.href);
|
let locationHref = new URL(location.href);
|
||||||
let node = locationHref.searchParams.get('node')
|
let node = locationHref.searchParams.get('node')
|
||||||
let pubsub = locationHref.searchParams.get('pubsub')
|
let pubsub = locationHref.searchParams.get('pubsub')
|
||||||
let feedUrl = `feed://${location.host}/atom?pubsub=${pubsub}&node=${node}`;
|
// Fix button follow
|
||||||
follow.href = feedUrl;
|
let follow = document.querySelector('#follow');
|
||||||
follow.addEventListener ('click', function() {
|
if (follow) {
|
||||||
window.open(feedUrl, "_self");
|
//let feedUrl = location.href.replace(/^https?:/, 'feed:');
|
||||||
});
|
let feedUrl = `feed://${location.host}/atom?pubsub=${pubsub}&node=${node}`;
|
||||||
// Fix button subtome
|
follow.href = feedUrl;
|
||||||
document.querySelector('#subtome').href='https://www.subtome.com/#/subscribe?feeds=' + feedUrl;
|
follow.addEventListener ('click', function() {
|
||||||
|
window.open(feedUrl, "_self");
|
||||||
|
});
|
||||||
|
// Fix button subtome
|
||||||
|
document.querySelector('#subtome').href='https://www.subtome.com/#/subscribe?feeds=' + feedUrl;
|
||||||
|
}
|
||||||
// Convert ISO8601 To UTC
|
// Convert ISO8601 To UTC
|
||||||
for (let element of document.querySelectorAll('#articles > ul > li > div > h4, #feed > #header > h2#subtitle.date')) {
|
for (let element of document.querySelectorAll(
|
||||||
|
'#articles > ul > li > div > h4.published,' +
|
||||||
|
'#articles > ul > li > div > h4.updated, ' +
|
||||||
|
'#feed > #header > h2#subtitle.date')) {
|
||||||
let timeStamp = new Date(element.textContent);
|
let timeStamp = new Date(element.textContent);
|
||||||
element.textContent = timeStamp.toUTCString();
|
element.textContent = timeStamp.toUTCString();
|
||||||
}
|
}
|
||||||
// Parse Markdown
|
// Parse Markdown
|
||||||
for (let element of document.querySelectorAll('#articles > ul > li > div > p')) {
|
for (let element of document.querySelectorAll('#articles > ul > li > div > p[type="text"]')) {
|
||||||
let markDown = element.textContent
|
let markDown = element.textContent
|
||||||
element.innerHTML = marked.parse(markDown);
|
element.innerHTML = marked.parse(markDown);
|
||||||
}
|
}
|
||||||
// Build a journal list
|
// Build a journal list
|
||||||
itemsList = await openJson(node)
|
if (node) {
|
||||||
if (itemsList && locationHref.searchParams.get('item')) {
|
itemsList = await openJson(node)
|
||||||
node = locationHref.searchParams.get('node')
|
if (itemsList && locationHref.searchParams.get('item')) {
|
||||||
pubsub = locationHref.searchParams.get('pubsub')
|
node = locationHref.searchParams.get('node')
|
||||||
let elementDiv = document.createElement('div');
|
pubsub = locationHref.searchParams.get('pubsub')
|
||||||
elementDiv.id = 'journal';
|
let elementDiv = document.createElement('div');
|
||||||
let elementH3 = document.createElement('h3');
|
elementDiv.id = 'journal';
|
||||||
elementH3.textContent = 'Journal';
|
let elementH3 = document.createElement('h3');
|
||||||
elementDiv.appendChild(elementH3);
|
elementH3.textContent = 'Journal';
|
||||||
let elementH4 = document.createElement('h4');
|
elementDiv.appendChild(elementH3);
|
||||||
elementH4.textContent = node;
|
let elementH4 = document.createElement('h4');
|
||||||
elementDiv.appendChild(elementH4);
|
elementH4.textContent = node;
|
||||||
let elementUl = document.createElement('ul');
|
elementDiv.appendChild(elementH4);
|
||||||
elementDiv.appendChild(elementUl);
|
let elementUl = document.createElement('ol');
|
||||||
for (let item of itemsList) {
|
elementDiv.appendChild(elementUl);
|
||||||
let elementLi = document.createElement('li');
|
for (let item of itemsList) {
|
||||||
let elementA = document.createElement('a');
|
let elementLi = document.createElement('li');
|
||||||
elementA.textContent = item.title;
|
let elementA = document.createElement('a');
|
||||||
elementA.href = item.link;
|
elementA.textContent = item.title;
|
||||||
elementLi.appendChild(elementA);
|
elementA.href = item.link;
|
||||||
elementUl.appendChild(elementLi);
|
elementLi.appendChild(elementA);
|
||||||
|
elementUl.appendChild(elementLi);
|
||||||
|
}
|
||||||
|
let elementB = document.createElement('b');
|
||||||
|
elementB.textContent = 'Actions';
|
||||||
|
elementDiv.appendChild(elementB);
|
||||||
|
let elementUl2 = document.createElement('ul');
|
||||||
|
elementDiv.appendChild(elementUl2);
|
||||||
|
links = [
|
||||||
|
{'text' : 'Subscribe from an XMPP client.',
|
||||||
|
'href' : `xmpp:${pubsub}?pubsub;action=subscribe;node=${node}`},
|
||||||
|
{'text' : 'Subscribe with a News Reader.',
|
||||||
|
'href' : `feed://${location.host}/atom?pubsub=${pubsub}&node=${node}`},
|
||||||
|
{'text' : 'Browse the journal.',
|
||||||
|
'href' : `atom?pubsub=${pubsub}&node=${node}`},
|
||||||
|
{'text' : 'Browse the portal.',
|
||||||
|
'href' : `atom?pubsub=${pubsub}`}
|
||||||
|
]
|
||||||
|
for (let link of links) {
|
||||||
|
let elementLi = document.createElement('li');
|
||||||
|
let elementA = document.createElement('a');
|
||||||
|
elementA.textContent = link.text;
|
||||||
|
elementA.href = link.href;
|
||||||
|
elementLi.appendChild(elementA);
|
||||||
|
elementUl2.appendChild(elementLi);
|
||||||
|
}
|
||||||
|
elementDiv.appendChild(elementUl2);
|
||||||
|
// document.querySelector('#feed').appendChild(elementDiv); // This would result in a combination of Title, Article, and Journal
|
||||||
|
document.querySelector('#articles').appendChild(elementDiv);
|
||||||
}
|
}
|
||||||
let elementB = document.createElement('b');
|
|
||||||
elementB.textContent = 'Actions';
|
|
||||||
elementDiv.appendChild(elementB);
|
|
||||||
let elementUl2 = document.createElement('ul');
|
|
||||||
elementDiv.appendChild(elementUl2);
|
|
||||||
links = [
|
|
||||||
{'text' : 'Subscribe from an XMPP client.',
|
|
||||||
'href' : `xmpp:${pubsub}?pubsub;action=subscribe;node=${node}`},
|
|
||||||
{'text' : 'Subscribe with a News Reader.',
|
|
||||||
'href' : `feed://${location.host}/atom?pubsub=${pubsub}&node=${node}`},
|
|
||||||
{'text' : 'Browse the journal.',
|
|
||||||
'href' : `atom?pubsub=${pubsub}&node=${node}`}
|
|
||||||
]
|
|
||||||
for (let link of links) {
|
|
||||||
let elementLi = document.createElement('li');
|
|
||||||
let elementA = document.createElement('a');
|
|
||||||
elementA.textContent = link.text;
|
|
||||||
elementA.href = link.href;
|
|
||||||
elementLi.appendChild(elementA);
|
|
||||||
elementUl2.appendChild(elementLi);
|
|
||||||
}
|
|
||||||
elementDiv.appendChild(elementUl2);
|
|
||||||
// document.querySelector('#feed').appendChild(elementDiv); // This would result in a combination of Title, Article, and Journal
|
|
||||||
document.querySelector('#articles').appendChild(elementDiv);
|
|
||||||
}
|
}
|
||||||
// Convert URI xmpp: to URI http: links.
|
// Convert URI xmpp: to URI http: links.
|
||||||
for (let xmppLink of document.querySelectorAll('a[href^="xmpp:"]')) {
|
for (let xmppLink of document.querySelectorAll('#articles > ul a[href^="xmpp:"], ol a[href^="xmpp:"]')) {
|
||||||
xmppUri = new URL(xmppLink);
|
xmppUri = new URL(xmppLink);
|
||||||
let parameters = xmppUri.search.split(';');
|
let parameters = xmppUri.search.split(';');
|
||||||
try {
|
try {
|
||||||
let node = parameters.find(parameter => parameter.startsWith('node=')).split('=')[1];
|
try {
|
||||||
let item = parameters.find(parameter => parameter.startsWith('item=')).split('=')[1];
|
let node = parameters.find(parameter => parameter.startsWith('node=')).split('=')[1];
|
||||||
let pubsub = xmppUri.pathname;
|
let item = parameters.find(parameter => parameter.startsWith('item=')).split('=')[1];
|
||||||
xmppLink.href = `atom?pubsub=${pubsub}&node=${node}&item=${item}`
|
let pubsub = xmppUri.pathname;
|
||||||
|
xmppLink.href = `atom?pubsub=${pubsub}&node=${node}&item=${item}`
|
||||||
|
} catch {
|
||||||
|
let node = parameters.find(parameter => parameter.startsWith('node=')).split('=')[1];
|
||||||
|
let pubsub = xmppUri.pathname;
|
||||||
|
xmppLink.href = `atom?pubsub=${pubsub}&node=${node}`
|
||||||
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.warn(err)
|
console.warn(err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,22 +8,15 @@ element <meta/> inside of html element </head>
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
|
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
|
||||||
|
<xsl:output method = 'html'
|
||||||
<xsl:output
|
indent = 'yes'
|
||||||
method = 'html'
|
omit-xml-decleration='no' />
|
||||||
indent = 'yes'
|
|
||||||
omit-xml-decleration='no' />
|
|
||||||
|
|
||||||
<!-- Atom 1.0 Syndication Format -->
|
<!-- Atom 1.0 Syndication Format -->
|
||||||
<xsl:include href='atom_as_xhtml.xsl'/>
|
<xsl:include href='atom_as_xhtml.xsl'/>
|
||||||
|
|
||||||
<!-- extract filename from given url string -->
|
<!-- extract filename from given url string -->
|
||||||
<xsl:include href='extract-filename.xsl'/>
|
<xsl:include href='extract-filename.xsl'/>
|
||||||
|
|
||||||
<!-- set page metadata -->
|
<!-- set page metadata -->
|
||||||
<xsl:include href='metadata.xsl'/>
|
<xsl:include href='metadata.xsl'/>
|
||||||
|
|
||||||
<!-- transform filesize from given length string -->
|
<!-- transform filesize from given length string -->
|
||||||
<xsl:include href='transform-filesize.xsl'/>
|
<xsl:include href='transform-filesize.xsl'/>
|
||||||
|
|
||||||
</xsl:stylesheet>
|
</xsl:stylesheet>
|
|
@ -1,7 +1,7 @@
|
||||||
<?xml version="1.0" encoding="UTF-8" ?>
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
Copyright (C) 2016 - 2017 Schimon Jehuda. Released under MIT license
|
Copyright (C) 2016 - 2024 Schimon Jehuda. Released under MIT license
|
||||||
Feeds rendered using this XSLT stylesheet, or it's derivatives, must
|
Feeds rendered using this XSLT stylesheet, or it's derivatives, must
|
||||||
include https://schimon.i2p/ in attribute name='generator' of
|
include https://schimon.i2p/ in attribute name='generator' of
|
||||||
element <meta/> inside of html element </head>
|
element <meta/> inside of html element </head>
|
||||||
|
@ -46,9 +46,12 @@ xmlns:atom='http://www.w3.org/2005/Atom'>
|
||||||
</xsl:call-template>
|
</xsl:call-template>
|
||||||
<title>
|
<title>
|
||||||
<xsl:choose>
|
<xsl:choose>
|
||||||
<xsl:when test='atom:title and not(atom:title="")'>
|
<xsl:when test='atom:title and not(atom:title="") and count(atom:entry) > 1'>
|
||||||
<xsl:value-of select='atom:title'/>
|
<xsl:value-of select='atom:title'/>
|
||||||
</xsl:when>
|
</xsl:when>
|
||||||
|
<xsl:when test='atom:entry'>
|
||||||
|
<xsl:value-of select='atom:entry/atom:title'/>
|
||||||
|
</xsl:when>
|
||||||
<xsl:otherwise>StreamBurner</xsl:otherwise>
|
<xsl:otherwise>StreamBurner</xsl:otherwise>
|
||||||
</xsl:choose>
|
</xsl:choose>
|
||||||
</title>
|
</title>
|
||||||
|
@ -282,7 +285,7 @@ xmlns:atom='http://www.w3.org/2005/Atom'>
|
||||||
<!-- entry content -->
|
<!-- entry content -->
|
||||||
<!-- entry summary of GitLab Atom Syndication Feeds -->
|
<!-- entry summary of GitLab Atom Syndication Feeds -->
|
||||||
<xsl:if test='atom:content or atom:summary'>
|
<xsl:if test='atom:content or atom:summary'>
|
||||||
<p class='content'>
|
<div class='content'>
|
||||||
<xsl:choose>
|
<xsl:choose>
|
||||||
<xsl:when test='atom:summary[contains(@type,"text")]'>
|
<xsl:when test='atom:summary[contains(@type,"text")]'>
|
||||||
<xsl:attribute name='type'>
|
<xsl:attribute name='type'>
|
||||||
|
@ -290,6 +293,12 @@ xmlns:atom='http://www.w3.org/2005/Atom'>
|
||||||
</xsl:attribute>
|
</xsl:attribute>
|
||||||
<xsl:value-of select='atom:summary'/>
|
<xsl:value-of select='atom:summary'/>
|
||||||
</xsl:when>
|
</xsl:when>
|
||||||
|
<xsl:when test='atom:summary[contains(@type,"html")]'>
|
||||||
|
<xsl:attribute name='type'>
|
||||||
|
<xsl:value-of select='atom:summary/@type'/>
|
||||||
|
</xsl:attribute>
|
||||||
|
<xsl:value-of select='atom:summary' disable-output-escaping='yes'/>
|
||||||
|
</xsl:when>
|
||||||
<xsl:when test='atom:summary[contains(@type,"base64")]'>
|
<xsl:when test='atom:summary[contains(@type,"base64")]'>
|
||||||
<!-- TODO add xsl:template to handle inline media -->
|
<!-- TODO add xsl:template to handle inline media -->
|
||||||
</xsl:when>
|
</xsl:when>
|
||||||
|
@ -299,6 +308,12 @@ xmlns:atom='http://www.w3.org/2005/Atom'>
|
||||||
</xsl:attribute>
|
</xsl:attribute>
|
||||||
<xsl:value-of select='atom:content'/>
|
<xsl:value-of select='atom:content'/>
|
||||||
</xsl:when>
|
</xsl:when>
|
||||||
|
<xsl:when test='atom:content[contains(@type,"html")]'>
|
||||||
|
<xsl:attribute name='type'>
|
||||||
|
<xsl:value-of select='atom:content/@type'/>
|
||||||
|
</xsl:attribute>
|
||||||
|
<xsl:value-of select='atom:content' disable-output-escaping='yes'/>
|
||||||
|
</xsl:when>
|
||||||
<xsl:when test='atom:content[contains(@type,"base64")]'>
|
<xsl:when test='atom:content[contains(@type,"base64")]'>
|
||||||
<!-- TODO add xsl:template to handle inline media -->
|
<!-- TODO add xsl:template to handle inline media -->
|
||||||
</xsl:when>
|
</xsl:when>
|
||||||
|
@ -313,7 +328,7 @@ xmlns:atom='http://www.w3.org/2005/Atom'>
|
||||||
</xsl:choose>
|
</xsl:choose>
|
||||||
</xsl:otherwise>
|
</xsl:otherwise>
|
||||||
</xsl:choose>
|
</xsl:choose>
|
||||||
</p>
|
</div>
|
||||||
</xsl:if>
|
</xsl:if>
|
||||||
<!-- entry enclosure -->
|
<!-- entry enclosure -->
|
||||||
<xsl:if test='atom:link[contains(@rel,"enclosure")]'>
|
<xsl:if test='atom:link[contains(@rel,"enclosure")]'>
|
||||||
|
|
18
xsl/opml.xsl
Normal file
18
xsl/opml.xsl
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Copyright (C) 2016 - 2017 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'
|
||||||
|
indent = 'yes'
|
||||||
|
omit-xml-decleration='no' />
|
||||||
|
<!-- Atom 1.0 Syndication Format -->
|
||||||
|
<xsl:include href='opml_as_xhtml.xsl'/>
|
||||||
|
<!-- set page metadata -->
|
||||||
|
<xsl:include href='metadata.xsl'/>
|
||||||
|
</xsl:stylesheet>
|
226
xsl/opml_as_xhtml.xsl
Normal file
226
xsl/opml_as_xhtml.xsl
Normal file
|
@ -0,0 +1,226 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
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'
|
||||||
|
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 -->
|
||||||
|
<xsl:variable name='rtl'
|
||||||
|
select='lang[
|
||||||
|
contains(self::node(),"ar") or
|
||||||
|
contains(self::node(),"fa") or
|
||||||
|
contains(self::node(),"he") or
|
||||||
|
contains(self::node(),"ji") or
|
||||||
|
contains(self::node(),"ku") or
|
||||||
|
contains(self::node(),"ur") or
|
||||||
|
contains(self::node(),"yi")]'/>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<xsl:call-template name='metadata'>
|
||||||
|
<xsl:with-param name='name' select='"description"' />
|
||||||
|
<xsl:with-param name='content' select='subtitle' />
|
||||||
|
</xsl:call-template>
|
||||||
|
<xsl:call-template name='metadata'>
|
||||||
|
<xsl:with-param name='name' select='"generator"' />
|
||||||
|
<xsl:with-param name='content' select='StreamBurner' />
|
||||||
|
</xsl:call-template>
|
||||||
|
<xsl:call-template name='metadata'>
|
||||||
|
<xsl:with-param name='name' select='"mimetype"' />
|
||||||
|
<xsl:with-param name='content' select='"text/x-opml"' />
|
||||||
|
</xsl:call-template>
|
||||||
|
<title>
|
||||||
|
<xsl:choose>
|
||||||
|
<xsl:when test='title and not(title="")'>
|
||||||
|
<xsl:value-of select='title'/>
|
||||||
|
</xsl:when>
|
||||||
|
<xsl:otherwise>StreamBurner</xsl:otherwise>
|
||||||
|
</xsl:choose>
|
||||||
|
</title>
|
||||||
|
<!-- TODO media='print' -->
|
||||||
|
<link href='/css/stylesheet.css' rel='stylesheet' type='text/css' media='screen'/>
|
||||||
|
<link rel='icon' type='image/svg+xml' href='/graphic/xmpp.svg'/>
|
||||||
|
<!-- whether language code is of direction right-to-left -->
|
||||||
|
<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
|
||||||
|
</a>
|
||||||
|
<a href='https://join.jabber.network/#syndication@conference.movim.eu?join'
|
||||||
|
title='Syndictaion and PubSub.'>
|
||||||
|
Groupchat
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div id='feed'>
|
||||||
|
<div id='header'>
|
||||||
|
<!-- feed title -->
|
||||||
|
<h1 id='title'>
|
||||||
|
<xsl:choose>
|
||||||
|
<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
|
||||||
|
</xsl:otherwise>
|
||||||
|
</xsl:choose>
|
||||||
|
</h1>
|
||||||
|
<!-- feed subtitle -->
|
||||||
|
<h2 id='subtitle'>
|
||||||
|
<xsl:choose>
|
||||||
|
<xsl:when test='//head/description and not(//head/description="") and count(//outline) > 1'>
|
||||||
|
<xsl:value-of select='//head/description'/>
|
||||||
|
</xsl:when>
|
||||||
|
<xsl:otherwise>
|
||||||
|
Outline Processor Markup Language
|
||||||
|
</xsl:otherwise>
|
||||||
|
</xsl:choose>
|
||||||
|
</h2>
|
||||||
|
</div>
|
||||||
|
<xsl:if test='count(//outline) > 1'>
|
||||||
|
<div id='menu'>
|
||||||
|
<h3>Subscriptions</h3>
|
||||||
|
<!-- xsl:for-each select='outline[position() <21]' -->
|
||||||
|
<ul>
|
||||||
|
<xsl:for-each select='//outline[not(position() > 20)]'>
|
||||||
|
<li>
|
||||||
|
<xsl:element name='a'>
|
||||||
|
<xsl:attribute name='href'>
|
||||||
|
<xsl:text>#stremburner-</xsl:text>
|
||||||
|
<xsl:value-of select='position()'/>
|
||||||
|
</xsl:attribute>
|
||||||
|
<xsl:choose>
|
||||||
|
<xsl:when test='string-length(@text) > 0'>
|
||||||
|
<xsl:value-of select='@text'/>
|
||||||
|
</xsl:when>
|
||||||
|
<xsl:otherwise>
|
||||||
|
*** No Title ***
|
||||||
|
</xsl:otherwise>
|
||||||
|
</xsl:choose>
|
||||||
|
</xsl:element>
|
||||||
|
</li>
|
||||||
|
</xsl:for-each>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</xsl:if>
|
||||||
|
<div id='articles'>
|
||||||
|
<!-- opml outline -->
|
||||||
|
<xsl:choose>
|
||||||
|
<xsl:when test='//outline'>
|
||||||
|
<ul>
|
||||||
|
<xsl:for-each select='//outline[not(position() > 20)]'>
|
||||||
|
<li>
|
||||||
|
<div class='entry'>
|
||||||
|
<!-- outline title -->
|
||||||
|
<h3 class='title'>
|
||||||
|
<xsl:element name='a'>
|
||||||
|
<xsl:attribute name='href'>
|
||||||
|
<xsl:value-of select='@xmlUrl'/>
|
||||||
|
</xsl:attribute>
|
||||||
|
<xsl:attribute name='title'>
|
||||||
|
<xsl:value-of select='@text'/>
|
||||||
|
</xsl:attribute>
|
||||||
|
<xsl:attribute name='id'>
|
||||||
|
<xsl:text>stremburner-</xsl:text>
|
||||||
|
<xsl:value-of select='position()'/>
|
||||||
|
</xsl:attribute>
|
||||||
|
<xsl:choose>
|
||||||
|
<xsl:when test='string-length(@text) > 0'>
|
||||||
|
<xsl:value-of select='@text'/>
|
||||||
|
</xsl:when>
|
||||||
|
<xsl:otherwise>
|
||||||
|
*** No Title ***
|
||||||
|
</xsl:otherwise>
|
||||||
|
</xsl:choose>
|
||||||
|
</xsl:element>
|
||||||
|
</h3>
|
||||||
|
<!-- entry content -->
|
||||||
|
<h4>
|
||||||
|
<xsl:value-of select='@text'/>
|
||||||
|
</h4>
|
||||||
|
<p class='content'>
|
||||||
|
<xsl:value-of select='@xmlUrl'/>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
</xsl:for-each>
|
||||||
|
</ul>
|
||||||
|
</xsl:when>
|
||||||
|
<xsl:otherwise>
|
||||||
|
<div class='notice no-entry'></div>
|
||||||
|
</xsl:otherwise>
|
||||||
|
</xsl:choose>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id='references'>
|
||||||
|
<a href='https://joinjabber.org/'
|
||||||
|
title='An Inclusive Space On The Jabber Network.'>
|
||||||
|
JoinJabber
|
||||||
|
</a>
|
||||||
|
<a href='https://libervia.org/'
|
||||||
|
title='The Universal Communication Ecosystem.'>
|
||||||
|
Libervia
|
||||||
|
</a>
|
||||||
|
<a href='https://join.movim.eu/'
|
||||||
|
title='The Social Platform Shaped For Your Community.'>
|
||||||
|
Movim
|
||||||
|
</a>
|
||||||
|
<a href='https://modernxmpp.org/'
|
||||||
|
title='A Project To Improve The Quality Of User-To-User Messaging Applications That Use Xmpp.'>
|
||||||
|
Modern
|
||||||
|
</a>
|
||||||
|
<a href='https://xmpp.org/'
|
||||||
|
title='The Universal Messaging Standard.'>
|
||||||
|
XMPP
|
||||||
|
</a>
|
||||||
|
<a href='https://xmpp.org/extensions/xep-0060.html'
|
||||||
|
title='XEP-0060: Publish-Subscribe.'>
|
||||||
|
PubSub
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<!-- note -->
|
||||||
|
<p id='note'>
|
||||||
|
<i>
|
||||||
|
This is an
|
||||||
|
<b title ='Outline Processor Markup Language'>OPML</b>
|
||||||
|
document which is conveyed as an HTML 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!
|
||||||
|
</i>
|
||||||
|
</p>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
</xsl:template>
|
||||||
|
</xsl:stylesheet>
|
Loading…
Reference in a new issue