- gopher? gopher.

- no js needed button
- removed donation, I'll figure out something better, sometimes, in the future
- text/plain alternate, also for gopher
- better Pandoc subclassing
- bye message
- first image becomes OG image
- removed duplicate reply symbol
- DAT .well-known prepare, not active
- oembed singular vars, should they ever be needed
- fixed target lookup for webmentions so it works both with index.html or with path only
This commit is contained in:
Peter Molnar 2019-02-25 22:40:01 +00:00
parent 67662b69e9
commit 9f73a9b111
10 changed files with 513 additions and 230 deletions

View file

@ -0,0 +1,5 @@
<!-- Generated by IcoMoon.io -->
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
<title>facebook</title>
<path fill="#3b5998" d="M15.117 0h-14.235c-0.487 0-0.883 0.395-0.883 0.883v14.235c0 0.488 0.395 0.883 0.883 0.883h7.663v-6.196h-2.086v-2.414h2.086v-1.783c0-2.066 1.263-3.19 3.106-3.19 0.883 0 1.643 0.065 1.864 0.094v2.16h-1.281c-1 0-1.195 0.481-1.195 1.181v1.541h2.389l-0.31 2.42h-2.079v6.188h4.077c0.489 0 0.883-0.395 0.883-0.883v-14.235c0-0.487-0.395-0.883-0.883-0.883z"></path>
</svg>

After

Width:  |  Height:  |  Size: 543 B

27
assets/nojs-button.svg Normal file
View file

@ -0,0 +1,27 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="80" height="15" version="1.1" viewBox="0 0 80 15" xmlns="http://www.w3.org/2000/svg">
<rect width="80" height="15" fill="#666"/>
<rect x="1" y="1" width="78" height="13" fill="#fff"/>
<rect x="16" y="2" width="62" height="11" fill="#666"/>
<g fill="#fff">
<path d="m27 5v1h2v-1zm2 1v3h1v-3zm0 3h-2v1h2zm-2 0v-3h-1v3z"/>
<path d="m20 5h1v1h1v1h1v1h1v-3h1v5h-1v-1h-1v-1h-1v-1h-1v3h-1z"/>
<rect x="-1" y="259.03" width="78" height="13"/>
<path d="m70 5v5h3v-1h-2v-3h2v-1zm3 1v3h1v-3z"/>
<path d="m66 5h3v1h-2v1h2v1h-2v1h2v1h-3z"/>
<circle cx="7.5" cy="7.5" r="5.5" stroke="#666" style="paint-order:markers fill stroke"/>
</g>
<g fill="#666">
<rect transform="rotate(-45)" x="-6" y="10" width="11" height="1"/>
<path d="m9 5h3v1h-3v1h2v1h1v1h-1v1h-3v-1h3v-1h-2v-1h-1v-1h1"/>
<path d="m6 5v4h1v-4zm0 4h-2v1h2zm-2 0v-1h-1v1z"/>
</g>
<g fill="#fff">
<path d="m40 5h3v1h-3v1h2v1h1v1h-1v1h-3v-1h3v-1h-2v-1h-1v-1h1"/>
<path d="m37 5v4h1v-4zm0 4h-2v1h2zm-2 0v-1h-1v1z"/>
<path d="m57 5h3v1h-2v1h2v1h-2v1h2v1h-3z"/>
<path d="m61 5v5h3v-1h-2v-3h2v-1zm3 1v3h1v-3z"/>
<path d="m53 5h3v1h-2v1h2v1h-2v1h2v1h-3z"/>
<path d="m47 5h1v1h1v1h1v1h1v-3h1v5h-1v-1h-1v-1h-1v-1h-1v3h-1z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View file

@ -18,6 +18,7 @@ EXIFDATE = re.compile(
r'(?P<time>[0-9]{2}:[0-9]{2}:[0-9]{2})$'
)
class CachedMeta(dict):
def __init__(self, fpath):
self.fpath = fpath
@ -25,7 +26,7 @@ class CachedMeta(dict):
@property
def cfile(self):
fname = os.path.basename(self.fpath)
if fname == 'index.md':
if fname == 'index.md':
fname = os.path.basename(os.path.dirname(self.fpath))
return os.path.join(

338
nasg.py
View file

@ -21,6 +21,7 @@ from math import ceil
from urllib.parse import urlparse
from collections import OrderedDict, namedtuple
import logging
import csv
import arrow
import langdetect
@ -31,8 +32,9 @@ import frontmatter
from feedgen.feed import FeedGenerator
from slugify import slugify
import requests
import lxml.etree as etree
from pandoc import PandocMarkdown
from pandoc import PandocMarkdown, PandocTXT
from meta import Exif
import settings
from settings import struct
@ -40,6 +42,12 @@ import keys
logger = logging.getLogger('NASG')
CATEGORY = 'category'
MDFILE = 'index.md'
TXTFILE = 'index.txt'
HTMLFILE = 'index.html'
GOPHERFILE = 'gophermap'
MarkdownImage = namedtuple(
'MarkdownImage',
['match', 'alt', 'fname', 'title', 'css']
@ -72,6 +80,7 @@ def mtime(path):
return int(os.path.getmtime(path))
return 0
def utfyamldump(data):
""" dump YAML with actual UTF-8 chars """
return yaml.dump(
@ -81,6 +90,7 @@ def utfyamldump(data):
allow_unicode=True
)
def url2slug(url, limit=200):
""" convert URL to max 200 char ASCII string """
return slugify(
@ -89,13 +99,16 @@ def url2slug(url, limit=200):
lower=True
)[:limit]
J2.filters['url2slug'] = url2slug
def rfc3339todt(rfc3339):
""" nice dates for humans """
t = arrow.get(rfc3339).format('YYYY-MM-DD HH:mm ZZZ')
return "%s" % (t)
J2.filters['printdate'] = rfc3339todt
RE_MYURL = re.compile(
@ -105,6 +118,7 @@ RE_MYURL = re.compile(
)
)
def relurl(text, baseurl=None):
if not baseurl:
baseurl = settings.site.url
@ -118,15 +132,17 @@ def relurl(text, baseurl=None):
r = os.path.relpath(url, baseurl)
if url.endswith('/') and not r.endswith('/'):
r = "%s/index.html" % r
r = "%s/%s" % (r, HTMLFILE)
if needsquotes:
r = '"%s"' % r
logger.debug("RELURL: %s => %s (base: %s)", match, r, baseurl)
text = text.replace(match, r)
return text
J2.filters['relurl'] = relurl
def writepath(fpath, content, mtime=0):
""" f.write with extras """
d = os.path.dirname(fpath)
@ -163,6 +179,7 @@ class cached_property(object):
class AQ:
""" Async queue which starts execution right on population """
def __init__(self):
self.loop = asyncio.get_event_loop()
self.queue = asyncio.Queue(loop=self.loop)
@ -174,7 +191,7 @@ class AQ:
while not self.queue.empty():
item = await self.queue.get()
self.queue.task_done()
#asyncio.gather() ?
# asyncio.gather() ?
def run(self):
consumer = asyncio.ensure_future(self.consume())
@ -183,6 +200,7 @@ class AQ:
class Webmention(object):
""" outgoing webmention class """
def __init__(self, source, target, dpath, mtime=0):
self.source = source
self.target = target
@ -275,7 +293,7 @@ class MarkdownDoc(object):
def pandoc(self, c):
if c and len(c):
c = PandocMarkdown(c)
c = str(PandocMarkdown(c))
c = RE_PRECODE.sub(
'<pre><code lang="\g<1>" class="language-\g<1>">', c)
return c
@ -305,7 +323,9 @@ class Comment(MarkdownDoc):
@property
def targetname(self):
t = urlparse(self.meta.get('target'))
return t.path.rstrip('/').strip('/').split('/')[-1]
return os.path.split(t.path.lstrip('/'))[0]
#t = urlparse(self.meta.get('target'))
#return t.path.rstrip('/').strip('/').split('/')[-1]
@property
def source(self):
@ -335,11 +355,10 @@ class Comment(MarkdownDoc):
@property
def type(self):
return self.meta.get('type', 'webmention')
#if len(self.content):
#maybe = clean(self.content, strip=True)
#if maybe in UNICODE_EMOJI:
#return maybe
# if len(self.content):
#maybe = clean(self.content, strip=True)
# if maybe in UNICODE_EMOJI:
# return maybe
@cached_property
def jsonld(self):
@ -418,10 +437,24 @@ class Singular(MarkdownDoc):
maybe = c.dt
return maybe
@property
def dt(self):
dt = int(MarkdownDoc.dt.fget(self))
for maybe in self.comments.keys():
if int(dt) < int(maybe):
dt = int(maybe)
return dt
@property
def sameas(self):
r = []
for k in glob.glob(os.path.join(os.path.dirname(self.fpath), '*.copy')):
for k in glob.glob(
os.path.join(
os.path.dirname(self.fpath),
'*.copy'
)
):
with open(k, 'rt') as f:
r.append(f.read())
return r
@ -436,7 +469,7 @@ class Singular(MarkdownDoc):
files = [
k
for k in glob.glob(os.path.join(os.path.dirname(self.fpath), '*.md'))
if os.path.basename(k) != 'index.md'
if os.path.basename(k) != MDFILE
]
for f in files:
c = Comment(f)
@ -463,9 +496,9 @@ class Singular(MarkdownDoc):
images.update({match: WebImage(imgpath, mdimg, self)})
else:
logger.error("Missing image: %s, referenced in %s",
imgpath,
self.fpath
)
imgpath,
self.fpath
)
return images
@property
@ -493,7 +526,7 @@ class Singular(MarkdownDoc):
if len(self.images) != 1:
return False
photo = next(iter(self.images.values()))
maybe = self.fpath.replace("index.md", "%s.jpg" % (self.name))
maybe = self.fpath.replace(MDFILE, "%s.jpg" % (self.name))
if photo.fpath == maybe:
return True
return False
@ -630,46 +663,46 @@ class Singular(MarkdownDoc):
else:
return False
#@cached_property
#def oembed_xml(self):
#oembed = etree.Element("oembed", version="1.0")
#xmldoc = etree.ElementTree(oembed)
#for k, v in self.oembed_json.items():
#x = etree.SubElement(oembed, k).text = "%s" % (v)
#s = etree.tostring(
#xmldoc,
#encoding='utf-8',
#xml_declaration=True,
#pretty_print=True
#)
#return s
@cached_property
def oembed_xml(self):
oembed = etree.Element("oembed", version="1.0")
xmldoc = etree.ElementTree(oembed)
for k, v in self.oembed_json.items():
x = etree.SubElement(oembed, k).text = "%s" % (v)
s = etree.tostring(
xmldoc,
encoding='utf-8',
xml_declaration=True,
pretty_print=True
)
return s
#@cached_property
#def oembed_json(self):
#r = {
#"version": "1.0",
#"provider_name": settings.site.name,
#"provider_url": settings.site.url,
#"author_name": settings.author.name,
#"author_url": settings.author.url,
#"title": self.title,
#"type": "link",
#"html": self.html_content,
#}
@cached_property
def oembed_json(self):
r = {
"version": "1.0",
"provider_name": settings.site.name,
"provider_url": settings.site.url,
"author_name": settings.author.name,
"author_url": settings.author.url,
"title": self.title,
"type": "link",
"html": self.html_content,
}
#img = None
#if self.is_photo:
#img = self.photo
#elif not self.is_photo and len(self.images):
#img = list(self.images.values())[0]
#if img:
#r.update({
#"type": "rich",
#"thumbnail_url": img.jsonld.thumbnail.url,
#"thumbnail_width": img.jsonld.thumbnail.width,
#"thumbnail_height": img.jsonld.thumbnail.height
#})
#return r
img = None
if self.is_photo:
img = self.photo
elif not self.is_photo and len(self.images):
img = list(self.images.values())[0]
if img:
r.update({
"type": "rich",
"thumbnail_url": img.jsonld.thumbnail.url,
"thumbnail_width": img.jsonld.thumbnail.width,
"thumbnail_height": img.jsonld.thumbnail.height
})
return r
@cached_property
def review(self):
@ -744,7 +777,7 @@ class Singular(MarkdownDoc):
if self.is_photo:
r.update({
"@type": "Photograph",
"image": self.photo.jsonld,
#"image": self.photo.jsonld,
})
elif self.has_code:
r.update({
@ -754,11 +787,15 @@ class Singular(MarkdownDoc):
r.update({
"@type": "WebPage",
})
if not self.is_photo and len(self.images):
img = list(self.images.values())[0]
r.update({
"image": img.jsonld,
})
if len(self.images):
r["image"] = []
for img in list(self.images.values()):
r["image"].append(img.jsonld)
# if not self.is_photo and len(self.images):
# img = list(self.images.values())[0]
# r.update({
# "image": img.jsonld,
# })
if self.is_reply:
r.update({
@ -775,8 +812,8 @@ class Singular(MarkdownDoc):
if self.event:
r.update({"subjectOf": self.event})
for donation in settings.donateActions:
r["potentialAction"].append(donation)
#for donation in settings.donateActions:
#r["potentialAction"].append(donation)
for url in list(set(self.syndicate)):
r["potentialAction"].append({
@ -794,16 +831,29 @@ class Singular(MarkdownDoc):
def template(self):
return "%s.j2.html" % (self.__class__.__name__)
@property
def gophertemplate(self):
return "%s.j2.txt" % (self.__class__.__name__)
@property
def renderdir(self):
return os.path.dirname(self.renderfile)
return os.path.join(
settings.paths.get('build'),
self.name
)
@property
def renderfile(self):
return os.path.join(
settings.paths.get('build'),
self.name,
'index.html'
self.renderdir,
HTMLFILE
)
@property
def gopherfile(self):
return os.path.join(
self.renderdir,
TXTFILE
)
@property
@ -831,7 +881,15 @@ class Singular(MarkdownDoc):
])
async def copyfiles(self):
exclude = ['.md', '.jpg', '.png', '.gif', '.ping', '.url', '.del', '.copy']
exclude = [
'.md',
'.jpg',
'.png',
'.gif',
'.ping',
'.url',
'.del',
'.copy']
files = glob.glob(
os.path.join(
os.path.dirname(self.fpath),
@ -854,39 +912,41 @@ class Singular(MarkdownDoc):
logger.info("copying '%s' to '%s'", f, t)
cp(f, t)
@cached_property
def html(self):
r = J2.get_template(self.template).render({
async def render(self):
if self.exists:
return
logger.info("rendering %s", self.name)
v = {
'baseurl': self.url,
'post': self.jsonld,
'site': settings.site,
'menu': settings.menu,
'meta': settings.meta,
})
return r
async def render(self):
if self.exists:
return
logger.info("rendering %s", self.name)
}
writepath(
self.renderfile,
self.html
J2.get_template(self.template).render(v)
)
g = {
'post': self.jsonld,
'summary': PandocTXT(self.summary),
'content': PandocTXT(self.content)
}
writepath(
self.gopherfile,
J2.get_template(self.gophertemplate).render(g)
)
j = settings.site.copy()
j.update({
"mainEntity": self.jsonld
})
writepath(
os.path.join(self.renderdir,'index.json'),
os.path.join(self.renderdir, 'index.json'),
json.dumps(j, indent=4, ensure_ascii=False)
)
del(j)
cp(
self.fpath,
os.path.join(self.renderdir,'index.md')
)
class Home(Singular):
def __init__(self, fpath):
@ -904,7 +964,7 @@ class Home(Singular):
def renderfile(self):
return os.path.join(
settings.paths.get('build'),
'index.html'
HTMLFILE
)
@property
@ -916,6 +976,27 @@ class Home(Singular):
maybe = pts
return maybe
async def render_gopher(self):
lines = [
"%s's gopherhole - phlog, if you prefer" % (settings.site.name),
'',
''
]
for category, post in self.posts:
line = "1%s\t/%s/%s\t%s\t70" % (
category['name'],
CATEGORY,
category['name'],
settings.site.name
)
lines.append(line)
lines.append('')
lines.append('')
lines = lines + list(map(lambda x: ("%s" % x), settings.bye.split('\n')))
lines.append('')
writepath(self.renderfile.replace(HTMLFILE,GOPHERFILE), "\r\n".join(lines))
async def render(self):
if self.exists:
return
@ -929,6 +1010,7 @@ class Home(Singular):
'posts': self.posts
})
writepath(self.renderfile, r)
await self.render_gopher()
class WebImage(object):
@ -1111,7 +1193,7 @@ class WebImage(object):
'Model': ['Model'],
'FNumber': ['FNumber', 'Aperture'],
'ExposureTime': ['ExposureTime'],
'FocalLength': ['FocalLength'], # ['FocalLengthIn35mmFormat'],
'FocalLength': ['FocalLength'], # ['FocalLengthIn35mmFormat'],
'ISO': ['ISO'],
'LensID': ['LensID', 'LensSpec', 'Lens'],
'CreateDate': ['CreateDate', 'DateTimeOriginal']
@ -1189,7 +1271,8 @@ class WebImage(object):
def data(self):
with open(self.fpath, 'rb') as f:
encoded = base64.b64encode(f.read())
return "data:%s;base64,%s" % (self.parent.mime_type, encoded.decode('utf-8'))
return "data:%s;base64,%s" % (
self.parent.mime_type, encoded.decode('utf-8'))
@property
def suffix(self):
@ -1328,8 +1411,8 @@ class PHPFile(object):
raise ValueError('Not implemented')
async def render(self):
#if self.exists:
#return
# if self.exists:
# return
await self._render()
@ -1359,7 +1442,7 @@ class Search(PHPFile):
notindexed=mtime,
tokenize=porter
)'''
)
)
self.is_changed = False
def __exit__(self):
@ -1548,7 +1631,7 @@ class Category(dict):
@property
def url(self):
if len(self.name):
url = "%s/category/%s/" % (settings.site.get('url'), self.name)
url = "%s/%s/%s/" % (settings.site.get('url'), CATEGORY, self.name)
else:
url = '%s/' % (settings.site.get('url'))
return url
@ -1566,7 +1649,7 @@ class Category(dict):
if len(self.name):
return os.path.join(
settings.paths.get('build'),
'category',
CATEGORY,
self.name
)
else:
@ -1665,17 +1748,17 @@ class Category(dict):
'posts': posts,
}
def indexfpath(self, subpath=None):
def indexfpath(self, subpath=None, fname=HTMLFILE):
if subpath:
return os.path.join(
self.dpath,
subpath,
'index.html'
fname
)
else:
return os.path.join(
self.dpath,
'index.html'
fname
)
async def render_feed(self, xmlformat):
@ -1711,8 +1794,9 @@ class Category(dict):
fe.category({
'term': post.category,
'label': post.category,
'scheme': "%s/category/%s/" % (
'scheme': "%s/%s/%s/" % (
settings.site.get('url'),
CATEGORY,
post.category
)
})
@ -1758,6 +1842,32 @@ class Category(dict):
)
writepath(self.indexfpath(), r)
async def render_gopher(self):
lines = [
'%s - %s' % (self.name, settings.site.name),
'',
''
]
for post in self.get_posts():
line = "0%s\t/%s/%s\t%s\t70" % (
post.headline,
post.name,
TXTFILE,
settings.site.name
)
lines.append(line)
if isinstance(post['image'], list):
for img in post['image']:
line = "I%s\t/%s/%s\t%s\t70" % (
img.headline,
post.name,
img.name,
settings.site.name
)
lines.append(line)
lines.append('')
writepath(self.indexfpath(fname=GOPHERFILE), "\r\n".join(lines))
async def render_archives(self):
for year in self.years.keys():
if year == self.newest_year:
@ -1779,7 +1889,7 @@ class Category(dict):
end = index
if self.is_uptodate(fpath, self[self.sortedkeys[start]].dt):
logger.info("%s / %d is up to date", self.name, year)
logger.info("%s / %d is up to date", self.name, year)
else:
logger.info("updating %s / %d", self.name, year)
logger.info("getting posts from %d to %d", start, end)
@ -1788,7 +1898,7 @@ class Category(dict):
# I don't know why end needs the +1, but without that
# some posts disappear
# TODO figure this out...
self.get_posts(start, end+1),
self.get_posts(start, end + 1),
tyear
)
)
@ -1819,9 +1929,10 @@ class Category(dict):
self.name
)
async def render(self):
await self.render_feeds()
if not self.is_uptodate(self.indexfpath(), self.newest()):
await self.render_gopher()
if not self.is_paginated:
if not self.is_uptodate(self.indexfpath(), self.newest()):
logger.info(
@ -1829,6 +1940,7 @@ class Category(dict):
self.name
)
await self.render_flat()
else:
logger.info(
'%s flat index is up to date',
@ -1839,6 +1951,7 @@ class Category(dict):
await self.render_archives()
class Sitemap(dict):
@property
def mtime(self):
@ -1875,7 +1988,7 @@ class WebmentionIO(object):
newest = 0
content = settings.paths.get('content')
for e in glob.glob(os.path.join(content, '*', '*', '*.md')):
if os.path.basename(e) == 'index.md':
if os.path.basename(e) == MDFILE:
continue
# filenames are like [received epoch]-[slugified source url].md
try:
@ -1889,7 +2002,7 @@ class WebmentionIO(object):
continue
if mtime > newest:
newest = mtime
return arrow.get(newest+1)
return arrow.get(newest + 1)
def makecomment(self, webmention):
if 'published_ts' in webmention.get('data'):
@ -1899,12 +2012,19 @@ class WebmentionIO(object):
else:
dt = arrow.get(webmention.get('data').get('published'))
slug = webmention.get('target').strip('/').split('/')[-1]
slug = os.path.split(urlparse(webmention.get('target')).path.lstrip('/'))[0]
# ignore selfpings
if slug == settings.site.get('name'):
return
fdir = glob.glob(os.path.join(settings.paths.get('content'), '*', slug))
fdir = glob.glob(
os.path.join(
settings.paths.get('content'),
'*',
slug
)
)
if not len(fdir):
logger.error(
"couldn't find post for incoming webmention: %s",
@ -1962,6 +2082,7 @@ class WebmentionIO(object):
logger.error('failed to query webmention.io: %s', e)
pass
def make():
start = int(round(time.time() * 1000))
last = 0
@ -1989,7 +2110,7 @@ def make():
frontposts = Category()
home = Home(settings.paths.get('home'))
for e in sorted(glob.glob(os.path.join(content, '*', '*', 'index.md'))):
for e in sorted(glob.glob(os.path.join(content, '*', '*', MDFILE))):
post = Singular(e)
# deal with images, if needed
for i in post.images.values():
@ -2060,11 +2181,18 @@ def make():
for e in glob.glob(os.path.join(content, '*.*')):
if e.endswith('.md'):
continue
t = os.path.join(settings.paths.get('build'),os.path.basename(e))
t = os.path.join(settings.paths.get('build'), os.path.basename(e))
if os.path.exists(t) and mtime(e) <= mtime(t):
continue
cp(e, t)
# ...
#for url in settings.site.sameAs:
#if "dat://" in url:
#p = os.path.join(settings.paths.build, '.well-known', 'dat')
#if not os.path.exists(p):
#writepath(p, "%s\nTTL=3600" % (url))
end = int(round(time.time() * 1000))
logger.info('process took %d ms' % (end - start))

249
pandoc.py
View file

@ -7,34 +7,43 @@ __email__ = "mail@petermolnar.net"
import subprocess
import logging
class PandocMarkdown(str):
def __new__(cls, text):
""" Pandoc command line call with piped in- and output """
cmd = (
class PandocBase(str):
in_format = 'html'
in_options = []
out_format = 'plain'
out_options = []
columns = None
def __init__(self, text):
self.source = text
conv_to = '--to=%s' % (self.out_format)
if (len(self.out_options)):
conv_to = '%s+%s' % (
conv_to,
'+'.join(self.out_options)
)
conv_from = '--from=%s' % (self.in_format)
if (len(self.in_options)):
conv_from = '%s+%s' % (
conv_from,
'+'.join(self.in_options)
)
cmd = [
'pandoc',
'-o-',
'--from=markdown+%s' % (
'+'.join([
'footnotes',
'pipe_tables',
'strikeout',
#'superscript',
#'subscript',
'raw_html',
'definition_lists',
'backtick_code_blocks',
'fenced_code_attributes',
'shortcut_reference_links',
'lists_without_preceding_blankline',
'autolink_bare_uris',
])
),
'--to=html5',
conv_to,
conv_from,
'--quiet',
'--no-highlight'
)
]
if self.columns:
cmd.append(self.columns)
p = subprocess.Popen(
cmd,
tuple(cmd),
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
@ -48,46 +57,158 @@ class PandocMarkdown(str):
stderr
)
r = stdout.decode('utf-8').strip()
return str.__new__(cls, r)
self.result = r
class PandocHTML(str):
def __new__(cls, text):
""" Pandoc command line call with piped in- and output """
cmd = (
'pandoc',
'-o-',
'--to=markdown+%s' % (
'+'.join([
'footnotes',
'pipe_tables',
'strikeout',
#'superscript',
#'subscript',
'raw_html',
'definition_lists',
'backtick_code_blocks',
'fenced_code_attributes',
'shortcut_reference_links',
'lists_without_preceding_blankline',
'autolink_bare_uris',
])
),
'--from=html',
'--quiet',
)
p = subprocess.Popen(
cmd,
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
)
def __str__(self):
return str(self.result)
stdout, stderr = p.communicate(input=text.encode())
if stderr:
logging.warning(
"Error during pandoc covert:\n\t%s\n\t%s",
cmd,
stderr
)
r = stdout.decode('utf-8').strip()
return str.__new__(cls, r)
def __repr__(self):
return str(self.result)
class PandocMarkdown(PandocBase):
in_format = 'markdown'
in_options = [
'footnotes',
'pipe_tables',
'strikeout',
# 'superscript',
# 'subscript',
'raw_html',
'definition_lists',
'backtick_code_blocks',
'fenced_code_attributes',
'shortcut_reference_links',
'lists_without_preceding_blankline',
'autolink_bare_uris',
]
out_format = 'html5'
out_options = []
class PandocHTML(PandocBase):
in_format = 'html'
in_options = []
out_format = 'markdown'
out_options = [
'footnotes',
'pipe_tables',
'strikeout',
# 'superscript',
# 'subscript',
'raw_html',
'definition_lists',
'backtick_code_blocks',
'fenced_code_attributes',
'shortcut_reference_links',
'lists_without_preceding_blankline',
'autolink_bare_uris',
]
class PandocTXT(PandocBase):
in_format = 'markdown'
in_options = [
'footnotes',
'pipe_tables',
'strikeout',
# 'superscript',
# 'subscript',
'raw_html',
'definition_lists',
'backtick_code_blocks',
'fenced_code_attributes',
'shortcut_reference_links',
'lists_without_preceding_blankline',
'autolink_bare_uris',
]
out_format = 'plain'
out_options = []
columns = '--columns=72'
#class PandocMarkdown(str):
#def __new__(cls, text):
#""" Pandoc command line call with piped in- and output """
#cmd = (
#'pandoc',
#'-o-',
#'--from=markdown+%s' % (
#'+'.join([
#'footnotes',
#'pipe_tables',
#'strikeout',
## 'superscript',
## 'subscript',
#'raw_html',
#'definition_lists',
#'backtick_code_blocks',
#'fenced_code_attributes',
#'shortcut_reference_links',
#'lists_without_preceding_blankline',
#'autolink_bare_uris',
#])
#),
#'--to=html5',
#'--quiet',
#'--no-highlight'
#)
#p = subprocess.Popen(
#cmd,
#stdin=subprocess.PIPE,
#stdout=subprocess.PIPE,
#stderr=subprocess.PIPE,
#)
#stdout, stderr = p.communicate(input=text.encode())
#if stderr:
#logging.warning(
#"Error during pandoc covert:\n\t%s\n\t%s",
#cmd,
#stderr
#)
#r = stdout.decode('utf-8').strip()
#return str.__new__(cls, r)
#class PandocHTML(str):
#def __new__(cls, text):
#""" Pandoc command line call with piped in- and output """
#cmd = (
#'pandoc',
#'-o-',
#'--to=markdown+%s' % (
#'+'.join([
#'footnotes',
#'pipe_tables',
#'strikeout',
## 'superscript',
## 'subscript',
#'raw_html',
#'definition_lists',
#'backtick_code_blocks',
#'fenced_code_attributes',
#'shortcut_reference_links',
#'lists_without_preceding_blankline',
#'autolink_bare_uris',
#])
#),
#'--from=html',
#'--quiet',
#)
#p = subprocess.Popen(
#cmd,
#stdin=subprocess.PIPE,
#stdout=subprocess.PIPE,
#stderr=subprocess.PIPE,
#)
#stdout, stderr = p.communicate(input=text.encode())
#if stderr:
#logging.warning(
#"Error during pandoc covert:\n\t%s\n\t%s",
#cmd,
#stderr
#)
#r = stdout.decode('utf-8').strip()
#return str.__new__(cls, r)

View file

@ -47,6 +47,9 @@ site = struct({
"name": "petermolnar.net",
"image": "https://petermolnar.net/favicon.ico",
"license": "https://spdx.org/licenses/%s.html" % (licence['_default']),
#"sameAs": [
#"dat://8d03735af11d82fff82028e0f830f9ac470f5e9fbe10ab5eb6feb877232714a2"
#],
"author": {
"@context": "http://schema.org",
"@type": "Person",
@ -88,30 +91,28 @@ site = struct({
"@type": "FollowAction",
"url": "https://petermolnar.net/follow/",
"name": "follow"
}
},
#{
#"@context": "http://schema.org",
#"@type": "DonateAction",
#"description": "Monzo (only in the UK or via Google Pay)",
#"name": "monzo",
#"price": "3GBP",
#"url": "https://monzo.me/petermolnar/3",
#"recipient": author
#},
#{
#"@context": "http://schema.org",
#"@type": "DonateAction",
#"description": "Paypal",
#"name": "paypal",
#"price": "3GBP",
#"url": "https://paypal.me/petermolnar/3GBP",
#"recipient": author
#}
]
})
donateActions = [
{
"@context": "http://schema.org",
"@type": "DonateAction",
"description": "Monzo (only in the UK or via Google Pay)",
"name": "monzo",
"price": "3GBP",
"url": "https://monzo.me/petermolnar/3",
"recipient": author
},
{
"@context": "http://schema.org",
"@type": "DonateAction",
"description": "Paypal",
"name": "paypal",
"price": "3GBP",
"url": "https://paypal.me/petermolnar/3GBP",
"recipient": author
}
]
menu = {
'home': {
@ -170,6 +171,29 @@ photo = struct({
},
})
bye = """
"""
_parser = argparse.ArgumentParser(description='Parameters for NASG')
_booleanparams = {
'regenerate': 'force downsizing images',

View file

@ -10,6 +10,7 @@
<link rel="alternate" type="application/json" href="{{ post.url }}index.json" />
<link rel="alternate" type="application/ld+json" href="{{ post.url }}index.json" />
<link rel="alternate" type="application/mf2+json" href="https://pin13.net/mf2/?url={{ post.url|urlencode }}" />
<link rel="alternate" type="text/plain" href="{{ post.url }}index.txt" />
<meta property="og:title" content="{{ post.headline }}" />
<meta property="og:type" content="article" />
<meta property="og:url" content="{{ post.url }}" />
@ -17,11 +18,11 @@
<meta property="article:published_time" content="{{ post.datePublished }}" />
<meta property="article:modified_time" content="{{ post.dateModified }}" />
<meta property="article:author" content="{{ post.author.name }} ({{ post.author.email}})" />
{% if post.image.url is defined %}
<meta property="og:image" content="{{ post.image.url }}" />
<meta property="og:image:type" content="{{ post.image.encodingFormat }}" />
<meta property="og:image:width" content="{{ post.image.width }}" />
<meta property="og:image:height" content="{{ post.image.height }}" />
{% if post.image is iterable %}
<meta property="og:image" content="{{ post.image[0].url }}" />
<meta property="og:image:type" content="{{ post.image[0].encodingFormat }}" />
<meta property="og:image:width" content="{{ post.image[0].width }}" />
<meta property="og:image:height" content="{{ post.image[0].height }}" />
{% else %}
<meta property="og:image" content="{{ post.image }}" />
{% endif %}
@ -216,24 +217,6 @@
{% endfor %}
</section>
<section class="encourage">
<h2>Encourage creation!</h2>
<p>
If this entry helped you, or you simply liked it, leave a tip via <br />
{% set counters = {'donation': False} %}
{% for donate in post.potentialAction %}
{% if 'DonateAction' == donate['@type'] %}
{% if counters.donation %} or {% endif %}
<a href="{{ donate.url }}">
<svg width="16" height="16">
<use xlink:href="#icon-{{ donate.name }}"></use>
</svg> {{ donate.description }}</a>
{% if counters.update({'donation': True}) %} {% endif %}
{% endif %}
{% endfor %}
</p>
</section>
{% if post.comment|length %}
<section class="comments">
<h2><a id="comments"></a>Responses</h2>

10
templates/Singular.j2.txt Normal file
View file

@ -0,0 +1,10 @@
---
Title: {{ post.headline }}
Author: {{ post.author.name }} <{{ post.author.email}}>
URL: {{ post.url }}
Published: {{ post.datePublished|printdate }}
---
{{ summary }}
{{ content }}

View file

@ -288,19 +288,6 @@ li p {
margin: 0;
}
.encourage, .encourage a {
color: #090;
}
.encourage a {
color: #0a0;
font-weight: bold;
}
.encourage h2 {
border-color: #090;
}
.footnotes hr:before {
content: 'Links';
color: #ccc;

View file

@ -77,9 +77,6 @@
<symbol id="icon-star" viewBox="0 0 16 16">
<path d="M16 6.204l-5.528-0.803-2.472-5.009-2.472 5.009-5.528 0.803 4 3.899-0.944 5.505 4.944-2.599 4.944 2.599-0.944-5.505 4-3.899z"></path>
</symbol>
<symbol id="icon-reply" viewBox="0 0 16 16">
<path d="M7 12.119v3.881l-6-6 6-6v3.966c6.98 0.164 6.681-4.747 4.904-7.966 4.386 4.741 3.455 12.337-4.904 12.119z"></path>
</symbol>
<symbol id="button-indieweb" viewBox="0 0 80 15">
<rect width="80" height="15" fill="#666"/>
<rect x="1" y="1" width="78" height="13" fill="#fff"/>

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 22 KiB