- webmention sending is back (only for RE: for now) via Telegraph

- removed the abomination experiment (aka microdata)
- added svg source icomoon for the possibility to extend later
- rsync moved into nasg.py itself, so the ordering of render - sync - webmentions is ok
This commit is contained in:
Peter Molnar 2018-08-15 11:02:59 +01:00
parent 9c749f4591
commit 55cc0f3307
11 changed files with 331 additions and 247 deletions

2
.gitignore vendored
View file

@ -1,5 +1,5 @@
__pycache__ __pycache__
_scratch _scratch
keys.py keys.py
nasg.proj
.idea .idea
.venv

Binary file not shown.

398
nasg.py
View file

@ -41,6 +41,8 @@ MarkdownImage = namedtuple(
['match', 'alt', 'fname', 'title', 'css'] ['match', 'alt', 'fname', 'title', 'css']
) )
REPLY_TYPES = ['webmention', 'in-reply-to', 'reply']
J2 = jinja2.Environment( J2 = jinja2.Environment(
loader=jinja2.FileSystemLoader(searchpath=settings.paths.get('tmpl')), loader=jinja2.FileSystemLoader(searchpath=settings.paths.get('tmpl')),
lstrip_blocks=True, lstrip_blocks=True,
@ -78,6 +80,58 @@ class cached_property(object):
setattr(inst, self.name, result) setattr(inst, self.name, result)
return result return result
class Webmention(object):
def __init__(self, source, target, stime):
self.source = source
self.target = target
self.stime = stime
@property
def fpath(self):
return os.path.join(
settings.paths.get('webmentions'),
'%s => %s.txt' % (
url2slug(self.source, 100),
url2slug(self.target, 100)
)
)
@property
def exists(self):
if not os.path.isfile(self.fpath):
return False
elif os.path.getmtime(self.fpath) > self.stime:
return True
else:
return False
async def save(self, content):
d = os.path.dirname(self.fpath)
if not os.path.isdir(d):
os.makedirs(d)
with open(self.fpath, 'wt') as f:
f.write(content)
async def send(self):
if self.exists:
return
telegraph_url = 'https://telegraph.p3k.io/webmention'
telegraph_params = {
'token': '%s' % (keys.telegraph.get('token')),
'source': '%s' % (self.source),
'target': '%s' % (self.target)
}
r = requests.post(telegraph_url, data=telegraph_params)
settings.logger.info(
"sent webmention to telegraph from %s to %s",
self.source,
self.target
)
if r.status_code not in [200, 201, 202]:
settings.logger.error('sending failed: %s %s', r.status_code, r.text)
else:
await self.save(r.text)
class MarkdownDoc(object): class MarkdownDoc(object):
@cached_property @cached_property
@ -95,12 +149,19 @@ class MarkdownDoc(object):
def content(self): def content(self):
return self._parsed[1] return self._parsed[1]
@property
def has_mainimg(self):
if hasattr(self, 'images') and len(self.images) == 1:
return True
else:
return False
@cached_property @cached_property
def html_content(self): def html_content(self):
c = "%s" % (self.content) c = "%s" % (self.content)
if hasattr(self, 'images') and len(self.images): if hasattr(self, 'images') and len(self.images):
for match, img in self.images.items(): for match, img in self.images.items():
c = c.replace(match, str(img)) c = c.replace(match, img.mkstring(self.has_mainimg))
# return MD.reset().convert(c) # return MD.reset().convert(c)
c = Pandoc(c) c = Pandoc(c)
c = RE_PRECODE.sub('<pre><code lang="\g<1>" class="language-\g<1>">', c) c = RE_PRECODE.sub('<pre><code lang="\g<1>" class="language-\g<1>">', c)
@ -337,8 +398,6 @@ class Singular(MarkdownDoc):
@property @property
def syndicate(self): def syndicate(self):
urls = self.meta.get('syndicate', []) urls = self.meta.get('syndicate', [])
if "https://brid.gy/publish/twitter" not in urls:
urls.append("https://brid.gy/publish/twitter")
if self.is_photo: if self.is_photo:
urls.append("https://brid.gy/publish/flickr") urls.append("https://brid.gy/publish/flickr")
return urls return urls
@ -375,6 +434,14 @@ class Singular(MarkdownDoc):
return True return True
return False return False
@property
def to_ping(self):
urls = []
if self.is_reply:
w = Webmention(self.url, self.is_reply, self.mtime)
urls.append(w)
return urls
@property @property
def licence(self): def licence(self):
if self.category in settings.licence: if self.category in settings.licence:
@ -404,15 +471,16 @@ class Singular(MarkdownDoc):
def replies(self): def replies(self):
r = OrderedDict() r = OrderedDict()
for mtime, c in self.comments.items(): for mtime, c in self.comments.items():
if c.type in ['webmention', 'in-reply-to']: if c.type not in REPLY_TYPES:
r[mtime] = c.tmplvars continue
r[mtime] = c.tmplvars
return r return r
@property @property
def reactions(self): def reactions(self):
r = OrderedDict() r = OrderedDict()
for mtime, c in self.comments.items(): for mtime, c in self.comments.items():
if c.type in ['webmention', 'in-reply-to']: if c.type in REPLY_TYPES:
continue continue
t = "%s" % (c.type) t = "%s" % (c.type)
if t not in r: if t not in r:
@ -450,7 +518,10 @@ class Singular(MarkdownDoc):
'has_code': self.has_code, 'has_code': self.has_code,
} }
if (self.enclosure): if (self.enclosure):
v.update({'enclosure': self.enclosure}) v.update({
'enclosure': self.enclosure,
'has_mainimg': self.has_mainimg
})
return v return v
@property @property
@ -488,6 +559,14 @@ class Singular(MarkdownDoc):
self.content, self.content,
]) ])
#async def update(self):
#fm = frontmatter.loads('')
#fm.metadata = self.meta
#fm.content = self.content
#with open(fpath, 'wt') as f:
#settings.logger.info("updating %s", fpath)
#f.write(frontmatter.dumps(fm))
async def copyfiles(self): async def copyfiles(self):
copystatics( os.path.dirname(self.fpath)) copystatics( os.path.dirname(self.fpath))
@ -501,7 +580,6 @@ class Singular(MarkdownDoc):
'meta': settings.meta, 'meta': settings.meta,
'licence': settings.licence, 'licence': settings.licence,
'tips': settings.tips, 'tips': settings.tips,
'labels': settings.labels
}) })
if not os.path.isdir(self.renderdir): if not os.path.isdir(self.renderdir):
settings.logger.info("creating directory: %s", self.renderdir) settings.logger.info("creating directory: %s", self.renderdir)
@ -530,7 +608,7 @@ class WebImage(object):
self.Resized(self, max(self.width, self.height)) self.Resized(self, max(self.width, self.height))
)) ))
def __str__(self): def mkstring(self, is_mainimg=False):
if len(self.mdimg.css): if len(self.mdimg.css):
return self.mdimg.match return self.mdimg.match
tmpl = J2.get_template("%s.j2.html" % (self.__class__.__name__)) tmpl = J2.get_template("%s.j2.html" % (self.__class__.__name__))
@ -543,6 +621,7 @@ class WebImage(object):
'caption': self.caption, 'caption': self.caption,
'exif': self.exif, 'exif': self.exif,
'is_photo': self.is_photo, 'is_photo': self.is_photo,
'is_mainimg': is_mainimg
}) })
@cached_property @cached_property
@ -847,18 +926,143 @@ class AsyncWorker(object):
def run(self): def run(self):
self._loop.run_until_complete(asyncio.wait(self._tasks)) self._loop.run_until_complete(asyncio.wait(self._tasks))
class PHPFile(object):
class IndexPHP(object): @property
def __init__(self): def exists(self):
self.gone = {} if settings.args.get('force'):
self.redirect = {} return False
if not os.path.exists(self.renderfile):
return False
if self.mtime > os.path.getmtime(self.renderfile):
return False
return True
@property @property
def mtime(self): def mtime(self):
r = 0 return os.path.getmtime(
if os.path.exists(self.renderfile): os.path.join(
r = os.path.getmtime(self.renderfile) settings.paths.get('tmpl'),
return r self.templatefile
)
)
@property
def renderfile(self):
raise ValueError('Not implemented')
@property
def templatefile(self):
raise ValueError('Not implemented')
async def render(self):
if self.exists:
return
await self._render()
class Search(PHPFile):
def __init__(self):
self.fpath = os.path.join(
settings.paths.get('build'),
'search.sqlite'
)
self.db = sqlite3.connect(self.fpath)
self.db.execute('PRAGMA auto_vacuum = INCREMENTAL;')
self.db.execute('PRAGMA journal_mode = MEMORY;')
self.db.execute('PRAGMA temp_store = MEMORY;')
self.db.execute('PRAGMA locking_mode = NORMAL;')
self.db.execute('PRAGMA synchronous = FULL;')
self.db.execute('PRAGMA encoding = "UTF-8";')
self.db.execute('''
CREATE VIRTUAL TABLE IF NOT EXISTS data USING fts4(
url,
mtime,
name,
title,
category,
content,
notindexed=category,
notindexed=url,
notindexed=mtime,
tokenize=porter
)'''
)
def __exit__(self):
self.db.commit()
self.db.execute('PRAGMA auto_vacuum;')
self.db.close()
def check(self, name):
ret = 0
maybe = self.db.execute('''
SELECT
mtime
FROM
data
WHERE
name = ?
''', (name,)).fetchone()
if maybe:
ret = int(maybe[0])
return ret
def append(self, url, mtime, name, title, category, content):
mtime = int(mtime)
check = self.check(name)
if (check and check < mtime):
self.db.execute('''
DELETE
FROM
data
WHERE
name=?''', (name,))
check = False
if not check:
self.db.execute('''
INSERT INTO
data
(url, mtime, name, title, category, content)
VALUES
(?,?,?,?,?,?);
''', (
url,
mtime,
name,
title,
category,
content
))
@property
def renderfile(self):
return os.path.join(
settings.paths.get('build'),
'search.php'
)
@property
def templatefile(self):
return 'Search.j2.php'
async def _render(self):
r = J2.get_template(self.templatefile).render({
'post': {},
'site': settings.site,
'author': settings.author,
'meta': settings.meta,
'licence': settings.licence,
'tips': settings.tips,
})
with open(self.renderfile, 'wt') as f:
settings.logger.info("rendering to %s", self.renderfile)
f.write(r)
class IndexPHP(PHPFile):
def __init__(self):
self.gone = {}
self.redirect = {}
def add_gone(self, uri): def add_gone(self, uri):
self.gone[uri] = True self.gone[uri] = True
@ -878,8 +1082,12 @@ class IndexPHP(object):
'index.php' 'index.php'
) )
async def render(self): @property
r = J2.get_template('Index.j2.php').render({ def templatefile(self):
return 'Index.j2.php'
async def _render(self):
r = J2.get_template(self.templatefile).render({
'post': {}, 'post': {},
'site': settings.site, 'site': settings.site,
'gones': self.gone, 'gones': self.gone,
@ -890,7 +1098,7 @@ class IndexPHP(object):
f.write(r) f.write(r)
class WebhookPHP(object): class WebhookPHP(PHPFile):
@property @property
def renderfile(self): def renderfile(self):
return os.path.join( return os.path.join(
@ -898,8 +1106,12 @@ class WebhookPHP(object):
'webhook.php' 'webhook.php'
) )
async def render(self): @property
r = J2.get_template('Webhook.j2.php').render({ def templatefile(self):
return 'Webhook.j2.php'
async def _render(self):
r = J2.get_template(self.templatefile).render({
'author': settings.author, 'author': settings.author,
'callback_secret': keys.webmentionio.get('callback_secret'), 'callback_secret': keys.webmentionio.get('callback_secret'),
}) })
@ -1084,7 +1296,6 @@ class Category(dict):
'meta': settings.meta, 'meta': settings.meta,
'licence': settings.licence, 'licence': settings.licence,
'tips': settings.tips, 'tips': settings.tips,
'labels': settings.labels,
'category': self.tmplvars, 'category': self.tmplvars,
'pages': { 'pages': {
'current': pagenum, 'current': pagenum,
@ -1119,103 +1330,6 @@ class Category(dict):
await self.render_feed() await self.render_feed()
class Search(object):
def __init__(self):
self.changed = False
self.fpath = os.path.join(
settings.paths.get('build'),
'search.sqlite'
)
self.db = sqlite3.connect(self.fpath)
self.db.execute('PRAGMA auto_vacuum = INCREMENTAL;')
self.db.execute('PRAGMA journal_mode = MEMORY;')
self.db.execute('PRAGMA temp_store = MEMORY;')
self.db.execute('PRAGMA locking_mode = NORMAL;')
self.db.execute('PRAGMA synchronous = FULL;')
self.db.execute('PRAGMA encoding = "UTF-8";')
self.db.execute('''
CREATE VIRTUAL TABLE IF NOT EXISTS data USING fts4(
url,
mtime,
name,
title,
category,
content,
notindexed=category,
notindexed=url,
notindexed=mtime,
tokenize=porter
)'''
)
def __exit__(self):
if (self.changed):
self.db.commit()
self.db.execute('PRAGMA auto_vacuum;')
self.db.close()
def exists(self, name):
ret = 0
maybe = self.db.execute('''
SELECT
mtime
FROM
data
WHERE
name = ?
''', (name,)).fetchone()
if maybe:
ret = int(maybe[0])
return ret
def append(self, url, mtime, name, title, category, content):
mtime = int(mtime)
exists = self.exists(name)
if (exists and exists < mtime):
self.db.execute('''
DELETE
FROM
data
WHERE
name=?''', (name,))
exists = False
if not exists:
self.db.execute('''
INSERT INTO
data
(url, mtime, name, title, category, content)
VALUES
(?,?,?,?,?,?);
''', (
url,
mtime,
name,
title,
category,
content
))
self.changed = True
async def render(self):
target = os.path.join(
settings.paths.get('build'),
'search.php'
)
if os.path.exists(target):
return
r = J2.get_template('Search.j2.php').render({
'post': {},
'site': settings.site,
'author': settings.author,
'meta': settings.meta,
'licence': settings.licence,
'tips': settings.tips,
'labels': settings.labels
})
with open(target, 'wt') as f:
settings.logger.info("rendering to %s", target)
f.write(r)
class Sitemap(dict): class Sitemap(dict):
@property @property
def mtime(self): def mtime(self):
@ -1280,11 +1394,7 @@ def mkcomment(webmention):
fdir, fdir,
"%d-%s.md" % ( "%d-%s.md" % (
dt.timestamp, dt.timestamp,
slugify( url2slug(webmention.get('source'))
re.sub(r"^https?://(?:www)?", "", webmention.get('source')),
only_ascii=True,
lower=True
)[:200]
) )
) )
@ -1336,6 +1446,14 @@ def makecomments():
pass pass
def url2slug(url, limit=200):
return slugify(
re.sub(r"^https?://(?:www)?", "", url),
only_ascii=True,
lower=True
)[:limit]
def make(): def make():
start = int(round(time.time() * 1000)) start = int(round(time.time() * 1000))
last = 0 last = 0
@ -1344,8 +1462,9 @@ def make():
content = settings.paths.get('content') content = settings.paths.get('content')
worker = AsyncWorker() worker = AsyncWorker()
rules = IndexPHP() webmentions = AsyncWorker()
rules = IndexPHP()
for e in glob.glob(os.path.join(content, '*', '*.ptr')): for e in glob.glob(os.path.join(content, '*', '*.ptr')):
post = Gone(e) post = Gone(e)
if post.mtime > last: if post.mtime > last:
@ -1356,16 +1475,11 @@ def make():
if post.mtime > last: if post.mtime > last:
last = post.mtime last = post.mtime
rules.add_redirect(post.source, post.target) rules.add_redirect(post.source, post.target)
worker.add(rules.render())
if rules.mtime < last or settings.args.get('force'):
worker.add(rules.render())
webhook = WebhookPHP() webhook = WebhookPHP()
worker.add(webhook.render()) worker.add(webhook.render())
if rules.mtime < last or settings.args.get('force'):
worker.add(rules.render())
sitemap = Sitemap() sitemap = Sitemap()
search = Search() search = Search()
categories = {} categories = {}
@ -1375,6 +1489,9 @@ def make():
post = Singular(e) post = Singular(e)
for i in post.images.values(): for i in post.images.values():
worker.add(i.downsize()) worker.add(i.downsize())
for i in post.to_ping:
webmentions.add(i.send())
worker.add(post.render()) worker.add(post.render())
worker.add(post.copyfiles()) worker.add(post.copyfiles())
if post.is_future: if post.is_future:
@ -1400,11 +1517,10 @@ def make():
search.__exit__() search.__exit__()
worker.add(search.render()) worker.add(search.render())
worker.add(sitemap.render())
for category in categories.values(): for category in categories.values():
worker.add(category.render()) worker.add(category.render())
worker.add(sitemap.render())
worker.run() worker.run()
settings.logger.info('worker finished') settings.logger.info('worker finished')
@ -1427,7 +1543,17 @@ def make():
end = int(round(time.time() * 1000)) end = int(round(time.time() * 1000))
settings.logger.info('process took %d ms' % (end - start)) settings.logger.info('process took %d ms' % (end - start))
settings.logger.info('starting syncing')
os.system(
"rsync -avuhH --delete-after %s/ %s/" % (
settings.paths.get('build'),
settings.syncserver
)
)
settings.logger.info('syncing finished')
settings.logger.info('sending webmentions')
webmentions.run()
settings.logger.info('sending webmentions finished')
if __name__ == '__main__': if __name__ == '__main__':
make() make()

View file

@ -1,23 +1,10 @@
arrow==0.12.1 arrow==0.12.1
bleach==2.1.3 bleach==2.1.3
certifi==2018.4.16
chardet==3.0.4
emoji==0.5.0 emoji==0.5.0
feedgen==0.7.0 feedgen==0.7.0
html5lib==1.0.1
idna==2.7
Jinja2==2.10 Jinja2==2.10
langdetect==1.0.7 langdetect==1.0.7
lxml==4.2.4
MarkupSafe==1.0
pkg-resources==0.0.0
python-dateutil==2.7.3
python-frontmatter==0.4.2 python-frontmatter==0.4.2
PyYAML==3.13
requests==2.19.1 requests==2.19.1
six==1.11.0
unicode-slugify==0.1.3 unicode-slugify==0.1.3
Unidecode==1.0.22
urllib3==1.23
Wand==0.4.4 Wand==0.4.4
webencodings==0.5.1

2
run
View file

@ -3,5 +3,5 @@
set -euo pipefail set -euo pipefail
IFS=$'\n\t' IFS=$'\n\t'
source ./.venv/bin/activate #source ./.venv/bin/activate
python3 nasg.py "$@" python3 nasg.py "$@"

View file

@ -4,6 +4,7 @@ import argparse
import logging import logging
base = os.path.abspath(os.path.expanduser('~/Projects/petermolnar.net')) base = os.path.abspath(os.path.expanduser('~/Projects/petermolnar.net'))
syncserver = 'liveserver:/web/petermolnar.net/web'
site = { site = {
'title': 'Peter Molnar', 'title': 'Peter Molnar',
@ -16,10 +17,6 @@ site = {
'journal' 'journal'
], ],
'licence': 'by-nc-nd', 'licence': 'by-nc-nd',
'piwik': {
'domain': 'stats.petermolnar.net',
'id': 1
}
} }
categorydisplay = { categorydisplay = {
@ -33,25 +30,12 @@ licence = {
'journal': 'by-nc', 'journal': 'by-nc',
} }
labels = {
'tiptext': {
'photo':
"Did you like this photo?<br />"
"Leave a tip! If you're interested in prints, please get in touch.",
'article':
"Did you find this article useful?<br />"
"Support me, so I can write more like this.<br />"
"If you want my help for your project, get in touch.",
'journal':
"Did you like this entry?<br />"
"Encourage me to write more of them.",
}
}
meta = { meta = {
'webmention': 'https://webmention.io/petermolnar.net/webmention', 'webmention': 'https://webmention.io/petermolnar.net/webmention',
'pingback': 'https://webmention.io/petermolnar.net/xmlrpc', 'pingback': 'https://webmention.io/petermolnar.net/xmlrpc',
'hub': 'https://petermolnar.superfeedr.com/' 'hub': 'https://petermolnar.superfeedr.com/',
'authorization_endpoint': 'https://indieauth.com/auth',
'token_endpoint': 'https://tokens.indieauth.com/token',
} }
author = { author = {
@ -61,14 +45,18 @@ author = {
'avatar': 'https://petermolnar.net/molnar_peter_avatar.jpg', 'avatar': 'https://petermolnar.net/molnar_peter_avatar.jpg',
'gpg': 'https://petermolnar.net/pgp.asc', 'gpg': 'https://petermolnar.net/pgp.asc',
'cv': 'https://petermolnar.net/about.html', 'cv': 'https://petermolnar.net/about.html',
'xmpp': 'mail@petermolnar.net', 'contact': {
'flickr': 'petermolnareu', 'xmpp': 'xmpp:mail@petermolnar.net',
'github': 'petermolnar', 'tumblr': 'https://petermolnarnet.tumblr.com/',
'twitter': 'petermolnar' 'wordpress': 'https://petermolnareu.wordpress.com/',
'flickr': 'https://flickr.com/people/petermolnareu',
'github': 'https://github.com/petermolnar',
}
} }
paths = { paths = {
'content': os.path.join(base, 'content'), 'content': os.path.join(base, 'content'),
'webmentions': os.path.join(base, 'content', 'webmentions'),
'tmpl': os.path.join(base, 'nasg', 'templates'), 'tmpl': os.path.join(base, 'nasg', 'templates'),
'watermark': os.path.join(base, 'nasg', 'templates', 'watermark.png'), 'watermark': os.path.join(base, 'nasg', 'templates', 'watermark.png'),
'build': os.path.join(base, 'www'), 'build': os.path.join(base, 'www'),
@ -85,20 +73,10 @@ photo = {
}, },
} }
tips = [ tips = {
{ 'paypal': 'https://paypal.me/petermolnar/3GBP',
'name': 'paypal', 'monzo': 'https://monzo.me/petermolnar/3',
'label': 'PayPal', }
'value': '£3',
'url': 'https://paypal.me/petermolnar/3GBP',
},
{
'name': 'monzo',
'label': 'Monzo (UK)',
'value': '£3',
'url': 'https://monzo.me/petermolnar/3',
},
]
dateformat = { dateformat = {
'iso': 'YYYY-MM-DDTHH:mm:ssZZ', 'iso': 'YYYY-MM-DDTHH:mm:ssZZ',

6
sync
View file

@ -1,6 +0,0 @@
#!/usr/bin/env bash
set -euo pipefail
IFS=$'\n\t'
rsync -avuhH --delete-after ../www/ liveserver:/web/petermolnar.net/web/

View file

@ -19,26 +19,26 @@
{% endif %} {% endif %}
{% set _ = year.append(post.year)%} {% set _ = year.append(post.year)%}
<article class="h-entry hentry singular" lang="{{ post.lang }}" itemprop="blogPost" itemscope="" itemtype="http://schema.org/BlogPosting" itemref="site-publisher"> <article class="h-entry hentry singular" lang="{{ post.lang }}">
<header> <header>
{% if category.display == 'flat' %} {% if category.display == 'flat' %}
<h3 itemprop="name headline" class="p-name entry-title"> <h3 class="p-name entry-title">
{% else %} {% else %}
<h2 itemprop="name headline" class="p-name entry-title"> <h2 class="p-name entry-title">
{% endif %} {% endif %}
{% if post.is_reply %} {% if post.is_reply %}
<svg class="icon" width="16" height="16"> <svg class="icon" width="16" height="16">
<use xlink:href="#icon-reply" /> <use xlink:href="#icon-reply" />
</svg> </svg>
<a href="{{ post.url }}/" class="u-url bookmark" itemprop="url mainEntityOfPage"> <a href="{{ post.url }}/" class="u-url u-uuid" rel="bookmark">
RE: RE:
</a> </a>
<a href="{{ post.is_reply }}" class="u-in-reply-to"> <a href="{{ post.is_reply }}" class="u-in-reply-to">
{{ post.is_reply }} {{ post.is_reply }}
</a> </a>
{% else %} {% else %}
<a href="{{ post.url }}" title="{{ post.title }}" class="u-url bookmark" itemprop="url mainEntityOfPage"> <a href="{{ post.url }}" title="{{ post.title }}" rel="bookmark" class="u-url u-uuid">
<span class="entry-title p-name">{{ post.title }}</span> {{ post.title }}
</a> </a>
{% endif %} {% endif %}
{% if category.display == 'flat' %} {% if category.display == 'flat' %}
@ -49,7 +49,7 @@
</header> </header>
{% if post.summary %} {% if post.summary %}
<div class="e-summary entry-summary" itemprop="description"> <div class="e-summary entry-summary">
{{ post.html_summary }} {{ post.html_summary }}
<p class="more"> <p class="more">
<a href="{{ post.url }}" title="{{ post.title }}"> <a href="{{ post.url }}" title="{{ post.title }}">
@ -58,7 +58,7 @@
</p> </p>
</div> </div>
{% else %} {% else %}
<div class="e-content entry-content" itemprop="articleBody"> <div class="e-content entry-content">
{{ post.html_content }} {{ post.html_content }}
</div> </div>
{% endif %} {% endif %}

View file

@ -2,7 +2,7 @@
{% if href != src %} {% if href != src %}
<a href="{{ href }}"> <a href="{{ href }}">
{% endif %} {% endif %}
<img src="{{ src }}" title="{{ title }}" alt="" width="{{ width }}" height="{{ height }}" {% if is_mainimg %}itemprop="image" class="u-featured"{% endif %} /> <img src="{{ src }}" title="{{ title }}" alt="" width="{{ width }}" height="{{ height }}"{% if is_mainimg %} class="u-featured"{% endif %} />
{% if href != src %} {% if href != src %}
</a> </a>
{% endif %} {% endif %}

View file

@ -13,5 +13,21 @@ if(! isset($payload['secret']) || $payload['secret'] != '{{ callback_secret }}'
die('Bad Request'); die('Bad Request');
} }
mail("{{ author.email }}", "[webmention] {$payload['source']}", $raw); $msg = sprintf('
Type: %s
Source: %s
Target: %s
From: %s
%s
',
$payload['post']['wm-property'],
$payload['source'],
$payload['target'],
$payload['post']['author']['name'],
$payload['post']['content']['text']
);
mail("{{ author.email }}", "[webmention] {$payload['source']}", $msg);
header('HTTP/1.1 202 Accepted'); header('HTTP/1.1 202 Accepted');

View file

@ -36,7 +36,7 @@
} }
</script> </script>
</head> </head>
<body itemscope="" itemtype="http://schema.org/Blog http://schema.org/WebPage"> <body>
{% macro activemenu(name) %}{% if (post is defined and post.category == name ) or ( category is defined and category.name == name ) %}active{% endif %}{% endmacro %} {% macro activemenu(name) %}{% if (post is defined and post.category == name ) or ( category is defined and category.name == name ) %}active{% endif %}{% endmacro %}
@ -103,9 +103,9 @@
{% block content %} {% block content %}
<main> <main>
<article class="h-entry hentry singular" lang="{{ post.lang }}" itemprop="blogPost" itemscope="" itemtype="http://schema.org/BlogPosting" itemref="site-publisher"> <article class="h-entry hentry singular" lang="{{ post.lang }}">
<header> <header>
<h1 class="entry-title p-name" itemprop="name headline"> <h1 class="entry-title p-name">
{% if post.is_reply %} {% if post.is_reply %}
<span> <span>
<svg width="16" height="16"> <svg width="16" height="16">
@ -128,35 +128,32 @@
{% if post.review %} {% if post.review %}
<hr/> <hr/>
<div class="h-review hreview" itemprop="review" itemscope="" itemtype="http://schema.org/Review"> <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.title }}</a></strong> <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>
<p> <p>
By By
<span class="p-author h-card vcard reviewer" itemprop="author" itemscope="" itemtype="http://schema.org/Person"> <span class="p-author h-card vcard reviewer">
<a class="fn p-name url u-url u-uid" href="{{ author.url }}" itemprop="url"> <a class="fn p-name url u-url u-uid" href="{{ author.url }}">{{ author.name }}</a></span> at <time class="dt-published dtreviewed" datetime="{{ post.pubtime }}">{{ post.pubdate }}</time>
<span itemprop="name">{{ author.name }}</span>
</a></span> at <time class="dt-published dtreviewed" datetime="{{ post.pubtime }}" itemprop="datePublished">{{ post.pubdate }}</time>
</p> </p>
<p> <p>
<span class="rating" itemprop="reviewRating" itemscope itemtype="http://schema.org/Rating"> <span class="rating">
<meta itemprop="worstRating" content = "1"> <span class="value">{{ post.review.rating }}</span>
<span class="value" itemprop="ratingValue">{{ post.review.rating }}</span>
out of out of
<span class="best" itemprop="bestRating">5</span> <span class="best">5</span>
</span> </span>
</p> </p>
<p class="p-summary summary" itemprop="reviewBody">{{ post.review.summary }}</p> <p class="p-summary summary">{{ post.review.summary }}</p>
</div> </div>
<hr/> <hr/>
{% endif %} {% endif %}
{% if post.summary %} {% if post.summary %}
<div class="e-summary entry-summary" itemprop="description"> <div class="e-summary entry-summary">
{{ post.html_summary }} {{ post.html_summary }}
</div> </div>
{% endif %} {% endif %}
<div class="e-content entry-content" itemprop="articleBody"> <div class="e-content entry-content">
{{ post.html_content }} {{ post.html_content }}
</div> </div>
@ -167,35 +164,27 @@
<dd class="published"> <dd class="published">
<time class="dt-published" <time class="dt-published"
datetime="{{ post.pubtime }}" datetime="{{ post.pubtime }}"
itemprop="dateModified datePublished"
>{{ post.pubdate }}</time> >{{ post.pubdate }}</time>
</dd> </dd>
<dt>Author</dt> <dt>Author</dt>
<dd> <dd>
<p class="p-author h-card vcard" itemprop="author" itemscope="" itemtype="http://schema.org/Person"> <p class="p-author h-card vcard">
<img class="photo avatar u-photo u-avatar" <img class="photo avatar u-photo u-avatar"
src="{{ author.avatar }}" src="{{ author.avatar }}"
alt="Photo of {{ author.name }}" alt="Photo of {{ author.name }}"
itemprop="image" /> />
<a class="fn p-name url u-url u-uid" <a class="fn p-name url u-url u-uid"
href="{{ author.url }}" href="{{ author.url }}"
rel="author" rel="author"
itemprop="url"> >{{ author.name }}</a>
<span itemprop="name">{{ author.name }}</span> &lt;<a class="u-email email" href="mailto:{{ author.email }}">{{ author.email }}</a>&gt;
</a>
<a class="u-email email" href="mailto:{{ author.email }}">
<span itemprop="email">{{ author.email }}</span>
</a>
</p> </p>
</dd> </dd>
<dt>Entry URL</dt> <dt>Entry URL</dt>
<dd> <dd>
{% if not post.has_mainimg %} <a class="u-url u-uuid" rel="bookmark" href="{{ post.url }}">
<img aria-hidden="true" src="{{ author.avatar }}" itemprop="image" />
{% endif %}
<a class="u-url u-uuid" rel="bookmark" href="{{ post.url }}" itemprop="url mainEntityOfPage">
{{ post.url }} {{ post.url }}
</a> </a>
</dd> </dd>
@ -203,7 +192,7 @@
<dt>License</dt> <dt>License</dt>
<dd class="license"> <dd class="license">
{% if post.licence == 'by' %} {% if post.licence == 'by' %}
<a rel="license" href="https://creativecommons.org/licenses/by/4.0/" class="u-license" itemprop="license">CC BY 4.0</a> <a rel="license" href="https://creativecommons.org/licenses/by/4.0/" class="u-license">CC BY 4.0</a>
<ul> <ul>
<li>you can share it</li> <li>you can share it</li>
<li>you can republish it</li> <li>you can republish it</li>
@ -212,7 +201,7 @@
<li>you always need to make a link back here</li> <li>you always need to make a link back here</li>
</ul> </ul>
{% elif post.licence.text == 'by-nc' %} {% elif post.licence.text == 'by-nc' %}
<a rel="license" href="https://creativecommons.org/licenses/by-nc/4.0/" class="u-license" itemprop="license">CC BY-NC 4.0</a> <a rel="license" href="https://creativecommons.org/licenses/by-nc/4.0/" class="u-license">CC BY-NC 4.0</a>
<ul> <ul>
<li>you can share it</li> <li>you can share it</li>
<li>you can republish it</li> <li>you can republish it</li>
@ -222,7 +211,7 @@
</ul> </ul>
For commercial use, please contact me. For commercial use, please contact me.
{% else %} {% else %}
<a rel="license" href="https://creativecommons.org/licenses/by-nc-nd/4.0/" class="u-license" itemprop="license">CC BY-NC-ND 4.0</a> <a rel="license" href="https://creativecommons.org/licenses/by-nc-nd/4.0/" class="u-license">CC BY-NC-ND 4.0</a>
<ul> <ul>
<li>you can share it</li> <li>you can share it</li>
<li>you can't modify it</li> <li>you can't modify it</li>
@ -332,27 +321,21 @@
{% endblock %} {% endblock %}
<footer class="p-author h-card vcard" id="site-publisher" itemprop="publisher" itemscope="" itemtype="http://schema.org/Organization"> <footer class="p-author h-card vcard">
<p> <p>
<a href="https://creativecommons.org/">CC</a>, <a href="https://creativecommons.org/">CC</a>,
1999-2018, 1999-2018,
<span itemprop="logo" itemscope itemtype="https://schema.org/ImageObject">
<img class="photo avatar u-photo u-avatar" <img class="photo avatar u-photo u-avatar"
src="{{ author.avatar }}" src="{{ author.avatar }}"
alt="Photo of {{ author.name }}" alt="Photo of {{ author.name }}" />
itemprop="url" /> <a class="fn p-name url u-url u-uid" rel="me" href="{{ site.url }}/about.html">{{ author.name }}</a>
</span> &lt;<a rel="me" class="u-email email" href="mailto:{{ author.email }}">{{ author.email }}</a>&gt;
<a class="fn p-name url u-url u-uid" rel="me" href="{{ site.url }}/about.html" itemprop="url">
<span itemprop="name">{{ author.name }}</span></a>
<a rel="me" class="u-email email" href="mailto:{{ author.email }}">
<span itemprop="email">{{ author.email }}</span>
</a>
</p> </p>
<nav> <nav>
<ul> <ul>
{% for name, value in author.contact.items() %} {% for name, value in author.contact.items() %}
<li> <li>
<a class="url u-url x-{{name}}" rel="me" href="{{ value }}" itemprop="sameAs"> <a class="url u-url x-{{name}}" rel="me" href="{{ value }}">
<svg width="16" height="16"> <svg width="16" height="16">
<use xlink:href="#icon-{{ name }}"></use> <use xlink:href="#icon-{{ name }}"></use>
</svg> </svg>