- 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})$'
|
||||
)
|
||||
|
||||
|
||||
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
338
nasg.py
|
@ -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
249
pandoc.py
|
@ -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)
|
||||
|
|
66
settings.py
66
settings.py
|
@ -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',
|
||||
|
|
|
@ -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
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;
|
||||
}
|
||||
|
||||
.encourage, .encourage a {
|
||||
color: #090;
|
||||
}
|
||||
|
||||
.encourage a {
|
||||
color: #0a0;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.encourage h2 {
|
||||
border-color: #090;
|
||||
}
|
||||
|
||||
.footnotes hr:before {
|
||||
content: 'Links';
|
||||
color: #ccc;
|
||||
|
|
|
@ -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 |
Loading…
Reference in a new issue