- 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:
parent
67662b69e9
commit
9f73a9b111
10 changed files with 513 additions and 230 deletions
5
assets/icomoon/SVG/facebook.svg
Normal file
5
assets/icomoon/SVG/facebook.svg
Normal 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
27
assets/nojs-button.svg
Normal 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 |
3
meta.py
3
meta.py
|
@ -18,6 +18,7 @@ EXIFDATE = re.compile(
|
||||||
r'(?P<time>[0-9]{2}:[0-9]{2}:[0-9]{2})$'
|
r'(?P<time>[0-9]{2}:[0-9]{2}:[0-9]{2})$'
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class CachedMeta(dict):
|
class CachedMeta(dict):
|
||||||
def __init__(self, fpath):
|
def __init__(self, fpath):
|
||||||
self.fpath = fpath
|
self.fpath = fpath
|
||||||
|
@ -25,7 +26,7 @@ class CachedMeta(dict):
|
||||||
@property
|
@property
|
||||||
def cfile(self):
|
def cfile(self):
|
||||||
fname = os.path.basename(self.fpath)
|
fname = os.path.basename(self.fpath)
|
||||||
if fname == 'index.md':
|
if fname == 'index.md':
|
||||||
fname = os.path.basename(os.path.dirname(self.fpath))
|
fname = os.path.basename(os.path.dirname(self.fpath))
|
||||||
|
|
||||||
return os.path.join(
|
return os.path.join(
|
||||||
|
|
338
nasg.py
338
nasg.py
|
@ -21,6 +21,7 @@ from math import ceil
|
||||||
from urllib.parse import urlparse
|
from urllib.parse import urlparse
|
||||||
from collections import OrderedDict, namedtuple
|
from collections import OrderedDict, namedtuple
|
||||||
import logging
|
import logging
|
||||||
|
import csv
|
||||||
|
|
||||||
import arrow
|
import arrow
|
||||||
import langdetect
|
import langdetect
|
||||||
|
@ -31,8 +32,9 @@ import frontmatter
|
||||||
from feedgen.feed import FeedGenerator
|
from feedgen.feed import FeedGenerator
|
||||||
from slugify import slugify
|
from slugify import slugify
|
||||||
import requests
|
import requests
|
||||||
|
import lxml.etree as etree
|
||||||
|
|
||||||
from pandoc import PandocMarkdown
|
from pandoc import PandocMarkdown, PandocTXT
|
||||||
from meta import Exif
|
from meta import Exif
|
||||||
import settings
|
import settings
|
||||||
from settings import struct
|
from settings import struct
|
||||||
|
@ -40,6 +42,12 @@ import keys
|
||||||
|
|
||||||
logger = logging.getLogger('NASG')
|
logger = logging.getLogger('NASG')
|
||||||
|
|
||||||
|
CATEGORY = 'category'
|
||||||
|
MDFILE = 'index.md'
|
||||||
|
TXTFILE = 'index.txt'
|
||||||
|
HTMLFILE = 'index.html'
|
||||||
|
GOPHERFILE = 'gophermap'
|
||||||
|
|
||||||
MarkdownImage = namedtuple(
|
MarkdownImage = namedtuple(
|
||||||
'MarkdownImage',
|
'MarkdownImage',
|
||||||
['match', 'alt', 'fname', 'title', 'css']
|
['match', 'alt', 'fname', 'title', 'css']
|
||||||
|
@ -72,6 +80,7 @@ def mtime(path):
|
||||||
return int(os.path.getmtime(path))
|
return int(os.path.getmtime(path))
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
|
||||||
def utfyamldump(data):
|
def utfyamldump(data):
|
||||||
""" dump YAML with actual UTF-8 chars """
|
""" dump YAML with actual UTF-8 chars """
|
||||||
return yaml.dump(
|
return yaml.dump(
|
||||||
|
@ -81,6 +90,7 @@ def utfyamldump(data):
|
||||||
allow_unicode=True
|
allow_unicode=True
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def url2slug(url, limit=200):
|
def url2slug(url, limit=200):
|
||||||
""" convert URL to max 200 char ASCII string """
|
""" convert URL to max 200 char ASCII string """
|
||||||
return slugify(
|
return slugify(
|
||||||
|
@ -89,13 +99,16 @@ def url2slug(url, limit=200):
|
||||||
lower=True
|
lower=True
|
||||||
)[:limit]
|
)[:limit]
|
||||||
|
|
||||||
|
|
||||||
J2.filters['url2slug'] = url2slug
|
J2.filters['url2slug'] = url2slug
|
||||||
|
|
||||||
|
|
||||||
def rfc3339todt(rfc3339):
|
def rfc3339todt(rfc3339):
|
||||||
""" nice dates for humans """
|
""" nice dates for humans """
|
||||||
t = arrow.get(rfc3339).format('YYYY-MM-DD HH:mm ZZZ')
|
t = arrow.get(rfc3339).format('YYYY-MM-DD HH:mm ZZZ')
|
||||||
return "%s" % (t)
|
return "%s" % (t)
|
||||||
|
|
||||||
|
|
||||||
J2.filters['printdate'] = rfc3339todt
|
J2.filters['printdate'] = rfc3339todt
|
||||||
|
|
||||||
RE_MYURL = re.compile(
|
RE_MYURL = re.compile(
|
||||||
|
@ -105,6 +118,7 @@ RE_MYURL = re.compile(
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def relurl(text, baseurl=None):
|
def relurl(text, baseurl=None):
|
||||||
if not baseurl:
|
if not baseurl:
|
||||||
baseurl = settings.site.url
|
baseurl = settings.site.url
|
||||||
|
@ -118,15 +132,17 @@ def relurl(text, baseurl=None):
|
||||||
|
|
||||||
r = os.path.relpath(url, baseurl)
|
r = os.path.relpath(url, baseurl)
|
||||||
if url.endswith('/') and not r.endswith('/'):
|
if url.endswith('/') and not r.endswith('/'):
|
||||||
r = "%s/index.html" % r
|
r = "%s/%s" % (r, HTMLFILE)
|
||||||
if needsquotes:
|
if needsquotes:
|
||||||
r = '"%s"' % r
|
r = '"%s"' % r
|
||||||
logger.debug("RELURL: %s => %s (base: %s)", match, r, baseurl)
|
logger.debug("RELURL: %s => %s (base: %s)", match, r, baseurl)
|
||||||
text = text.replace(match, r)
|
text = text.replace(match, r)
|
||||||
return text
|
return text
|
||||||
|
|
||||||
|
|
||||||
J2.filters['relurl'] = relurl
|
J2.filters['relurl'] = relurl
|
||||||
|
|
||||||
|
|
||||||
def writepath(fpath, content, mtime=0):
|
def writepath(fpath, content, mtime=0):
|
||||||
""" f.write with extras """
|
""" f.write with extras """
|
||||||
d = os.path.dirname(fpath)
|
d = os.path.dirname(fpath)
|
||||||
|
@ -163,6 +179,7 @@ class cached_property(object):
|
||||||
|
|
||||||
class AQ:
|
class AQ:
|
||||||
""" Async queue which starts execution right on population """
|
""" Async queue which starts execution right on population """
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.loop = asyncio.get_event_loop()
|
self.loop = asyncio.get_event_loop()
|
||||||
self.queue = asyncio.Queue(loop=self.loop)
|
self.queue = asyncio.Queue(loop=self.loop)
|
||||||
|
@ -174,7 +191,7 @@ class AQ:
|
||||||
while not self.queue.empty():
|
while not self.queue.empty():
|
||||||
item = await self.queue.get()
|
item = await self.queue.get()
|
||||||
self.queue.task_done()
|
self.queue.task_done()
|
||||||
#asyncio.gather() ?
|
# asyncio.gather() ?
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
consumer = asyncio.ensure_future(self.consume())
|
consumer = asyncio.ensure_future(self.consume())
|
||||||
|
@ -183,6 +200,7 @@ class AQ:
|
||||||
|
|
||||||
class Webmention(object):
|
class Webmention(object):
|
||||||
""" outgoing webmention class """
|
""" outgoing webmention class """
|
||||||
|
|
||||||
def __init__(self, source, target, dpath, mtime=0):
|
def __init__(self, source, target, dpath, mtime=0):
|
||||||
self.source = source
|
self.source = source
|
||||||
self.target = target
|
self.target = target
|
||||||
|
@ -275,7 +293,7 @@ class MarkdownDoc(object):
|
||||||
|
|
||||||
def pandoc(self, c):
|
def pandoc(self, c):
|
||||||
if c and len(c):
|
if c and len(c):
|
||||||
c = PandocMarkdown(c)
|
c = str(PandocMarkdown(c))
|
||||||
c = RE_PRECODE.sub(
|
c = RE_PRECODE.sub(
|
||||||
'<pre><code lang="\g<1>" class="language-\g<1>">', c)
|
'<pre><code lang="\g<1>" class="language-\g<1>">', c)
|
||||||
return c
|
return c
|
||||||
|
@ -305,7 +323,9 @@ class Comment(MarkdownDoc):
|
||||||
@property
|
@property
|
||||||
def targetname(self):
|
def targetname(self):
|
||||||
t = urlparse(self.meta.get('target'))
|
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
|
@property
|
||||||
def source(self):
|
def source(self):
|
||||||
|
@ -335,11 +355,10 @@ class Comment(MarkdownDoc):
|
||||||
@property
|
@property
|
||||||
def type(self):
|
def type(self):
|
||||||
return self.meta.get('type', 'webmention')
|
return self.meta.get('type', 'webmention')
|
||||||
#if len(self.content):
|
# if len(self.content):
|
||||||
#maybe = clean(self.content, strip=True)
|
#maybe = clean(self.content, strip=True)
|
||||||
#if maybe in UNICODE_EMOJI:
|
# if maybe in UNICODE_EMOJI:
|
||||||
#return maybe
|
# return maybe
|
||||||
|
|
||||||
|
|
||||||
@cached_property
|
@cached_property
|
||||||
def jsonld(self):
|
def jsonld(self):
|
||||||
|
@ -418,10 +437,24 @@ class Singular(MarkdownDoc):
|
||||||
maybe = c.dt
|
maybe = c.dt
|
||||||
return maybe
|
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
|
@property
|
||||||
def sameas(self):
|
def sameas(self):
|
||||||
r = []
|
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:
|
with open(k, 'rt') as f:
|
||||||
r.append(f.read())
|
r.append(f.read())
|
||||||
return r
|
return r
|
||||||
|
@ -436,7 +469,7 @@ class Singular(MarkdownDoc):
|
||||||
files = [
|
files = [
|
||||||
k
|
k
|
||||||
for k in glob.glob(os.path.join(os.path.dirname(self.fpath), '*.md'))
|
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:
|
for f in files:
|
||||||
c = Comment(f)
|
c = Comment(f)
|
||||||
|
@ -463,9 +496,9 @@ class Singular(MarkdownDoc):
|
||||||
images.update({match: WebImage(imgpath, mdimg, self)})
|
images.update({match: WebImage(imgpath, mdimg, self)})
|
||||||
else:
|
else:
|
||||||
logger.error("Missing image: %s, referenced in %s",
|
logger.error("Missing image: %s, referenced in %s",
|
||||||
imgpath,
|
imgpath,
|
||||||
self.fpath
|
self.fpath
|
||||||
)
|
)
|
||||||
return images
|
return images
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
@ -493,7 +526,7 @@ class Singular(MarkdownDoc):
|
||||||
if len(self.images) != 1:
|
if len(self.images) != 1:
|
||||||
return False
|
return False
|
||||||
photo = next(iter(self.images.values()))
|
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:
|
if photo.fpath == maybe:
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
@ -630,46 +663,46 @@ class Singular(MarkdownDoc):
|
||||||
else:
|
else:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
#@cached_property
|
@cached_property
|
||||||
#def oembed_xml(self):
|
def oembed_xml(self):
|
||||||
#oembed = etree.Element("oembed", version="1.0")
|
oembed = etree.Element("oembed", version="1.0")
|
||||||
#xmldoc = etree.ElementTree(oembed)
|
xmldoc = etree.ElementTree(oembed)
|
||||||
#for k, v in self.oembed_json.items():
|
for k, v in self.oembed_json.items():
|
||||||
#x = etree.SubElement(oembed, k).text = "%s" % (v)
|
x = etree.SubElement(oembed, k).text = "%s" % (v)
|
||||||
#s = etree.tostring(
|
s = etree.tostring(
|
||||||
#xmldoc,
|
xmldoc,
|
||||||
#encoding='utf-8',
|
encoding='utf-8',
|
||||||
#xml_declaration=True,
|
xml_declaration=True,
|
||||||
#pretty_print=True
|
pretty_print=True
|
||||||
#)
|
)
|
||||||
#return s
|
return s
|
||||||
|
|
||||||
#@cached_property
|
@cached_property
|
||||||
#def oembed_json(self):
|
def oembed_json(self):
|
||||||
#r = {
|
r = {
|
||||||
#"version": "1.0",
|
"version": "1.0",
|
||||||
#"provider_name": settings.site.name,
|
"provider_name": settings.site.name,
|
||||||
#"provider_url": settings.site.url,
|
"provider_url": settings.site.url,
|
||||||
#"author_name": settings.author.name,
|
"author_name": settings.author.name,
|
||||||
#"author_url": settings.author.url,
|
"author_url": settings.author.url,
|
||||||
#"title": self.title,
|
"title": self.title,
|
||||||
#"type": "link",
|
"type": "link",
|
||||||
#"html": self.html_content,
|
"html": self.html_content,
|
||||||
#}
|
}
|
||||||
|
|
||||||
#img = None
|
img = None
|
||||||
#if self.is_photo:
|
if self.is_photo:
|
||||||
#img = self.photo
|
img = self.photo
|
||||||
#elif not self.is_photo and len(self.images):
|
elif not self.is_photo and len(self.images):
|
||||||
#img = list(self.images.values())[0]
|
img = list(self.images.values())[0]
|
||||||
#if img:
|
if img:
|
||||||
#r.update({
|
r.update({
|
||||||
#"type": "rich",
|
"type": "rich",
|
||||||
#"thumbnail_url": img.jsonld.thumbnail.url,
|
"thumbnail_url": img.jsonld.thumbnail.url,
|
||||||
#"thumbnail_width": img.jsonld.thumbnail.width,
|
"thumbnail_width": img.jsonld.thumbnail.width,
|
||||||
#"thumbnail_height": img.jsonld.thumbnail.height
|
"thumbnail_height": img.jsonld.thumbnail.height
|
||||||
#})
|
})
|
||||||
#return r
|
return r
|
||||||
|
|
||||||
@cached_property
|
@cached_property
|
||||||
def review(self):
|
def review(self):
|
||||||
|
@ -744,7 +777,7 @@ class Singular(MarkdownDoc):
|
||||||
if self.is_photo:
|
if self.is_photo:
|
||||||
r.update({
|
r.update({
|
||||||
"@type": "Photograph",
|
"@type": "Photograph",
|
||||||
"image": self.photo.jsonld,
|
#"image": self.photo.jsonld,
|
||||||
})
|
})
|
||||||
elif self.has_code:
|
elif self.has_code:
|
||||||
r.update({
|
r.update({
|
||||||
|
@ -754,11 +787,15 @@ class Singular(MarkdownDoc):
|
||||||
r.update({
|
r.update({
|
||||||
"@type": "WebPage",
|
"@type": "WebPage",
|
||||||
})
|
})
|
||||||
if not self.is_photo and len(self.images):
|
if len(self.images):
|
||||||
img = list(self.images.values())[0]
|
r["image"] = []
|
||||||
r.update({
|
for img in list(self.images.values()):
|
||||||
"image": img.jsonld,
|
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:
|
if self.is_reply:
|
||||||
r.update({
|
r.update({
|
||||||
|
@ -775,8 +812,8 @@ class Singular(MarkdownDoc):
|
||||||
if self.event:
|
if self.event:
|
||||||
r.update({"subjectOf": self.event})
|
r.update({"subjectOf": self.event})
|
||||||
|
|
||||||
for donation in settings.donateActions:
|
#for donation in settings.donateActions:
|
||||||
r["potentialAction"].append(donation)
|
#r["potentialAction"].append(donation)
|
||||||
|
|
||||||
for url in list(set(self.syndicate)):
|
for url in list(set(self.syndicate)):
|
||||||
r["potentialAction"].append({
|
r["potentialAction"].append({
|
||||||
|
@ -794,16 +831,29 @@ class Singular(MarkdownDoc):
|
||||||
def template(self):
|
def template(self):
|
||||||
return "%s.j2.html" % (self.__class__.__name__)
|
return "%s.j2.html" % (self.__class__.__name__)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def gophertemplate(self):
|
||||||
|
return "%s.j2.txt" % (self.__class__.__name__)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def renderdir(self):
|
def renderdir(self):
|
||||||
return os.path.dirname(self.renderfile)
|
return os.path.join(
|
||||||
|
settings.paths.get('build'),
|
||||||
|
self.name
|
||||||
|
)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def renderfile(self):
|
def renderfile(self):
|
||||||
return os.path.join(
|
return os.path.join(
|
||||||
settings.paths.get('build'),
|
self.renderdir,
|
||||||
self.name,
|
HTMLFILE
|
||||||
'index.html'
|
)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def gopherfile(self):
|
||||||
|
return os.path.join(
|
||||||
|
self.renderdir,
|
||||||
|
TXTFILE
|
||||||
)
|
)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
@ -831,7 +881,15 @@ class Singular(MarkdownDoc):
|
||||||
])
|
])
|
||||||
|
|
||||||
async def copyfiles(self):
|
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(
|
files = glob.glob(
|
||||||
os.path.join(
|
os.path.join(
|
||||||
os.path.dirname(self.fpath),
|
os.path.dirname(self.fpath),
|
||||||
|
@ -854,39 +912,41 @@ class Singular(MarkdownDoc):
|
||||||
logger.info("copying '%s' to '%s'", f, t)
|
logger.info("copying '%s' to '%s'", f, t)
|
||||||
cp(f, t)
|
cp(f, t)
|
||||||
|
|
||||||
@cached_property
|
async def render(self):
|
||||||
def html(self):
|
if self.exists:
|
||||||
r = J2.get_template(self.template).render({
|
return
|
||||||
|
logger.info("rendering %s", self.name)
|
||||||
|
v = {
|
||||||
'baseurl': self.url,
|
'baseurl': self.url,
|
||||||
'post': self.jsonld,
|
'post': self.jsonld,
|
||||||
'site': settings.site,
|
'site': settings.site,
|
||||||
'menu': settings.menu,
|
'menu': settings.menu,
|
||||||
'meta': settings.meta,
|
'meta': settings.meta,
|
||||||
})
|
}
|
||||||
return r
|
|
||||||
|
|
||||||
async def render(self):
|
|
||||||
if self.exists:
|
|
||||||
return
|
|
||||||
logger.info("rendering %s", self.name)
|
|
||||||
writepath(
|
writepath(
|
||||||
self.renderfile,
|
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 = settings.site.copy()
|
||||||
j.update({
|
j.update({
|
||||||
"mainEntity": self.jsonld
|
"mainEntity": self.jsonld
|
||||||
})
|
})
|
||||||
writepath(
|
writepath(
|
||||||
os.path.join(self.renderdir,'index.json'),
|
os.path.join(self.renderdir, 'index.json'),
|
||||||
json.dumps(j, indent=4, ensure_ascii=False)
|
json.dumps(j, indent=4, ensure_ascii=False)
|
||||||
)
|
)
|
||||||
del(j)
|
del(j)
|
||||||
cp(
|
|
||||||
self.fpath,
|
|
||||||
os.path.join(self.renderdir,'index.md')
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class Home(Singular):
|
class Home(Singular):
|
||||||
def __init__(self, fpath):
|
def __init__(self, fpath):
|
||||||
|
@ -904,7 +964,7 @@ class Home(Singular):
|
||||||
def renderfile(self):
|
def renderfile(self):
|
||||||
return os.path.join(
|
return os.path.join(
|
||||||
settings.paths.get('build'),
|
settings.paths.get('build'),
|
||||||
'index.html'
|
HTMLFILE
|
||||||
)
|
)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
@ -916,6 +976,27 @@ class Home(Singular):
|
||||||
maybe = pts
|
maybe = pts
|
||||||
return maybe
|
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):
|
async def render(self):
|
||||||
if self.exists:
|
if self.exists:
|
||||||
return
|
return
|
||||||
|
@ -929,6 +1010,7 @@ class Home(Singular):
|
||||||
'posts': self.posts
|
'posts': self.posts
|
||||||
})
|
})
|
||||||
writepath(self.renderfile, r)
|
writepath(self.renderfile, r)
|
||||||
|
await self.render_gopher()
|
||||||
|
|
||||||
|
|
||||||
class WebImage(object):
|
class WebImage(object):
|
||||||
|
@ -1111,7 +1193,7 @@ class WebImage(object):
|
||||||
'Model': ['Model'],
|
'Model': ['Model'],
|
||||||
'FNumber': ['FNumber', 'Aperture'],
|
'FNumber': ['FNumber', 'Aperture'],
|
||||||
'ExposureTime': ['ExposureTime'],
|
'ExposureTime': ['ExposureTime'],
|
||||||
'FocalLength': ['FocalLength'], # ['FocalLengthIn35mmFormat'],
|
'FocalLength': ['FocalLength'], # ['FocalLengthIn35mmFormat'],
|
||||||
'ISO': ['ISO'],
|
'ISO': ['ISO'],
|
||||||
'LensID': ['LensID', 'LensSpec', 'Lens'],
|
'LensID': ['LensID', 'LensSpec', 'Lens'],
|
||||||
'CreateDate': ['CreateDate', 'DateTimeOriginal']
|
'CreateDate': ['CreateDate', 'DateTimeOriginal']
|
||||||
|
@ -1189,7 +1271,8 @@ class WebImage(object):
|
||||||
def data(self):
|
def data(self):
|
||||||
with open(self.fpath, 'rb') as f:
|
with open(self.fpath, 'rb') as f:
|
||||||
encoded = base64.b64encode(f.read())
|
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
|
@property
|
||||||
def suffix(self):
|
def suffix(self):
|
||||||
|
@ -1328,8 +1411,8 @@ class PHPFile(object):
|
||||||
raise ValueError('Not implemented')
|
raise ValueError('Not implemented')
|
||||||
|
|
||||||
async def render(self):
|
async def render(self):
|
||||||
#if self.exists:
|
# if self.exists:
|
||||||
#return
|
# return
|
||||||
await self._render()
|
await self._render()
|
||||||
|
|
||||||
|
|
||||||
|
@ -1359,7 +1442,7 @@ class Search(PHPFile):
|
||||||
notindexed=mtime,
|
notindexed=mtime,
|
||||||
tokenize=porter
|
tokenize=porter
|
||||||
)'''
|
)'''
|
||||||
)
|
)
|
||||||
self.is_changed = False
|
self.is_changed = False
|
||||||
|
|
||||||
def __exit__(self):
|
def __exit__(self):
|
||||||
|
@ -1548,7 +1631,7 @@ class Category(dict):
|
||||||
@property
|
@property
|
||||||
def url(self):
|
def url(self):
|
||||||
if len(self.name):
|
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:
|
else:
|
||||||
url = '%s/' % (settings.site.get('url'))
|
url = '%s/' % (settings.site.get('url'))
|
||||||
return url
|
return url
|
||||||
|
@ -1566,7 +1649,7 @@ class Category(dict):
|
||||||
if len(self.name):
|
if len(self.name):
|
||||||
return os.path.join(
|
return os.path.join(
|
||||||
settings.paths.get('build'),
|
settings.paths.get('build'),
|
||||||
'category',
|
CATEGORY,
|
||||||
self.name
|
self.name
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
|
@ -1665,17 +1748,17 @@ class Category(dict):
|
||||||
'posts': posts,
|
'posts': posts,
|
||||||
}
|
}
|
||||||
|
|
||||||
def indexfpath(self, subpath=None):
|
def indexfpath(self, subpath=None, fname=HTMLFILE):
|
||||||
if subpath:
|
if subpath:
|
||||||
return os.path.join(
|
return os.path.join(
|
||||||
self.dpath,
|
self.dpath,
|
||||||
subpath,
|
subpath,
|
||||||
'index.html'
|
fname
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
return os.path.join(
|
return os.path.join(
|
||||||
self.dpath,
|
self.dpath,
|
||||||
'index.html'
|
fname
|
||||||
)
|
)
|
||||||
|
|
||||||
async def render_feed(self, xmlformat):
|
async def render_feed(self, xmlformat):
|
||||||
|
@ -1711,8 +1794,9 @@ class Category(dict):
|
||||||
fe.category({
|
fe.category({
|
||||||
'term': post.category,
|
'term': post.category,
|
||||||
'label': post.category,
|
'label': post.category,
|
||||||
'scheme': "%s/category/%s/" % (
|
'scheme': "%s/%s/%s/" % (
|
||||||
settings.site.get('url'),
|
settings.site.get('url'),
|
||||||
|
CATEGORY,
|
||||||
post.category
|
post.category
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
@ -1758,6 +1842,32 @@ class Category(dict):
|
||||||
)
|
)
|
||||||
writepath(self.indexfpath(), r)
|
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):
|
async def render_archives(self):
|
||||||
for year in self.years.keys():
|
for year in self.years.keys():
|
||||||
if year == self.newest_year:
|
if year == self.newest_year:
|
||||||
|
@ -1779,7 +1889,7 @@ class Category(dict):
|
||||||
end = index
|
end = index
|
||||||
|
|
||||||
if self.is_uptodate(fpath, self[self.sortedkeys[start]].dt):
|
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:
|
else:
|
||||||
logger.info("updating %s / %d", self.name, year)
|
logger.info("updating %s / %d", self.name, year)
|
||||||
logger.info("getting posts from %d to %d", start, end)
|
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
|
# I don't know why end needs the +1, but without that
|
||||||
# some posts disappear
|
# some posts disappear
|
||||||
# TODO figure this out...
|
# TODO figure this out...
|
||||||
self.get_posts(start, end+1),
|
self.get_posts(start, end + 1),
|
||||||
tyear
|
tyear
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
@ -1819,9 +1929,10 @@ class Category(dict):
|
||||||
self.name
|
self.name
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
async def render(self):
|
async def render(self):
|
||||||
await self.render_feeds()
|
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_paginated:
|
||||||
if not self.is_uptodate(self.indexfpath(), self.newest()):
|
if not self.is_uptodate(self.indexfpath(), self.newest()):
|
||||||
logger.info(
|
logger.info(
|
||||||
|
@ -1829,6 +1940,7 @@ class Category(dict):
|
||||||
self.name
|
self.name
|
||||||
)
|
)
|
||||||
await self.render_flat()
|
await self.render_flat()
|
||||||
|
|
||||||
else:
|
else:
|
||||||
logger.info(
|
logger.info(
|
||||||
'%s flat index is up to date',
|
'%s flat index is up to date',
|
||||||
|
@ -1839,6 +1951,7 @@ class Category(dict):
|
||||||
await self.render_archives()
|
await self.render_archives()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class Sitemap(dict):
|
class Sitemap(dict):
|
||||||
@property
|
@property
|
||||||
def mtime(self):
|
def mtime(self):
|
||||||
|
@ -1875,7 +1988,7 @@ class WebmentionIO(object):
|
||||||
newest = 0
|
newest = 0
|
||||||
content = settings.paths.get('content')
|
content = settings.paths.get('content')
|
||||||
for e in glob.glob(os.path.join(content, '*', '*', '*.md')):
|
for e in glob.glob(os.path.join(content, '*', '*', '*.md')):
|
||||||
if os.path.basename(e) == 'index.md':
|
if os.path.basename(e) == MDFILE:
|
||||||
continue
|
continue
|
||||||
# filenames are like [received epoch]-[slugified source url].md
|
# filenames are like [received epoch]-[slugified source url].md
|
||||||
try:
|
try:
|
||||||
|
@ -1889,7 +2002,7 @@ class WebmentionIO(object):
|
||||||
continue
|
continue
|
||||||
if mtime > newest:
|
if mtime > newest:
|
||||||
newest = mtime
|
newest = mtime
|
||||||
return arrow.get(newest+1)
|
return arrow.get(newest + 1)
|
||||||
|
|
||||||
def makecomment(self, webmention):
|
def makecomment(self, webmention):
|
||||||
if 'published_ts' in webmention.get('data'):
|
if 'published_ts' in webmention.get('data'):
|
||||||
|
@ -1899,12 +2012,19 @@ class WebmentionIO(object):
|
||||||
else:
|
else:
|
||||||
dt = arrow.get(webmention.get('data').get('published'))
|
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
|
# ignore selfpings
|
||||||
if slug == settings.site.get('name'):
|
if slug == settings.site.get('name'):
|
||||||
return
|
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):
|
if not len(fdir):
|
||||||
logger.error(
|
logger.error(
|
||||||
"couldn't find post for incoming webmention: %s",
|
"couldn't find post for incoming webmention: %s",
|
||||||
|
@ -1962,6 +2082,7 @@ class WebmentionIO(object):
|
||||||
logger.error('failed to query webmention.io: %s', e)
|
logger.error('failed to query webmention.io: %s', e)
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
def make():
|
def make():
|
||||||
start = int(round(time.time() * 1000))
|
start = int(round(time.time() * 1000))
|
||||||
last = 0
|
last = 0
|
||||||
|
@ -1989,7 +2110,7 @@ def make():
|
||||||
frontposts = Category()
|
frontposts = Category()
|
||||||
home = Home(settings.paths.get('home'))
|
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)
|
post = Singular(e)
|
||||||
# deal with images, if needed
|
# deal with images, if needed
|
||||||
for i in post.images.values():
|
for i in post.images.values():
|
||||||
|
@ -2060,11 +2181,18 @@ def make():
|
||||||
for e in glob.glob(os.path.join(content, '*.*')):
|
for e in glob.glob(os.path.join(content, '*.*')):
|
||||||
if e.endswith('.md'):
|
if e.endswith('.md'):
|
||||||
continue
|
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):
|
if os.path.exists(t) and mtime(e) <= mtime(t):
|
||||||
continue
|
continue
|
||||||
cp(e, t)
|
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))
|
end = int(round(time.time() * 1000))
|
||||||
logger.info('process took %d ms' % (end - start))
|
logger.info('process took %d ms' % (end - start))
|
||||||
|
|
||||||
|
|
249
pandoc.py
249
pandoc.py
|
@ -7,34 +7,43 @@ __email__ = "mail@petermolnar.net"
|
||||||
import subprocess
|
import subprocess
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
class PandocMarkdown(str):
|
|
||||||
def __new__(cls, text):
|
class PandocBase(str):
|
||||||
""" Pandoc command line call with piped in- and output """
|
in_format = 'html'
|
||||||
cmd = (
|
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',
|
'pandoc',
|
||||||
'-o-',
|
'-o-',
|
||||||
'--from=markdown+%s' % (
|
conv_to,
|
||||||
'+'.join([
|
conv_from,
|
||||||
'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',
|
'--quiet',
|
||||||
'--no-highlight'
|
'--no-highlight'
|
||||||
)
|
]
|
||||||
|
if self.columns:
|
||||||
|
cmd.append(self.columns)
|
||||||
|
|
||||||
p = subprocess.Popen(
|
p = subprocess.Popen(
|
||||||
cmd,
|
tuple(cmd),
|
||||||
stdin=subprocess.PIPE,
|
stdin=subprocess.PIPE,
|
||||||
stdout=subprocess.PIPE,
|
stdout=subprocess.PIPE,
|
||||||
stderr=subprocess.PIPE,
|
stderr=subprocess.PIPE,
|
||||||
|
@ -48,46 +57,158 @@ class PandocMarkdown(str):
|
||||||
stderr
|
stderr
|
||||||
)
|
)
|
||||||
r = stdout.decode('utf-8').strip()
|
r = stdout.decode('utf-8').strip()
|
||||||
return str.__new__(cls, r)
|
self.result = r
|
||||||
|
|
||||||
class PandocHTML(str):
|
def __str__(self):
|
||||||
def __new__(cls, text):
|
return str(self.result)
|
||||||
""" 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())
|
def __repr__(self):
|
||||||
if stderr:
|
return str(self.result)
|
||||||
logging.warning(
|
|
||||||
"Error during pandoc covert:\n\t%s\n\t%s",
|
|
||||||
cmd,
|
class PandocMarkdown(PandocBase):
|
||||||
stderr
|
in_format = 'markdown'
|
||||||
)
|
in_options = [
|
||||||
r = stdout.decode('utf-8').strip()
|
'footnotes',
|
||||||
return str.__new__(cls, r)
|
'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)
|
||||||
|
|
66
settings.py
66
settings.py
|
@ -47,6 +47,9 @@ site = struct({
|
||||||
"name": "petermolnar.net",
|
"name": "petermolnar.net",
|
||||||
"image": "https://petermolnar.net/favicon.ico",
|
"image": "https://petermolnar.net/favicon.ico",
|
||||||
"license": "https://spdx.org/licenses/%s.html" % (licence['_default']),
|
"license": "https://spdx.org/licenses/%s.html" % (licence['_default']),
|
||||||
|
#"sameAs": [
|
||||||
|
#"dat://8d03735af11d82fff82028e0f830f9ac470f5e9fbe10ab5eb6feb877232714a2"
|
||||||
|
#],
|
||||||
"author": {
|
"author": {
|
||||||
"@context": "http://schema.org",
|
"@context": "http://schema.org",
|
||||||
"@type": "Person",
|
"@type": "Person",
|
||||||
|
@ -88,30 +91,28 @@ site = struct({
|
||||||
"@type": "FollowAction",
|
"@type": "FollowAction",
|
||||||
"url": "https://petermolnar.net/follow/",
|
"url": "https://petermolnar.net/follow/",
|
||||||
"name": "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 = {
|
menu = {
|
||||||
'home': {
|
'home': {
|
||||||
|
@ -170,6 +171,29 @@ photo = struct({
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
bye = """
|
||||||
|
███████╗███████╗███████╗ ██╗ ██╗ ██████╗ ██╗ ██╗
|
||||||
|
██╔════╝██╔════╝██╔════╝ ╚██╗ ██╔╝██╔═══██╗██║ ██║
|
||||||
|
███████╗█████╗ █████╗ ╚████╔╝ ██║ ██║██║ ██║
|
||||||
|
╚════██║██╔══╝ ██╔══╝ ╚██╔╝ ██║ ██║██║ ██║
|
||||||
|
███████║███████╗███████╗ ██║ ╚██████╔╝╚██████╔╝
|
||||||
|
╚══════╝╚══════╝╚══════╝ ╚═╝ ╚═════╝ ╚═════╝
|
||||||
|
|
||||||
|
███████╗██████╗ █████╗ ██████╗███████╗
|
||||||
|
██╔════╝██╔══██╗██╔══██╗██╔════╝██╔════╝
|
||||||
|
███████╗██████╔╝███████║██║ █████╗
|
||||||
|
╚════██║██╔═══╝ ██╔══██║██║ ██╔══╝
|
||||||
|
███████║██║ ██║ ██║╚██████╗███████╗
|
||||||
|
╚══════╝╚═╝ ╚═╝ ╚═╝ ╚═════╝╚══════╝
|
||||||
|
|
||||||
|
██████╗ ██████╗ ██╗ ██╗██████╗ ██████╗ ██╗ ██╗
|
||||||
|
██╔════╝██╔═══██╗██║ ██║██╔══██╗██╔═══██╗╚██╗ ██╔╝
|
||||||
|
██║ ██║ ██║██║ █╗ ██║██████╔╝██║ ██║ ╚████╔╝
|
||||||
|
██║ ██║ ██║██║███╗██║██╔══██╗██║ ██║ ╚██╔╝
|
||||||
|
╚██████╗╚██████╔╝╚███╔███╔╝██████╔╝╚██████╔╝ ██║
|
||||||
|
╚═════╝ ╚═════╝ ╚══╝╚══╝ ╚═════╝ ╚═════╝ ╚═╝
|
||||||
|
"""
|
||||||
|
|
||||||
_parser = argparse.ArgumentParser(description='Parameters for NASG')
|
_parser = argparse.ArgumentParser(description='Parameters for NASG')
|
||||||
_booleanparams = {
|
_booleanparams = {
|
||||||
'regenerate': 'force downsizing images',
|
'regenerate': 'force downsizing images',
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
<link rel="alternate" type="application/json" href="{{ post.url }}index.json" />
|
<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/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="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:title" content="{{ post.headline }}" />
|
||||||
<meta property="og:type" content="article" />
|
<meta property="og:type" content="article" />
|
||||||
<meta property="og:url" content="{{ post.url }}" />
|
<meta property="og:url" content="{{ post.url }}" />
|
||||||
|
@ -17,11 +18,11 @@
|
||||||
<meta property="article:published_time" content="{{ post.datePublished }}" />
|
<meta property="article:published_time" content="{{ post.datePublished }}" />
|
||||||
<meta property="article:modified_time" content="{{ post.dateModified }}" />
|
<meta property="article:modified_time" content="{{ post.dateModified }}" />
|
||||||
<meta property="article:author" content="{{ post.author.name }} ({{ post.author.email}})" />
|
<meta property="article:author" content="{{ post.author.name }} ({{ post.author.email}})" />
|
||||||
{% if post.image.url is defined %}
|
{% if post.image is iterable %}
|
||||||
<meta property="og:image" content="{{ post.image.url }}" />
|
<meta property="og:image" content="{{ post.image[0].url }}" />
|
||||||
<meta property="og:image:type" content="{{ post.image.encodingFormat }}" />
|
<meta property="og:image:type" content="{{ post.image[0].encodingFormat }}" />
|
||||||
<meta property="og:image:width" content="{{ post.image.width }}" />
|
<meta property="og:image:width" content="{{ post.image[0].width }}" />
|
||||||
<meta property="og:image:height" content="{{ post.image.height }}" />
|
<meta property="og:image:height" content="{{ post.image[0].height }}" />
|
||||||
{% else %}
|
{% else %}
|
||||||
<meta property="og:image" content="{{ post.image }}" />
|
<meta property="og:image" content="{{ post.image }}" />
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
@ -216,24 +217,6 @@
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</section>
|
</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 %}
|
{% if post.comment|length %}
|
||||||
<section class="comments">
|
<section class="comments">
|
||||||
<h2><a id="comments"></a>Responses</h2>
|
<h2><a id="comments"></a>Responses</h2>
|
||||||
|
|
10
templates/Singular.j2.txt
Normal file
10
templates/Singular.j2.txt
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
---
|
||||||
|
Title: {{ post.headline }}
|
||||||
|
Author: {{ post.author.name }} <{{ post.author.email}}>
|
||||||
|
URL: {{ post.url }}
|
||||||
|
Published: {{ post.datePublished|printdate }}
|
||||||
|
---
|
||||||
|
|
||||||
|
{{ summary }}
|
||||||
|
|
||||||
|
{{ content }}
|
|
@ -288,19 +288,6 @@ li p {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.encourage, .encourage a {
|
|
||||||
color: #090;
|
|
||||||
}
|
|
||||||
|
|
||||||
.encourage a {
|
|
||||||
color: #0a0;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
.encourage h2 {
|
|
||||||
border-color: #090;
|
|
||||||
}
|
|
||||||
|
|
||||||
.footnotes hr:before {
|
.footnotes hr:before {
|
||||||
content: 'Links';
|
content: 'Links';
|
||||||
color: #ccc;
|
color: #ccc;
|
||||||
|
|
|
@ -77,9 +77,6 @@
|
||||||
<symbol id="icon-star" viewBox="0 0 16 16">
|
<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>
|
<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>
|
||||||
<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">
|
<symbol id="button-indieweb" viewBox="0 0 80 15">
|
||||||
<rect width="80" height="15" fill="#666"/>
|
<rect width="80" height="15" fill="#666"/>
|
||||||
<rect x="1" y="1" width="78" height="13" fill="#fff"/>
|
<rect x="1" y="1" width="78" height="13" fill="#fff"/>
|
||||||
|
|
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 22 KiB |
Loading…
Reference in a new issue