- no more microdata or RDFa in HTML: went full JSON-LD - added og: and article: from open graph to meta - jsonld as tmplvars for most elements - removed unused commented code - indention for inline CSS for readability - merged reactions and comments into a single block
@@ -18,18 +18,18 @@ },
"default": { "arrow": { "hashes": [ - "sha256:a558d3b7b6ce7ffc74206a86c147052de23d3d4ef0e17c210dd478c53575c4cd" + "sha256:9cb4a910256ed536751cd5728673bfb53e6f0026e240466f90c2a92c0b79c895" ], "index": "pypi", - "version": "==0.12.1" + "version": "==0.13.0" }, "bleach": { "hashes": [ - "sha256:48d39675b80a75f6d1c3bdbffec791cf0bbbab665cf01e20da701c77de278718", - "sha256:73d26f018af5d5adcdabf5c1c974add4361a9c76af215fe32fdec8a6fc5fb9b9" + "sha256:213336e49e102af26d9cde77dd2d0397afabc5a6bf2fed985dc35b5d1e285a16", + "sha256:3fdf7f77adcf649c9911387df51254b813185e32b2c6619f690b593a617e19fa" ], "index": "pypi", - "version": "==3.0.2" + "version": "==3.1.0" }, "certifi": { "hashes": [@@ -148,18 +148,18 @@ "version": "==1.1.0"
}, "python-dateutil": { "hashes": [ - "sha256:063df5763652e21de43de7d9e00ccf239f953a832941e37be541614732cdfc93", - "sha256:88f9287c0174266bb0d8cedd395cfba9c58e87e5ad86b2ce58859bc11be3cf02" + "sha256:7e6584c74aeed623791615e26efd690f29817a27c73085b78e4bad02493df2fb", + "sha256:c89805f6f4d64db21ed966fda138f8a5ed7a4fdbc1a8ee329ce1b74e3c74da9e" ], - "version": "==2.7.5" + "version": "==2.8.0" }, "python-frontmatter": { "hashes": [ - "sha256:222203a9f56469c564b97b2d2607fde898b8546802d8361e729f087490761eff", - "sha256:94fc9ca449eddaae281455f2ba15662150c81d1e69c2f08a77ad11a5f38dfa1d" + "sha256:13a61749910f2e1968c011103406429abc79c41d52b69adfb07f702ae2df32cc", + "sha256:cec75b2afd1a06cf5b03cfd9c5173365d3dc14379526389a9742ff35846df771" ], "index": "pypi", - "version": "==0.4.4" + "version": "==0.4.5" }, "pyyaml": { "hashes": [
@@ -15,6 +15,7 @@ import asyncio
import sqlite3 import json import queue +from copy import deepcopy from shutil import copy2 as cp from math import ceil from urllib.parse import urlparse@@ -68,24 +69,6 @@ RE_PRECODE = re.compile(
r'<pre class="([^"]+)"><code>' ) -RE_MYURL= re.compile( - r'"?(?P<url>%s/?[^\"]+)"?' % (settings.site.get('url')) -) - -def relurl(txt, baseurl): - for url in RE_MYURL.findall(txt): - logger.debug('found URL candidate %s', url) - logger.debug('baseurl is %s', baseurl) - out = os.path.relpath(url, baseurl) - logger.debug('result is %s', out) - if not re.match(r'.*\.[a-z]{2,4}', out): - out = "%s/index.html" % out - #logger.debug('replacing %s with %s', url, out) - txt = txt.replace(url, out) - return txt - -J2.filters['relurl'] = relurl - def utfyamldump(data): return yaml.dump( data,@@ -101,6 +84,12 @@ only_ascii=True,
lower=True )[:limit] +def rfc3339todt(rfc3339): + t = arrow.get(rfc3339).to('utc').format('YYYY-MM-DD HH:mm') + return "%s UTC" % (t) + +J2.filters['printdate'] = rfc3339todt +J2.filters['url2slug'] = url2slug def writepath(fpath, content, mtime=0): d = os.path.dirname(fpath)@@ -155,11 +144,12 @@ def run(self):
consumer = asyncio.ensure_future(self.consume()) self.loop.run_until_complete(consumer) + class Webmention(object): def __init__(self, source, target, dpath, mtime=0): - self.dpath = dpath self.source = source self.target = target + self.dpath = dpath if not mtime: mtime = arrow.utcnow().timestamp self.mtime = mtime@@ -207,14 +197,6 @@ self.save(r.text)
class MarkdownDoc(object): - - @property - def regex(self): - return re.compile( - r'^---\s?[\r\n](?P<meta>.+?)[\r\n]---(?:\s?[\r\n](?P<content>.+))?', - flags=re.MULTILINE|re.DOTALL - ) - @property def mtime(self): return os.path.getmtime(self.fpath)@@ -245,20 +227,6 @@ logger.debug('parsing YAML+MD file %s', self.fpath)
meta, txt = frontmatter.parse(f.read()) return(meta, txt) - @cached_property - def _reparsed(self): - logger.debug('parsing file %s', self.fpath) - with open(self.fpath, mode='r') as f: - txt = f.read() - txt = self.mdregex.match(txt) - if not txt: - logger.error('failed to match YAML + MD doc: %s', self.fpath) - if txt.group('content'): - t = txt.group('content').strip() - else: - t = '' - return (yaml.safe_load(txt.group('meta')), t) - @property def meta(self): return self._parsed[0]@@ -280,14 +248,6 @@ c = "%s" % (self.content)
if hasattr(self, 'images') and len(self.images): for match, img in self.images.items(): c = c.replace(match, str(img)) - return self.__pandoc(c) - - @cached_property - def html_content_noimg(self): - c = "%s" % (self.content) - if hasattr(self, 'images') and len(self.images): - for match, img in self.images.items(): - c = c.replace(match, '') return self.__pandoc(c)@@ -340,16 +300,23 @@ if maybe in UNICODE_EMOJI:
return maybe return self.meta.get('type', 'webmention') - @property - def tmplvars(self): - return { - 'author': self.author, - 'source': self.source, - 'pubtime': self.dt.format(settings.dateformat.get('iso')), - 'pubdate': self.dt.format(settings.dateformat.get('display')), - 'html': self.html_content, - 'type': self.type + @cached_property + def jsonld(self): + r = { + "@context": "http://schema.org", + "@type": "Comment", + "author": { + "@context": "http://schema.org", + "@type": "Person", + "url": self.author.get('url'), + "name": self.author.get('name'), + }, + "url": self.source, + "discussionUrl": self.meta.get('target'), + "datePublished": str(self.dt), + "disambiguatingDescription": self.type } + return r class Gone(object):@@ -402,12 +369,7 @@ """
return [ k for k in glob.glob(os.path.join(os.path.dirname(self.fpath), '*.*')) - if - not k.startswith('.') - and not k.endswith('.md') - and not k.endswith('.ping') - and not k.endswith('.url') - and not k.endswith('.del') + if not k.startswith('.') ] @property@@ -419,6 +381,14 @@
if c.dt > maybe: maybe = c.dt return maybe + + @property + def sameas(self): + r = [] + 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 @cached_property def comments(self):@@ -468,7 +438,7 @@ def is_front(self):
""" Returns if the post should be displayed on the front """ - if self.category in settings.site.get('on_front'): + if self.category not in settings.notinfeed: return True return False@@ -521,9 +491,7 @@ if self.is_reply:
return "RE: %s" % self.is_reply return self.meta.get( 'title', - arrow.get( - self.published).format( - settings.dateformat.get('display')) + self.published.format(settings.displaydate) ) @property@@ -575,7 +543,7 @@ def to_ping(self):
urls = [] w = Webmention( self.url, - 'https://fed.brid.gy', + 'https://fed.brid.gy/', os.path.dirname(self.fpath), self.dt )@@ -591,7 +559,7 @@ urls.append(w)
elif self.is_photo: w = Webmention( self.url, - 'https://brid.gy/publish/flickr', + 'https://brid.gy/publish/flickr/', os.path.dirname(self.fpath), self.dt )@@ -602,7 +570,7 @@ @property
def licence(self): if self.category in settings.licence: return settings.licence[self.category] - return settings.site.get('licence') + return settings.licence['_default'] @property def lang(self):@@ -616,16 +584,6 @@ except BaseException:
pass return lang - # @property - # def classification(self): - # c = GoogleClassifyText(self.fpath, self.content, self.lang) - # k = '/Arts & Entertainment/Visual Art & Design/Photographic & Digital Arts' - # if self.is_photo and k not in c.keys(): - # c.update({ - # k : '1.0' - # }) - # return c - @property def url(self): return "%s/%s/" % (@@ -634,131 +592,136 @@ self.name
) @property - def replies(self): - r = OrderedDict() - for mtime, c in self.comments.items(): - if c.type not in REPLY_TYPES: - continue - r[mtime] = c.tmplvars - return r - - @property - def reactions(self): - r = OrderedDict() - for mtime, c in self.comments.items(): - if c.type in REPLY_TYPES: - continue - t = "%s" % (c.type) - if t not in r: - r[t] = OrderedDict() - r[t][mtime] = c.tmplvars - return r - - @property def has_code(self): if RE_CODE.search(self.content): return True else: return False - @property + @cached_property + def review(self): + if 'review' not in self.meta: + return False + review = self.meta.get('review') + rated, outof = review.get('rating').split('/') + r = { + "@context": "https://schema.org/", + "@type": "Review", + "reviewRating": { + "@type": "Rating", + "@context": "http://schema.org", + "ratingValue": rated, + "bestRating": outof, + "worstRating": 1 + }, + "name": review.get('title'), + "text": review.get('summary'), + "url": review.get('url'), + "author": settings.author, + } + return r + + @cached_property def event(self): if 'event' not in self.meta: return False event = self.meta.get('event', {}) - event.update({ - 'startdate': arrow.get(event.get('start')).format(settings.dateformat.get('display')), - 'starttime': arrow.get(event.get('start')).format(settings.dateformat.get('iso')), - 'enddate': arrow.get(event.get('end')).format(settings.dateformat.get('display')), - 'endtime': arrow.get(event.get('end')).format(settings.dateformat.get('iso')), - }) - return event + r = { + "@context": "http://schema.org", + "@type": "Event", + "endDate": str(arrow.get(event.get('end'))), + "startDate": str(arrow.get(event.get('start'))), + "location": { + "@context": "http://schema.org", + "@type": "Place", + "address": event.get('location'), + "name": event.get('location'), + }, + "name": self.title + } + return r + @cached_property def jsonld(self): r = { "@context": "http://schema.org", - "@type": "BlogPosting", + "@type": "Article", + "@id": self.url, + "inLanguage": self.lang, "headline": self.title, "url": self.url, - "mainEntityOfPage": self.url, - "articleBody": self.html_content, + "genre": self.category, + "mainEntityOfPage": "%s#article" % (self.url), + "dateModified": str(arrow.get(self.dt)), + "datePublished": str(self.published), + "copyrightYear": str(self.published.format('YYYY')), + "license": "https://spdx.org/licenses/%s.html" % (self.licence), + "image": settings.author.get('image'), + "author": settings.author, + "sameAs": self.sameas, + "publisher": settings.publisher, + "name": self.name, + "text": self.html_content, "description": self.html_summary, - "dateModified": self.published.format(settings.dateformat.get('iso')), - "datePublished": self.published.format(settings.dateformat.get('iso')), - "license": self.licence, - "image": settings.author.get('avatar'), - "author": { - "@context": "http://schema.org", - "@type": "Person", - "image": settings.author.get('avatar'), - "url": settings.author.get('url'), - "name": settings.author.get('name'), - "email": settings.author.get('url'), - }, - "publisher": { - "@context": "http://schema.org", - "@type": "Organization", - "logo": { - "@context": "http://schema.org", - "@type": "ImageObject", - "url": settings.author.get('avatar'), - }, - "url": settings.author.get('url'), - "name": settings.author.get('name'), - "email": settings.author.get('url'), - }, + "potentialAction": [], + "comment": [], + "commentCount": len(self.comments.keys()), } - if (self.is_photo): + + if self.is_photo: + r.update({ + "@type": "Photograph", + "image": self.photo.jsonld, + }) + elif not self.is_photo and len(self.images): + img = list(self.images.values())[0] + r.update({ + "image": img.jsonld, + }) + elif self.has_code: + r.update({ + "@type": "TechArticle", + }) + elif self.is_page: + r.update({ + "@type": "WebPage", + }) + + + if self.is_reply: r.update({ - 'image': self.enclosure.get('url'), + "mentions": { + "@context": "http://schema.org", + "@type": "Thing", + "url": self.is_reply + } }) - return json.dumps(r) + + if self.review: + r.update({"review": self.review}) + + if self.event: + r.update({"subjectOf": self.event}) + + for donation in settings.donateActions: + r["potentialAction"].append(donation) - @cached_property - def tmplvars(self): - v = { - 'title': self.title, - 'category': self.category, - 'lang': self.lang, - 'slug': self.name, - 'is_reply': self.is_reply, - 'is_page': self.is_page, - 'summary': self.summary, - 'html_summary': self.html_summary, - 'html_content': self.html_content, - 'mtime': self.dt, - 'pubtime': self.published.format(settings.dateformat.get('iso')), - 'pubdate': self.published.format(settings.dateformat.get('display')), - 'year': int(self.published.format('YYYY')), - 'licence': self.licence, - 'replies': self.replies, - 'reactions': self.reactions, - 'syndicate': self.syndicate, - 'url': self.url, - 'review': self.review, - 'has_code': self.has_code, - 'event': self.event, - 'is_photo': self.is_photo, - } - if (self.is_photo): - v.update({ - 'enclosure': self.enclosure, - 'photo': self.photo + syndicate = self.meta.get('syndicate', []) + syndicate.append("https://fed.brid.gy/") + if self.is_photo: + syndicate.append("https://brid.gy/publish/flickr/") + for url in list(set(syndicate)): + r["potentialAction"].append({ + "@context": "http://schema.org", + "@type": "InteractAction", + "url": url }) - return v - @property - def review(self): - if 'review' not in self.meta: - return False - r = self.meta.get('review') - rated, outof = r.get('rating').split('/') - r.update({ - 'rated': rated, - 'outof': outof - }) + for mtime in sorted(self.comments.keys()): + r["comment"].append(self.comments[mtime].jsonld) + return r @property@@ -802,11 +765,13 @@ self.content,
]) async def copyfiles(self): - exclude = ['.md', '.jpg', '.png', '.gif', '.ping'] - files = glob.glob(os.path.join( - os.path.dirname(self.fpath), - '*.*' - )) + exclude = ['.md', '.jpg', '.png', '.gif', '.ping', '.url', '.del', '.copy'] + files = glob.glob( + os.path.join( + os.path.dirname(self.fpath), + '*.*' + ) + ) for f in files: fname, fext = os.path.splitext(f) if fext.lower() in exclude:@@ -827,27 +792,30 @@ async def render(self):
if self.exists: return logger.info("rendering %s", self.name) - r = J2.get_template(self.template).render({ + r = J2.get_template(self.template).render({ 'baseurl': self.url, - 'post': self.tmplvars, + 'post': self.jsonld, 'site': settings.site, 'menu': settings.menu, - 'author': settings.author, 'meta': settings.meta, - 'licence': settings.licence, - 'tips': settings.tips, }) - writepath(self.renderfile, r) - #writepath(self.renderfile.replace('.html', '.json'), self.jsonld) + writepath( + self.renderfile, + r + ) + writepath( + self.renderfile.replace('.html', '.json'), + json.dumps(self.jsonld, indent=4, ensure_ascii=False) + ) class Home(Singular): def __init__(self, fpath): super().__init__(fpath) - self.elements = [] + self.posts = [] def add(self, category, post): - self.elements.append((category.ctmplvars, post.tmplvars)) + self.posts.append((category.ctmplvars, post.jsonld)) @property def renderdir(self):@@ -863,9 +831,10 @@
@property def dt(self): maybe = super().dt - for cat, post in self.elements: - if post['mtime'] > maybe: - maybe = post['mtime'] + for cat, post in self.posts: + pts = arrow.get(post['dateModified']).timestamp + if pts > maybe: + maybe = pts return maybe async def render(self):@@ -874,17 +843,13 @@ return
logger.info("rendering %s", self.name) r = J2.get_template(self.template).render({ 'baseurl': settings.site.get('url'), - 'post': self.tmplvars, + 'post': self.jsonld, 'site': settings.site, 'menu': settings.menu, - 'author': settings.author, 'meta': settings.meta, - 'licence': settings.licence, - 'tips': settings.tips, - 'elements': self.elements + 'posts': self.posts }) writepath(self.renderfile, r) - class WebImage(object):@@ -913,36 +878,64 @@ return True
return False @cached_property - def tmplvars(self): - return { - 'src': self.src, - 'href': self.href, - 'width': self.displayed.width, - 'height': self.displayed.height, - 'title': self.title, - 'caption': self.caption, - 'exif': self.exif, - 'is_photo': self.is_photo, - #'is_mainimg': self.is_mainimg, + def jsonld(self): + r = { + "@context": "http://schema.org", + "@type": "ImageObject", + "url": self.href, + "image": self.href, + "thumbnail": { + "@context": "http://schema.org", + "@type": "ImageObject", + "url": self.src, + "width": self.displayed.width, + "height": self.displayed.height, + }, + "name": os.path.basename(self.fpath), + "encodingFormat": self.mime_type, + "contentSize": self.mime_size, + "width": self.linked.width, + "height": self.linked.height, + "dateCreated": self.exif.get('CreateDate'), + "exifData": [], + "caption": self.caption, + "headline": self.title, + "representativeOfPage": False } + for k, v in self.exif.items(): + r["exifData"].append({ + "@type": "PropertyValue", + "name": k, + "value": v + }) + if self.is_photo: + r.update({ + "creator": settings.author, + "copyrightHolder": settings.author + }) + if self.is_mainimg: + r.update({"representativeOfPage": True}) + return r + + #@cached_property + #def tmplvars(self): + #return { + #'src': self.src, + #'href': self.href, + #'width': self.displayed.width, + #'height': self.displayed.height, + #'title': self.title, + #'caption': self.caption, + #'exif': self.exif, + #'is_photo': self.is_photo, + #'is_mainimg': self.is_mainimg + #} def __str__(self): if len(self.mdimg.css): return self.mdimg.match tmpl = J2.get_template("%s.j2.html" % (self.__class__.__name__)) - return tmpl.render(self.tmplvars) - - # @cached_property - # def visionapi(self): - # return GoogleVision(self.fpath, self.src) - - # @property - # def onlinecopies(self): - # copies = {} - # for m in self.visionapi.onlinecopies: - # if settings.site.get('domain') not in m: - # copies[m] = True - # return copies.keys() + return tmpl.render(self.jsonld) @cached_property def meta(self):@@ -1032,28 +1025,25 @@
@property def exif(self): exif = { - 'camera': '', - 'aperture': '', - 'shutter_speed': '', - 'focallength': '', - 'iso': '', - 'lens': '', - 'geo_latitude': '', - 'geo_longitude': '', + 'Model': '', + 'FNumber': '', + 'ExposureTime': '', + 'FocalLength': '', + 'ISO': '', + 'LensID': '', + 'CreateDate': str(arrow.get(self.mtime)) } if not self.is_photo: return exif mapping = { - 'camera': ['Model'], - 'aperture': ['FNumber', 'Aperture'], - 'shutter_speed': ['ExposureTime'], - # 'focallength': ['FocalLengthIn35mmFormat', 'FocalLength'], - 'focallength': ['FocalLength'], - 'iso': ['ISO'], - 'lens': ['LensID', 'LensSpec', 'Lens'], - 'geo_latitude': ['GPSLatitude'], - 'geo_longitude': ['GPSLongitude'], + 'Model': ['Model'], + 'FNumber': ['FNumber', 'Aperture'], + 'ExposureTime': ['ExposureTime'], + 'FocalLength': ['FocalLength'], # ['FocalLengthIn35mmFormat'], + 'ISO': ['ISO'], + 'LensID': ['LensID', 'LensSpec', 'Lens'], + 'CreateDate': ['CreateDate', 'DateTimeOriginal'] } for ekey, candidates in mapping.items():@@ -1061,8 +1051,6 @@ for candidate in candidates:
maybe = self.meta.get(candidate, None) if not maybe: continue - elif 'geo_' in ekey: - exif[ekey] = round(float(maybe), 5) else: exif[ekey] = maybe break@@ -1358,14 +1346,10 @@ return 'Search.j2.php'
async def _render(self): r = J2.get_template(self.templatefile).render({ - 'baseurl': settings.site.get('search'), 'post': {}, 'site': settings.site, 'menu': settings.menu, - 'author': settings.author, 'meta': settings.meta, - 'licence': settings.licence, - 'tips': settings.tips, }) writepath(self.renderfile, r)@@ -1472,15 +1456,17 @@ def sortedkeys(self):
return list(sorted(self.keys(), reverse=True)) @property - def display(self): - return settings.categorydisplay.get(self.name, '') + def is_paginated(self): + if self.name in settings.flat: + return False + return True @property def title(self): if len(self.name): - return "%s - %s" % (self.name, settings.site.get('domain')) + return "%s - %s" % (self.name, settings.site.get('name')) else: - return settings.site.get('title') + return settings.site.get('headline') @property def url(self):@@ -1550,7 +1536,7 @@ )
def get_posts(self, start=0, end=-1): return [ - self[k].tmplvars + self[k].jsonld for k in self.sortedkeys[start:end] ]@@ -1576,7 +1562,6 @@ @property
def ctmplvars(self): return { 'name': self.name, - 'display': self.display, 'url': self.url, 'feed': self.feedurl, 'title': self.title,@@ -1590,13 +1575,10 @@ return {
'baseurl': baseurl, 'site': settings.site, 'menu': settings.menu, - 'author': settings.author, 'meta': settings.meta, - 'licence': settings.licence, - 'tips': settings.tips, 'category': { 'name': self.name, - 'display': self.display, + 'paginated': self.is_paginated, 'url': self.url, 'feed': self.feedurl, 'title': self.title,@@ -1626,7 +1608,7 @@ self.name,
xmlformat ) start = 0 - end = int(settings.site.get('pagination')) + end = int(settings.pagination) fg = FeedGenerator() fg.id(self.feedurl)@@ -1637,57 +1619,53 @@ 'email': settings.author.get('email')
}) fg.logo('%s/favicon.png' % settings.site.get('url')) fg.updated(arrow.get(self.mtime).to('utc').datetime) - fg.description(settings.site.get('title')) + fg.description(settings.site.get('headline')) - for post in reversed(self.get_posts(start, end)): - dt = arrow.get(post.get('pubtime')) - mtime = arrow.get(post.get('mtime')) + for k in reversed(self.sortedkeys[start:end]): + post = self[k] fe = fg.add_entry() - fe.id(post.get('url')) - fe.title(post.get('title')) - + fe.id(post.url) + fe.title(post.title) fe.author({ 'name': settings.author.get('name'), 'email': settings.author.get('email') }) - fe.category({ - 'term': post.get('category'), - 'label': post.get('category'), + 'term': post.category, + 'label': post.category, 'scheme': "%s/category/%s/" % ( settings.site.get('url'), - post.get('category') + post.category ) }) - fe.published(dt.datetime) - fe.updated(mtime.datetime) + fe.published(post.published.datetime) + fe.updated(arrow.get(post.dt).datetime) fe.rights('%s %s %s' % ( - post.get('licence').upper(), + post.licence.upper(), settings.author.get('name'), - dt.format('YYYY') + post.published.format('YYYY') )) if xmlformat == 'rss': - fe.link(href=post.get('url')) - fe.content(post.get('html_content'), type='CDATA') - #fe.description(post.get('summary'), isSummary=True) - if 'enclosure' in post: - enc = post.get('enclosure') + fe.link(href=post.url) + fe.content(post.html_content, type='CDATA') + if post.is_photo: fe.enclosure( - enc.get('url'), - "%d" % enc.get('size'), - enc.get('mime') + post.photo.href, + "%d" % post.photo.mime_size, + post.photo.mime_type, ) elif xmlformat == 'atom': fe.link( - href=post.get('url'), + href=post.url, rel='alternate', - type='text/html') - fe.content(src=post.get('url'), type='text/html') - fe.summary(post.get('summary')) + type='text/html' + ) + fe.content(src=post.url, type='text/html') + fe.summary(post.summary) if xmlformat == 'rss': fg.link(href=self.feedurl)@@ -1737,59 +1715,6 @@ )
) writepath(fpath, r) - #async def render_archives(self): - #by_time = {} - #for key in self.sortedkeys: - #trange = arrow.get(key).format(self.trange) - #if trange not in by_time: - #by_time.update({ - #trange: [] - #}) - #by_time[trange].append(key) - - #keys = list(by_time.keys()) - #for p, c, n in zip([None] + keys[:-1], keys, keys[1:] + [None]): - #form = c.format(self.trange) - #if max(keys) == form: - #fpath = self.indexfpath() - #else: - #fpath = self.indexfpath(form) - - #try: - #findex = self.sortedkeys.index(by_time[c][0]) - #lindex = self.sortedkeys.index(by_time[c][-1]) - #newest = self.newest(findex, lindex) - #except Exception as e: - #logger.error( - #'calling newest failed with %s for %s', - #self.name, - #c - #) - #continue - - #if self.is_uptodate(fpath, newest): - #logger.info( - #'%s/%s index is up to date', - #self.name, - #form - #) - #continue - #else: - #logger.info( - #'%s/%s index is outdated, generating new', - #self.name, - #form - #) - #r = J2.get_template(self.template).render( - #self.tmplvars( - #[self[k].tmplvars for k in by_time[c]], - #c=form, - #p=p, - #n=n - #) - #) - #writepath(fpath, r) - async def render_feeds(self): if not self.is_uptodate(self.rssfeedfpath, self.newest()): logger.info(@@ -1818,7 +1743,7 @@
async def render(self): await self.render_feeds() - if self.display == 'flat': + if not self.is_paginated: if not self.is_uptodate(self.indexfpath(), self.newest()): logger.info( '%s flat index outdated, generating new',@@ -1861,7 +1786,7 @@ class WebmentionIO(object):
def __init__(self): self.params = { 'token': '%s' % (keys.webmentionio.get('token')), - 'since': '%s' % self.since.format(settings.dateformat.get('iso')), + 'since': '%s' % str(self.since), 'domain': '%s' % (keys.webmentionio.get('domain')) } self.url = 'https://webmention.io/api/mentions'@@ -1897,7 +1822,7 @@ dt = arrow.get(webmention.get('data').get('published'))
slug = webmention.get('target').strip('/').split('/')[-1] # ignore selfpings - if slug == settings.site.get('domain'): + if slug == settings.site.get('name'): return fdir = glob.glob(os.path.join(settings.paths.get('content'), '*', slug))@@ -1933,7 +1858,7 @@ 'name': author.get('name', ''),
'url': author.get('url', ''), 'photo': author.get('photo', '') }, - 'date': dt.format(settings.dateformat.get('iso')), + 'date': str(dt), 'source': webmention.get('source'), 'target': webmention.get('target'), 'type': webmention.get('activity').get('type', 'webmention')
@@ -12,56 +12,108 @@
base = os.path.abspath(os.path.expanduser('~/Projects/petermolnar.net')) syncserver = 'liveserver:/web/petermolnar.net' +pagination = 42 +notinfeed = ['note'] +flat = ['article', 'journal'] +displaydate = 'YYYY-MM-DD HH:mm' + +author = { + "@context": "http://schema.org", + "@type": "Person", + "image": "https://petermolnar.net/molnar_peter_avatar.jpg", + "email": "mail@petermolnar.net", + "url": "https://petermolnar.net/", + "name": "Peter Molnar", + "sameAs": [ + "https://github.com/petermolnar", + "https://petermolnar.net/cv.html", + ], + "follows": "https://petermolnar.net/following.opml", + #"@id": "https://petermolnar.net/#me", +} + +publisher = { + "@context": "http://schema.org", + "@type": "Organization", + "logo": { + "@context": "http://schema.org", + "@type": "ImageObject", + "url": "https://petermolnar.net/molnar_peter_avatar.jpg" + }, + "url": "https://petermolnar.net/", + "name": "petermolnar.net", + "email": "webmaster@petermolnar.net" +} + site = { - 'title': 'Peter Molnar', - 'url': 'https://petermolnar.net', - 'domain': 'petermolnar.net', - 'pagination': 42, - 'on_front': [ - 'article', - 'photo', - 'journal' - ], - 'licence': 'CC-BY-NC-ND-4.0', - 'search': 'https://petermolnar.net/search.php', - 'favicon': 'https://petermolnar.net/favicon.ico' + "@context": "http://schema.org", + "@type": "WebSite", + "headline": "Peter Molnar", + "url": "https://petermolnar.net", + "name": "petermolnar.net", + "image": "https://petermolnar.net/favicon.ico", + "author": author, + "publisher": publisher, + "potentialAction": { + "@type": "SearchAction", + "target": "https://petermolnar.net/search.php?q={q}", + "query-input": "required name=q", + "url": "https://petermolnar.net/search.php" + } } +donateActions = [ + { + "@context": "http://schema.org", + "@type": "DonateAction", + "description": "Monzo (only in the UK or via Google Pay)", + "name": "monzo", + "price": "3GBP", + "recipient": author, + "url": "https://monzo.me/petermolnar/3" + }, + { + "@context": "http://schema.org", + "@type": "DonateAction", + "description": "Paypal", + "name": "paypal", + "price": "3GBP", + "recipient": author, + "url": "https://paypal.me/petermolnar/3GBP" + } +] + menu = { 'home': { - 'url': '%s/' % site['url'], + 'url': '%s/' % site['url'], 'text': 'home', }, 'photo': { - 'url': '%s/category/photo/' % site['url'], + 'url': '%s/category/photo/' % site['url'], 'text': 'photos', }, 'journal': { - 'url': '%s/category/journal/' % site['url'], + 'url': '%s/category/journal/' % site['url'], 'text': 'journal', }, 'article': { - 'url': '%s/category/article/' % site['url'], + 'url': '%s/category/article/' % site['url'], 'text': 'IT', }, 'note': { - 'url': '%s/category/note/' % site['url'], + 'url': '%s/category/note/' % site['url'], 'text': 'notes' }, 'follow': { - 'url': '%s/follow/' % site['url'], + 'url': '%s/follow/' % site['url'], 'text': 'follow' } } -categorydisplay = { - 'article': 'flat', - 'journal': 'flat', -} - licence = { 'article': 'CC-BY-4.0', 'journal': 'CC-BY-NC-4.0', + '_default': 'CC-BY-NC-ND-4.0' } meta = {@@ -71,29 +123,7 @@ 'hub': 'https://petermolnar.superfeedr.com/',
'authorization_endpoint': 'https://indieauth.com/auth', 'token_endpoint': 'https://tokens.indieauth.com/token', 'micropub': 'https://petermolnar.net/micropub.php', - 'microsub': 'https://aperture.p3k.io/microsub/83' -} - -author = { - 'name': 'Peter Molnar', - 'email': 'mail@petermolnar.net', - 'url': 'https://petermolnar.net/', - 'avatar': 'https://petermolnar.net/molnar_peter_avatar.jpg', - 'gpg': 'https://petermolnar.net/pgp.asc', - 'cv': 'https://petermolnar.net/cv.html', - 'github': 'https://github.com/petermolnar', - 'about': 'https://petermolnar.net/about/', - 'following': 'https://petermolnar.net/following.opml', - 'tips': { - 'monzo': { - 'text': 'Monzo (only in the UK or via Google Pay)', - 'url': 'https://monzo.me/petermolnar/3' - }, - 'paypal': { - 'text': 'Paypal', - 'url': 'https://paypal.me/petermolnar/3GBP' - } - } + #'microsub': 'https://aperture.p3k.io/microsub/83' } paths = {@@ -118,17 +148,6 @@ #360 = m
720: '', 1280: '_b', }, -} - -tips = { - 'paypal': 'https://paypal.me/petermolnar/3GBP', - 'monzo': 'https://monzo.me/petermolnar/3', -} - -dateformat = { - 'iso': 'YYYY-MM-DDTHH:mm:ssZZ', - 'display': 'YYYY-MM-DD HH:mm', - 'fname': 'YYYYMMDDHHmmssZ', } _parser = argparse.ArgumentParser(description='Parameters for NASG')
@@ -9,7 +9,7 @@ <link rel="feed" title="{{ category.title}} feed" href="{{ category.url }}" />
{% endblock %} {% block pagination %} -{% if category.display != 'flat' %} +{% if category.paginated %} <nav> <ul> {% for y, url in category.years.items() %}@@ -33,7 +33,7 @@ {% endif %}
{% endblock %} {% block content %} -<main class="content-body h-feed hfeed {{ category.name }}" vocab="http://schema.org/" typeof="Blog WebPage"> +<main class="h-feed hfeed"> <header> <p> <svg width="128" height="128"><use xlink:href="#icon-{{ category.name }}" /></svg>@@ -44,62 +44,15 @@
{% set year = [0] %} {% for post in posts %} {% set _year = year.pop() %} - {% if category.display == 'flat' and _year != post.year %} + {% if not category.paginated and _year != post.copyrightYear %} {% if not loop.first %} </section> {% endif %} <section> - <h2>{{ post.year }}</h2> + <h2>{{ post.copyrightYear }}</h2> {% endif %} - {% set _ = year.append(post.year)%} - - <article class="h-entry hentry singular" lang="{{ post.lang }}" property="blogPost" typeof="BlogPosting"> - <header> - {% if category.display == 'flat' %} - <h3 class="p-name entry-title" property="headline"> - {% else %} - <h2 class="p-name entry-title" property="headline"> - {% endif %} - {% if post.is_reply %} - <svg class="icon" width="16" height="16"> - <use xlink:href="#icon-reply" /> - </svg> - <a href="{{ post.url }}/"> - RE: - </a> - <a href="{{ post.is_reply }}" class="u-in-reply-to"> - {{ post.is_reply }} - </a> - {% else %} - <a href="{{ post.url }}"> - <span class="entry-title p-name">{{ post.title }}</span> - </a> - {% endif %} - {% if category.display == 'flat' %} - </h3> - {% else %} - </h2> - {% endif %} - <a href="{{ post.url }}" property="url mainEntityOfPage" class="u-url bookmark"></a> - </header> - - {% if post.summary %} - <div class="e-summary entry-summary" property="description"> - {{ post.html_summary }} - <p class="more"> - <a href="{{ post.url }}"> - {% if post.lang == 'hu' %}Tovább »{% else %}Continue »{% endif %} - </a> - </p> - </div> - {% else %} - <div class="e-content entry-content" property="articleBody"> - {{ post.html_content }} - </div> - {% endif %} - - {% include 'meta-publisher.j2.html' %} - </article> + {% set _ = year.append(post.copyrightYear)%} + {% include 'meta-article.j2.html' %} {% if loop.last %} </section> {% endif %}
@@ -1,10 +1,10 @@
{% extends "base.j2.html" %} -{% block title %}{{ post.title }} - {{ site.domain }}{% endblock %} +{% block title %}{{ post.headline }} - {{ site.name }}{% endblock %} {% block meta %} - <meta name="author" content="{{ author.name }} <{{ author.email }}>" /> - <meta name="description" content="{{ post.summary|e }}" /> + <meta name="author" content="{{ site.author.name }} <{{ site.author.email }}>" /> + <meta name="description" content="{{ post.description|e }}" /> <link rel="canonical" href="{{ post.url }}" /> {% for category, latest in elements %} <link rel="alternate" type="application/rss+xml" title="{{ category.title }} RSS feed" href="{{ category.feed }}" />@@ -14,60 +14,22 @@ {% endfor %}
{% endblock %} {% block content %} -<main class="content-body h-feed hfeed" vocab="http://schema.org/" typeof="Blog WebPage"> +<main class="h-feed hfeed"> <aside> <div> - {{ post.html_content }} + {{ post.text }} </div> </aside> - {% for category, post in elements %} + {% for category, post in posts %} <section> - <h2>post in + <h2>in: <a href="{{ category.url }}/"> <svg width="16" height="16"><use xlink:href="#icon-{{ category.name }}" /></svg> {{ category.name }} </a> </h2> - <article class="h-entry hentry singular" lang="{{ post.lang }}" property="blogPost" typeof="BlogPosting"> - <header> - <h3 class="p-name entry-title" property="name headline" > - {% if post.is_reply %} - <svg class="icon" width="16" height="16"> - <use xlink:href="#icon-reply" /> - </svg> - <a href="{{ post.url }}/"> - RE: - </a> - <a href="{{ post.is_reply }}" class="u-in-reply-to"> - {{ post.is_reply }} - </a> - {% else %} - <a href="{{ post.url }}"> - <span class="entry-title p-name">{{ post.title }}</span> - </a> - {% endif %} - </h3> - <a href="{{ post.url }}" property="url mainEntityOfPage" class="u-url bookmark"></a> - </header> - - {% if post.summary %} - <div class="e-summary entry-summary" property="description"> - {{ post.html_summary }} - <span class="more"> - <a href="{{ post.url }}"> - {% if post.lang == 'hu' %}Tovább »{% else %}Continue »{% endif %} - </a> - </span> - </div> - {% else %} - <div class="e-content entry-content" property="articleBody"> - {{ post.html_content }} - </div> - {% endif %} - - {% include 'meta-publisher.j2.html' %} - </article> + {% include 'meta-article.j2.html' %} </section> {% endfor %} </main>
@@ -1,43 +1,62 @@
{% extends "base.j2.html" %} -{% block lang %} lang="{{ post.lang }}" {% endblock %} +{% macro includefile(fname) %} +{% include fname %} +{% endmacro %} + +{% block lang %} lang="{{ post.inLanguage }}" {% endblock %} -{% block title %}{{ post.title }} - {{ site.domain }}{% endblock %} +{% block title %}{{ post.headline }} - {{ site.name }}{% endblock %} {% block meta %} - <meta name="author" content="{{ author.name }} <{{ author.email }}>" /> - <meta name="description" content="{{ post.summary|e }}" /> + <meta name="author" content="{{ post.author.name }} ({{ post.author.email }})" /> + <meta name="description" content="{{ post.description|striptags|e }}" /> <link rel="canonical" href="{{ post.url }}" /> - {% if post.has_code %} + <script type="application/ld+json"> + {{ post|tojson(indent=4) }} + </script> + {% if post['@type'] == 'TechArticle' %} <style media="all"> - {% include 'prism.css' %} + {{ includefile('prism.css')|indent(8) }} </style> + {% endif %} + + <meta property="og:title" content="{{ post.headline }}" /> + <meta property="og:type" content="article" /> + <meta property="og:url" content="{{ post.url }}" /> + <meta property="og:description" content="{{ post.description|striptags|e }}" /> + <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 }}" /> + {% else %} + <meta property="og:image" content="{{ post.image }}" /> {% endif %} {% endblock %} {% block prism %} - {% if post.has_code %} + {% if post['@type'] == 'TechArticle' %} <script> - {% include 'prism.js' %} + {{ includefile('prism.js')|indent(8) }} </script> {% endif %} {% endblock %} -{% block licence %} - <link rel="license" href="https://spdx.org/licenses/{{ post.licence }}.html" type="{{ post.licence }}" /> -{% endblock %} - {% block content %} {% if post.event %} - {% set mftype = 'h-entry h-event' %} + {% set mftype = 'h-entry hentry h-event hevent' %} {% else %} - {% set mftype = 'h-entry' %} + {% set mftype = 'h-entry hentry' %} {% endif %} -<main role="main" vocab="http://schema.org/" typeof="Blog WebPage"> - <article class="{{ mftype }} hentry singular" lang="{{ post.lang }}" property="blogPost" typeof="BlogPosting"> +<main> + <article class="{{ mftype }}" lang="{{ post.inLanguage }}" id="article"> <header> - <h1 class="entry-title p-name" property="headline"> - {% if post.is_reply %} + <h1 class="entry-title p-name"> + {% if post.mentions %} <span> <svg width="16" height="16"> <use xlink:href="#icon-reply" />@@ -45,113 +64,93 @@ </svg>
<a href="{{ post.url }}"> RE: </a> - <a href="{{ post.is_reply }}" class="u-in-reply-to"> - {{ post.is_reply }} + <a href="{{ post.mentions.url }}" class="u-in-reply-to"> + {{ post.mentions.url }} </a> </span> {% else %} <a href="{{ post.url }}"> - <span>{{ post.title }}</span> + {{ post.headline }} </a> {% endif %} </h1> </header> {% if post.review %} - <div class="h-review hreview" property="review" typeof="http://schema.org/Review"> - <strong>Review summary of: <a href="{{ post.review.url }}" class="item fn p-name u-url p-item h-product">{{ post.review.title }}</a></strong> + <div class="h-review hreview"> + <strong>Review summary of: <a href="{{ post.review.url }}" class="item fn p-name u-url p-item h-product">{{ post.review.name }}</a></strong> <p> By - <span class="p-author h-card vcard reviewer" property="author" typeof="Person"> - <a class="fn p-name url u-url u-uid" href="{{ author.url }}" property="url"> - <span property="name">{{ author.name }}</span> - </a></span> at <time class="dt-published dtreviewed" datetime="{{ post.pubtime }}" property="datePublished">{{ post.pubdate }}</time> + <span class="p-author h-card vcard reviewer"> + <a class="fn p-name url u-url u-uid" href="{{ post.author.url }}"> + <span>{{ post.author.name }}</span> + </a></span> in <time class="dt-published dtreviewed" datetime="{{ post.datePublished }}">{{ post.copyrightYear }}</time> </p> <p> - <span class="rating" property="reviewRating" itemscope typeof="Rating"> - <meta property="worstRating" content = "1"> - <span class="value" property="ratingValue">{{ post.review.rated }}</span> + <span class="rating"> + <span class="value">{{ post.review.reviewRating.ratingValue }}</span> out of - <span class="best" property="bestRating">{{ post.review.outof }}</span> + <span class="best">{{ post.review.reviewRating.bestRating }}</span> </span> </p> - <p class="p-summary summary" property="reviewBody">{{ post.review.summary }}</p> + <p class="p-summary summary">{{ post.review.text }}</p> </div> {% endif %} - {% if post.summary %} - <div class="e-summary entry-summary" property="description"> - {{ post.html_summary }} + {% if post.description|length %} + <div class="e-summary entry-summary"> + {{ post.description }} </div> {% endif %} - <div class="e-content entry-content" property="articleBody"> - {{ post.html_content }} + <div class="e-content entry-content"> + {{ post.text }} </div> <footer> - {% if not post.enclosure %} - <img aria-hidden="true" hidden="hidden" class="u-featured hidden" property="image" src="{{ author.avatar }}" /> - {% else %} - <img aria-hidden="true" hidden="hidden" class="u-featured hidden" property="image" src="{{ post.enclosure.url }}" /> - {% endif %} <dl> - {% if post.event %} + {% if post.subjectOf %} <dt>Trip details</dt> <dd> From - <time class="dt-start dtstart" datetime="{{ post.event.starttime }}"> - {{ post.event.startdate }} + <time class="dt-start dtstart" datetime="{{ post.subjectOf.startDate }}"> + {{ post.subjectOf.startDate }} </time> to - <time class="dt-end dtend" datetime="{{ post.event.endtime }}"> - {{ post.event.enddate }} + <time class="dt-end dtend" datetime="{{ post.subjectOf.endDate }}"> + {{ post.subjectOf.endDate }} </time>, in <span class="p-location location"> - {{ post.event.location }} + {{ post.subjectOf.location.name }} </span> </dd> {% endif %} <dt>Author</dt> <dd> - <span class="p-author h-card vcard" property="author" typeof="Person"> + <span class="p-author h-card vcard"> <img class="photo avatar u-photo u-avatar" - src="{{ author.avatar }}" - alt="Photo of {{ author.name }}" - property="image" /> - <a class="fn p-name url u-url u-uid org" - href="{{ author.url }}" - property="url"> - <span property="name">{{ author.name }}</span> + src="{{ post.author.image }}" + alt="Photo of {{ post.author.name }}" /> + <a class="fn p-name url u-url u-uid org" href="{{ post.author.url }}"> + {{ post.author.name }} </a> - <a class="u-email email" href="mailto:{{ author.email }}"> - {{ author.email }} + <a class="u-email email" href="mailto:{{ post.author.email }}"> + {{ post.author.email }} </a> </span> - <span aria-hidden="true" hidden="hidden" property="publisher" typeof="Organization"> - <span property="name">{{ site.domain }}</span> - <a href="{{ site.url }}" property="url">{{ site.url }}</a> - <span property="logo" typeof="ImageObject"> - <img src="{{ author.avatar }}" alt="" property="url" /> - </span> - </span> </dd> <dt>Published</dt> <dd class="published updated"> - <time class="dt-published dt-updated" - datetime="{{ post.pubtime }}" - property="dateModified datePublished" - >{{ post.pubdate }}</time> + <time class="dt-published dt-updated" datetime="{{ post.datePublished }}">{{ post.datePublished|printdate }}</time> </dd> - <dt>License</dt> <dd class="license"> - {% if post.licence == 'CC-BY-4.0' %} - <a rel="license" href="https://creativecommons.org/licenses/by/4.0/" class="u-license" property="license"> - {{ post.licence }} + {% if 'CC-BY-4.0' in post.license %} + <a rel="license" href="{{ post.license }}" class="u-license"> + CC-BY-4.0 </a> <ul> <li>you can share it</li>@@ -160,9 +159,9 @@ <li>you can modify it, but you need to indicate the modifications</li>
<li>you can use it for commercial purposes</li> <li>you always need to make a link back here</li> </ul> - {% elif post.licence == 'CC-BY-NC-4.0' %} - <a rel="license" href="https://creativecommons.org/licenses/by-nc/4.0/" class="u-license" property="license"> - {{ post.licence }} + {% elif 'CC-BY-NC-4.0' in post.license %} + <a rel="license" href="{{ post.license }}" class="u-license"> + CC-BY-NC-4.0 </a> <ul> <li>you can share it</li>@@ -172,9 +171,9 @@ <li>you can't use it for commercial purposes</li>
<li>you always need to make a link back here</li> </ul> For commercial use, please contact me. - {% elif post.licence == 'CC-BY-NC-ND-4.0' %} - <a rel="license" href="https://creativecommons.org/licenses/by-nc-nd/4.0/" class="u-license" property="license"> - {{ post.licence }} + {% elif 'CC-BY-NC-ND-4.0' in post.license %} + <a rel="license" href="{{ post.license }}" class="u-license"> + CC-BY-NC-ND-4.0 </a> <ul> <li>you can share it</li>@@ -189,54 +188,68 @@ </dd>
<dt>Entry URL</dt> <dd> - <a class="u-url u-uuid" href="{{ post.url }}" property="url"> + <a class="u-url u-uuid" href="{{ post.url }}"> {{ post.url }} </a> </dd> + {% if post.sameAs|length %} + <dt>Also on</dt> + <dd> + <ul> + {% for url in post.sameAs %} + <li> + <a class="u-syndication" href="{{ url }}"> + {{ url }} + </a> + </li> + {% endfor %} + </ul> + </dd> + {% endif %} + </dl> </footer> - {% if not post.is_page %} - {% if post.syndicate|length %} + {% if 'WebPage' != post['@type'] %} <section class="syndication"> - {% for url in post.syndicate %} - <a href="{{ url }}" class="u-syndication"></a> - {% endfor %} + {% for action in post.potentialAction %} + {% if 'InteractAction' == action['@type'] %} + <a href="{{ action.url }}"></a> + {% endif %} + {% endfor %} </section> - {% endif %} <section class="encourage"> <h2>Encourage creation!</h2> <p> If this entry helped you, or you simply liked it, leave a tip via <br /> - {% for provider, info in author.tips.items() %} - <span typeof="DonateAction" property="potentialAction"> - {% if loop.last and not loop.first %} or {% endif %} - <a property="url" href="{{ info.url }}"> + {% 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-{{ provider }}"></use> - </svg> {{ info.text }}</a> - {% if not loop.last and not loop.first %}, {% endif %} - <span aria-hidden="true" hidden="hidden" property="recipient" typeof="Person"> - for - <span property="name">{{ author.name }}</span> - (<span property="email">{{ author.email }}</span>) - </span> - </span> + <use xlink:href="#icon-{{ donate.name }}"></use> + </svg> {{ donate.description }}</a> + {% if counters.update({'donation': True}) %} {% endif %} + {% endif %} {% endfor %} </p> </section> - {% if post.replies|length %} - <section class="replies"> - <h2><a id="replies"></a>Replies</h2> + {% if post.comment|length %} + <section class="comments"> + <h2><a id="comments"></a>Responses</h2> <ol> - {% for mtime, comment in post.replies.items() %} + {% for comment in post.comment %} <li class="h-entry p-comment"> - <time class="dt-published" datetime="{{ comment.pubtime }}"> - {{ comment.pubdate }} - </time> from + <span class="reaction"> + <a class="u-url" href="{{ comment.url }}"> + {{ comment.disambiguatingDescription }} + </a> + </span> + from <span class="p-author h-card"> {% if comment.author.url %} <a class="url u-url" href="{{ comment.author.url }}">@@ -249,58 +262,23 @@ <span class="p-name fn">
{{ comment.author.name }} </span> {% endif %} - </span><br /> + </span> + at + <time class="dt-published" datetime="{{ comment.datePublished }}"> + {{ comment.datePublished|printdate }} + </time> + <br /> <span class="source"> <svg width="16" height="16"> <use xlink:href="#icon-link"></use> </svg> - <a class="u-url" href="{{ comment.source }}"> - {{ comment.source }} + <a class="u-url" href="{{ comment.url }}"> + {{ comment.url }} </a> </span> </li> {% endfor %} </ol> - </section> - {% endif %} - - {% if post.reactions|length %} - <section class="reactions"> - <h2><a id="reactions"></a>Reactions</h2> - <dl> - {% for character, comments in post.reactions.items() %} - <dt>{{ character }}</dt> - <dd> - <ul> - {% for mtime, comment in comments.items() %} - <li class="h-entry p-comment"> - <span class="reaction"> - <a class="u-url" href="{{ comment.source }}"> - {{ comment.type }} - </a> - </span> - <time class="dt-published" datetime="{{ comment.pubtime }}"> - {{ comment.pubdate }} - </time> from - <span class="p-author h-card"> - {% if comment.author.url %} - <a class="url u-url" href="{{ comment.author.url }}"> - <span class="p-name fn"> - {{ comment.author.name }} - </span> - </a> - {% else %} - <span class="p-name fn"> - {{ comment.author.name }} - </span> - {% endif %} - </span> - </li> - {% endfor %} - </ul> - </dd> - {% endfor %} - </dl> </section> {% endif %}
@@ -1,56 +1,63 @@
<figure class="photo"> -{% if href != src %} - <a href="{{ href }}"> +{% if url != thumbnail.url %} + <a href="{{ url }}"{% if representativeOfPage %} class="u-featured"{% endif %}> {% endif %} - <img src="{{ src }}" title="{{ title }}" alt="" width="{{ width }}" height="{{ height }}" /> -{% if href != src %} + <img src="{{ thumbnail.url }}" title="{{ headline }}" alt="" width="{{ thumbnail.width }}" height="{{ thumbnail.height }}" /> +{% if url != thumbnail.url %} </a> {% endif %} <figcaption> <span class="alt">{{ caption }}</span> -{% if is_photo %} +{% if creator is defined %} +{% macro exifvalue(key) %} + {% for data in exifData: %} + {% if key == data['name'] %} + {{ data['value'] }} + {% endif %} + {% endfor %} +{% endmacro %} <dl class="exif"> <dt>Camera</dt> <dd> <svg width="16" height="16"> <use xlink:href="#icon-camera" /> </svg> - {{ exif.camera }} + {{ exifvalue('Model')|trim }} </dd> <dt>Aperture</dt> <dd> <svg width="16" height="16"> <use xlink:href="#icon-aperture" /> </svg> - f/{{ exif.aperture }} + f/{{ exifvalue('FNumber')|trim }} </dd> <dt>Shutter speed</dt> <dd> <svg width="16" height="16"> <use xlink:href="#icon-clock" /> </svg> - {{ exif.shutter_speed }} sec + {{ exifvalue('ExposureTime')|trim }} sec </dd> <dt>Focal length (as set)</dt> <dd> <svg width="16" height="16"> <use xlink:href="#icon-focallength" /> </svg> - {{ exif.focallength }} + {{ exifvalue('FocalLength')|trim }} </dd> <dt>Sensitivity</dt> <dd> <svg width="16" height="16"> <use xlink:href="#icon-sensitivity" /> </svg> - ISO {{ exif.iso }} + ISO {{ exifvalue('ISO')|trim }} </dd> <dt>Lens</dt> <dd> <svg width="16" height="16"> <use xlink:href="#icon-lens" /> </svg> - {{ exif.lens }} + {{ exifvalue('LensID')|trim }} </dd> </dl> {% endif %}
@@ -1,33 +1,36 @@
<!DOCTYPE html> -<html{% block lang %}{% endblock %}> +<html{% block lang %}{% endblock %} prefix="og: http://ogp.me/ns# article: http://ogp.me/ns/article#"> <head> <title>{% block title %}{% endblock %}</title> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1" /> - <link rel="icon" href="{{ site.favicon }}" /> - {% block licence %} - <link rel="license" href="https://spdx.org/licenses/{{ site.licence }}.html" type="{{ site.licence }}" /> - {% endblock %} + <link rel="icon" href="{{ site.image }}" /> {% for key, value in meta.items() %} <link rel="{{ key }}" href="{{ value }}" /> {% endfor %} - {% block meta %}{% endblock %} +{% macro cssinclude(fname) %} +{% include fname %} +{% endmacro %} <style media="all"> - {% include 'style.css' %} + {{ cssinclude('style.css')|indent(8)}} </style> <style id="css_alt" media="speech"> - {% include 'style-alt.css' %} + {{ cssinclude('style-alt.css')|indent(8)}} </style> <style media="print"> - {% include 'style-print.css' %} + {{ cssinclude('style-print.css')|indent(8)}} </style> + <script type="application/ld+json"> + {{ site|tojson(indent=4) }} + </script> + {% block meta %}{% endblock %} </head> <body> {% macro activemenu(name) %} -{% if (post is defined and post.slug == name ) - or (post is defined and post.category == name ) +{% if (post is defined and post.name == name ) + or (post is defined and post.genre == name ) or ( category is defined and category.name == name )%}active{% endif %} {% endmacro %}@@ -62,15 +65,14 @@ <input name="colorscheme" value="light" id="lightscheme" type="radio">
<label for="lightscheme">light</label> </span> </form> - <form role="search" method="get" action="{{ site.search }}" property="potentialAction" typeof="SearchAction" vocab="http://schema.org/"> - <meta property="target" content="{{ site.search }}?q={q}"/> + <form role="search" method="get" action="{{ site.potentialAction.url }}"> <label for="qsub"> <input type="submit" value="search" id="qsub" name="qsub" /> <svg width="16" height="16"> <use xlink:href="#icon-search"></use> </svg> </label> - <input type="search" placeholder="search..." value="" name="q" id="q" title="Search for:" required="required" property="query-input" /> + <input type="search" placeholder="search..." value="" name="q" id="q" title="Search for:" /> </form> </div> </section>@@ -87,39 +89,41 @@ <p>
<a href="https://creativecommons.org/">CC</a>, 1999-2019, <img class="photo avatar u-photo u-avatar" - src="{{ author.avatar }}" - alt="Photo of {{ author.name }}" /> - <a class="fn p-name url u-url u-uid org" rel="me" href="{{ site.url }}"> - {{ author.name }} + src="{{ site.author.image }}" + alt="Photo of {{ site.author.name }}" /> + <a class="fn p-name url u-url u-uid org" rel="me" href="{{ site.author.url }}"> + {{ site.author.name }} </a> - <a class="u-email email" rel="me" href="mailto:{{ author.email }}"> + <a class="u-email email" rel="me" href="mailto:{{ site.author.email }}"> <svg width="16" height="16"> - <title>email</title> <use xlink:href="#icon-mail"></use> </svg> - {{ author.email }} + {{ site.author.email }} </a> </p> <nav> <ul> <li> - <a title="following" href="{{ author.following }}"> + <a title="following" href="{{ site.author.follows }}"> <svg width="16" height="16"><use xlink:href="#icon-following" /></svg> followings </a> </li> + {% for url in site.author.sameAs %} <li> - <a title="CV" href="{{ author.cv }}"> + {% if 'cv.html' in url %} + <a title="CV" href="{{ url }}"> <svg width="16" height="16"><use xlink:href="#icon-resume" /></svg> resume </a> - </li> - <li> - <a title="github" rel="me" href="{{ author.github }}"> + {% elif 'github' in url %} + <a title="github" rel="me" href="{{ url }}"> <svg width="16" height="16"><use xlink:href="#icon-github" /></svg> github </a> + {% endif %} </li> + {% endfor %} </ul> </nav> <p>
@@ -0,0 +1,79 @@
+<article class="h-entry hentry" lang="{{ post.inLanguage }}"> + <header> + <h3 class="p-name entry-title"> + {% if post.mentions %} + <span> + <svg width="16" height="16"> + <use xlink:href="#icon-reply" /> + </svg> + <a href="{{ post.url }}"> + RE: + </a> + <a href="{{ post.mentions.url }}" class="u-in-reply-to"> + {{ post.mentions.url }} + </a> + </span> + {% else %} + <a href="{{ post.url }}"> + {{ post.headline }} + </a> + {% endif %} + + </h3> + <a href="{{ post.url }}" class="u-url"></a> + </header> + + {% if post.description|length %} + <div class="e-summary entry-summary"> + {{ post.description }} + <span class="more"> + <a href="{{ post.url }}"> + {% if post.inLanguage == 'hu' %}Tovább »{% else %}Continue »{% endif %} + </a> + </span> + </div> + {% else %} + <div class="e-content entry-content"> + {{ post.text }} + </div> + {% endif %} + + <footer aria-hidden="true" hidden="hidden"> + <time datetime="{{ post.datePublished }}" class="published dt-published"></time> + <time datetime="{{ post.dateModified }}" class="updated dt-updated"></time> + <span class="p-author h-card vcard"> + <img class="photo avatar u-photo u-avatar" + src="{{ post.author.image }}" + alt="Photo of {{ post.author.name }}" /> + <a class="fn p-name url u-url u-uid org" href="{{ post.author.url }}"> + {{ post.author.name }} + </a> + <a class="u-email email" href="mailto:{{ post.author.email }}"> + {{ post.author.email }} + </a> + </span> + </footer> + <script type="application/ld+json"> + {% + set basedata = { + "@context": post['@context'], + "@type": post['@type'], + "inLanguage": post.inLanguage, + "headline": post.headline, + "url": post.url, + "mainEntityOfPage": post.mainEntityOfPage, + "dateModified": post.dateModified, + "datePublished": post.datePublished, + "copyrightYear": post.copyrightYear, + "license": post.license, + "image": post.image, + "author": post.author, + "sameAs": post.sameAs, + "publisher": post.publisher, + "name": post.name, + "description": post.description + } + %} + {{ basedata|tojson(indent=4) }} + </script> +</article>
@@ -1,31 +0,0 @@
-<footer aria-hidden="true" hidden="hidden"> - <span class="published updated"> - <time datetime="{{ post.pubtime }}" class="dt-published dt-updated" property="dateModified datePublished">{{ post.pubdate }}</time> - </span> -{% if not post.enclosure %} - <img aria-hidden="true" hidden="hidden" class="u-featured hidden" property="image" src="{{ author.avatar }}" /> -{% else %} - <img aria-hidden="true" hidden="hidden" class="u-featured hidden" property="image" src="{{ post.enclosure.url }}" /> -{% endif %} - <p class="p-author h-card vcard" property="author" typeof="Person"> - <img class="photo avatar u-photo u-avatar" - src="{{ author.avatar }}" - alt="Photo of {{ author.name }}" - property="image" /> - <a class="fn p-name url u-url u-uid org" - href="{{ author.url }}" - property="url"> - <span property="name">{{ author.name }}</span> - </a> - <a class="u-email email" href="mailto:{{ author.email }}"> - {{ author.email }} - </a> - </p> - <span property="publisher" typeof="Organization"> - <span property="name">{{ site.domain }}</span> - <a href="{{ site.url }}" property="url">{{ site.url }}</a> - <span property="logo" typeof="ImageObject"> - <img src="{{ author.avatar }}" alt="" property="url" /> - </span> - </span> -</footer>
@@ -307,7 +307,7 @@ color: #ccc;
font-weight: bold; } -.replies .u-url, +.comments .u-url, .footnotes a { display: inline-block; overflow: hidden;@@ -357,4 +357,4 @@ bottom: 0;
right: 0; width: 10em; height: auto; -} +}