- 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:
parent
9c749f4591
commit
55cc0f3307
11 changed files with 331 additions and 247 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -1,5 +1,5 @@
|
|||
__pycache__
|
||||
_scratch
|
||||
keys.py
|
||||
nasg.proj
|
||||
.idea
|
||||
.venv
|
||||
|
|
BIN
assets/icomoon-2018-08-13.zip
Normal file
BIN
assets/icomoon-2018-08-13.zip
Normal file
Binary file not shown.
398
nasg.py
398
nasg.py
|
@ -41,6 +41,8 @@ MarkdownImage = namedtuple(
|
|||
['match', 'alt', 'fname', 'title', 'css']
|
||||
)
|
||||
|
||||
REPLY_TYPES = ['webmention', 'in-reply-to', 'reply']
|
||||
|
||||
J2 = jinja2.Environment(
|
||||
loader=jinja2.FileSystemLoader(searchpath=settings.paths.get('tmpl')),
|
||||
lstrip_blocks=True,
|
||||
|
@ -78,6 +80,58 @@ class cached_property(object):
|
|||
setattr(inst, self.name, 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):
|
||||
@cached_property
|
||||
|
@ -95,12 +149,19 @@ class MarkdownDoc(object):
|
|||
def content(self):
|
||||
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
|
||||
def html_content(self):
|
||||
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))
|
||||
c = c.replace(match, img.mkstring(self.has_mainimg))
|
||||
# return MD.reset().convert(c)
|
||||
c = Pandoc(c)
|
||||
c = RE_PRECODE.sub('<pre><code lang="\g<1>" class="language-\g<1>">', c)
|
||||
|
@ -337,8 +398,6 @@ class Singular(MarkdownDoc):
|
|||
@property
|
||||
def syndicate(self):
|
||||
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:
|
||||
urls.append("https://brid.gy/publish/flickr")
|
||||
return urls
|
||||
|
@ -375,6 +434,14 @@ class Singular(MarkdownDoc):
|
|||
return True
|
||||
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
|
||||
def licence(self):
|
||||
if self.category in settings.licence:
|
||||
|
@ -404,15 +471,16 @@ class Singular(MarkdownDoc):
|
|||
def replies(self):
|
||||
r = OrderedDict()
|
||||
for mtime, c in self.comments.items():
|
||||
if c.type in ['webmention', 'in-reply-to']:
|
||||
r[mtime] = c.tmplvars
|
||||
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 ['webmention', 'in-reply-to']:
|
||||
if c.type in REPLY_TYPES:
|
||||
continue
|
||||
t = "%s" % (c.type)
|
||||
if t not in r:
|
||||
|
@ -450,7 +518,10 @@ class Singular(MarkdownDoc):
|
|||
'has_code': self.has_code,
|
||||
}
|
||||
if (self.enclosure):
|
||||
v.update({'enclosure': self.enclosure})
|
||||
v.update({
|
||||
'enclosure': self.enclosure,
|
||||
'has_mainimg': self.has_mainimg
|
||||
})
|
||||
return v
|
||||
|
||||
@property
|
||||
|
@ -488,6 +559,14 @@ class Singular(MarkdownDoc):
|
|||
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):
|
||||
copystatics( os.path.dirname(self.fpath))
|
||||
|
||||
|
@ -501,7 +580,6 @@ class Singular(MarkdownDoc):
|
|||
'meta': settings.meta,
|
||||
'licence': settings.licence,
|
||||
'tips': settings.tips,
|
||||
'labels': settings.labels
|
||||
})
|
||||
if not os.path.isdir(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))
|
||||
))
|
||||
|
||||
def __str__(self):
|
||||
def mkstring(self, is_mainimg=False):
|
||||
if len(self.mdimg.css):
|
||||
return self.mdimg.match
|
||||
tmpl = J2.get_template("%s.j2.html" % (self.__class__.__name__))
|
||||
|
@ -543,6 +621,7 @@ class WebImage(object):
|
|||
'caption': self.caption,
|
||||
'exif': self.exif,
|
||||
'is_photo': self.is_photo,
|
||||
'is_mainimg': is_mainimg
|
||||
})
|
||||
|
||||
@cached_property
|
||||
|
@ -847,18 +926,143 @@ class AsyncWorker(object):
|
|||
def run(self):
|
||||
self._loop.run_until_complete(asyncio.wait(self._tasks))
|
||||
|
||||
|
||||
class IndexPHP(object):
|
||||
def __init__(self):
|
||||
self.gone = {}
|
||||
self.redirect = {}
|
||||
class PHPFile(object):
|
||||
@property
|
||||
def exists(self):
|
||||
if settings.args.get('force'):
|
||||
return False
|
||||
if not os.path.exists(self.renderfile):
|
||||
return False
|
||||
if self.mtime > os.path.getmtime(self.renderfile):
|
||||
return False
|
||||
return True
|
||||
|
||||
@property
|
||||
def mtime(self):
|
||||
r = 0
|
||||
if os.path.exists(self.renderfile):
|
||||
r = os.path.getmtime(self.renderfile)
|
||||
return r
|
||||
return os.path.getmtime(
|
||||
os.path.join(
|
||||
settings.paths.get('tmpl'),
|
||||
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):
|
||||
self.gone[uri] = True
|
||||
|
@ -878,8 +1082,12 @@ class IndexPHP(object):
|
|||
'index.php'
|
||||
)
|
||||
|
||||
async def render(self):
|
||||
r = J2.get_template('Index.j2.php').render({
|
||||
@property
|
||||
def templatefile(self):
|
||||
return 'Index.j2.php'
|
||||
|
||||
async def _render(self):
|
||||
r = J2.get_template(self.templatefile).render({
|
||||
'post': {},
|
||||
'site': settings.site,
|
||||
'gones': self.gone,
|
||||
|
@ -890,7 +1098,7 @@ class IndexPHP(object):
|
|||
f.write(r)
|
||||
|
||||
|
||||
class WebhookPHP(object):
|
||||
class WebhookPHP(PHPFile):
|
||||
@property
|
||||
def renderfile(self):
|
||||
return os.path.join(
|
||||
|
@ -898,8 +1106,12 @@ class WebhookPHP(object):
|
|||
'webhook.php'
|
||||
)
|
||||
|
||||
async def render(self):
|
||||
r = J2.get_template('Webhook.j2.php').render({
|
||||
@property
|
||||
def templatefile(self):
|
||||
return 'Webhook.j2.php'
|
||||
|
||||
async def _render(self):
|
||||
r = J2.get_template(self.templatefile).render({
|
||||
'author': settings.author,
|
||||
'callback_secret': keys.webmentionio.get('callback_secret'),
|
||||
})
|
||||
|
@ -1084,7 +1296,6 @@ class Category(dict):
|
|||
'meta': settings.meta,
|
||||
'licence': settings.licence,
|
||||
'tips': settings.tips,
|
||||
'labels': settings.labels,
|
||||
'category': self.tmplvars,
|
||||
'pages': {
|
||||
'current': pagenum,
|
||||
|
@ -1119,103 +1330,6 @@ class Category(dict):
|
|||
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):
|
||||
@property
|
||||
def mtime(self):
|
||||
|
@ -1280,11 +1394,7 @@ def mkcomment(webmention):
|
|||
fdir,
|
||||
"%d-%s.md" % (
|
||||
dt.timestamp,
|
||||
slugify(
|
||||
re.sub(r"^https?://(?:www)?", "", webmention.get('source')),
|
||||
only_ascii=True,
|
||||
lower=True
|
||||
)[:200]
|
||||
url2slug(webmention.get('source'))
|
||||
)
|
||||
)
|
||||
|
||||
|
@ -1336,6 +1446,14 @@ def makecomments():
|
|||
pass
|
||||
|
||||
|
||||
def url2slug(url, limit=200):
|
||||
return slugify(
|
||||
re.sub(r"^https?://(?:www)?", "", url),
|
||||
only_ascii=True,
|
||||
lower=True
|
||||
)[:limit]
|
||||
|
||||
|
||||
def make():
|
||||
start = int(round(time.time() * 1000))
|
||||
last = 0
|
||||
|
@ -1344,8 +1462,9 @@ def make():
|
|||
|
||||
content = settings.paths.get('content')
|
||||
worker = AsyncWorker()
|
||||
rules = IndexPHP()
|
||||
webmentions = AsyncWorker()
|
||||
|
||||
rules = IndexPHP()
|
||||
for e in glob.glob(os.path.join(content, '*', '*.ptr')):
|
||||
post = Gone(e)
|
||||
if post.mtime > last:
|
||||
|
@ -1356,16 +1475,11 @@ def make():
|
|||
if post.mtime > last:
|
||||
last = post.mtime
|
||||
rules.add_redirect(post.source, post.target)
|
||||
|
||||
if rules.mtime < last or settings.args.get('force'):
|
||||
worker.add(rules.render())
|
||||
worker.add(rules.render())
|
||||
|
||||
webhook = WebhookPHP()
|
||||
worker.add(webhook.render())
|
||||
|
||||
if rules.mtime < last or settings.args.get('force'):
|
||||
worker.add(rules.render())
|
||||
|
||||
sitemap = Sitemap()
|
||||
search = Search()
|
||||
categories = {}
|
||||
|
@ -1375,6 +1489,9 @@ def make():
|
|||
post = Singular(e)
|
||||
for i in post.images.values():
|
||||
worker.add(i.downsize())
|
||||
for i in post.to_ping:
|
||||
webmentions.add(i.send())
|
||||
|
||||
worker.add(post.render())
|
||||
worker.add(post.copyfiles())
|
||||
if post.is_future:
|
||||
|
@ -1400,11 +1517,10 @@ def make():
|
|||
|
||||
search.__exit__()
|
||||
worker.add(search.render())
|
||||
worker.add(sitemap.render())
|
||||
for category in categories.values():
|
||||
worker.add(category.render())
|
||||
|
||||
worker.add(sitemap.render())
|
||||
|
||||
worker.run()
|
||||
settings.logger.info('worker finished')
|
||||
|
||||
|
@ -1427,7 +1543,17 @@ def make():
|
|||
|
||||
end = int(round(time.time() * 1000))
|
||||
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__':
|
||||
make()
|
||||
|
|
|
@ -1,23 +1,10 @@
|
|||
arrow==0.12.1
|
||||
bleach==2.1.3
|
||||
certifi==2018.4.16
|
||||
chardet==3.0.4
|
||||
emoji==0.5.0
|
||||
feedgen==0.7.0
|
||||
html5lib==1.0.1
|
||||
idna==2.7
|
||||
Jinja2==2.10
|
||||
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
|
||||
PyYAML==3.13
|
||||
requests==2.19.1
|
||||
six==1.11.0
|
||||
unicode-slugify==0.1.3
|
||||
Unidecode==1.0.22
|
||||
urllib3==1.23
|
||||
Wand==0.4.4
|
||||
webencodings==0.5.1
|
||||
|
|
2
run
2
run
|
@ -3,5 +3,5 @@
|
|||
set -euo pipefail
|
||||
IFS=$'\n\t'
|
||||
|
||||
source ./.venv/bin/activate
|
||||
#source ./.venv/bin/activate
|
||||
python3 nasg.py "$@"
|
||||
|
|
54
settings.py
54
settings.py
|
@ -4,6 +4,7 @@ import argparse
|
|||
import logging
|
||||
|
||||
base = os.path.abspath(os.path.expanduser('~/Projects/petermolnar.net'))
|
||||
syncserver = 'liveserver:/web/petermolnar.net/web'
|
||||
|
||||
site = {
|
||||
'title': 'Peter Molnar',
|
||||
|
@ -16,10 +17,6 @@ site = {
|
|||
'journal'
|
||||
],
|
||||
'licence': 'by-nc-nd',
|
||||
'piwik': {
|
||||
'domain': 'stats.petermolnar.net',
|
||||
'id': 1
|
||||
}
|
||||
}
|
||||
|
||||
categorydisplay = {
|
||||
|
@ -33,25 +30,12 @@ licence = {
|
|||
'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 = {
|
||||
'webmention': 'https://webmention.io/petermolnar.net/webmention',
|
||||
'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 = {
|
||||
|
@ -61,14 +45,18 @@ author = {
|
|||
'avatar': 'https://petermolnar.net/molnar_peter_avatar.jpg',
|
||||
'gpg': 'https://petermolnar.net/pgp.asc',
|
||||
'cv': 'https://petermolnar.net/about.html',
|
||||
'xmpp': 'mail@petermolnar.net',
|
||||
'flickr': 'petermolnareu',
|
||||
'github': 'petermolnar',
|
||||
'twitter': 'petermolnar'
|
||||
'contact': {
|
||||
'xmpp': 'xmpp:mail@petermolnar.net',
|
||||
'tumblr': 'https://petermolnarnet.tumblr.com/',
|
||||
'wordpress': 'https://petermolnareu.wordpress.com/',
|
||||
'flickr': 'https://flickr.com/people/petermolnareu',
|
||||
'github': 'https://github.com/petermolnar',
|
||||
}
|
||||
}
|
||||
|
||||
paths = {
|
||||
'content': os.path.join(base, 'content'),
|
||||
'webmentions': os.path.join(base, 'content', 'webmentions'),
|
||||
'tmpl': os.path.join(base, 'nasg', 'templates'),
|
||||
'watermark': os.path.join(base, 'nasg', 'templates', 'watermark.png'),
|
||||
'build': os.path.join(base, 'www'),
|
||||
|
@ -85,20 +73,10 @@ photo = {
|
|||
},
|
||||
}
|
||||
|
||||
tips = [
|
||||
{
|
||||
'name': 'paypal',
|
||||
'label': 'PayPal',
|
||||
'value': '£3',
|
||||
'url': 'https://paypal.me/petermolnar/3GBP',
|
||||
},
|
||||
{
|
||||
'name': 'monzo',
|
||||
'label': 'Monzo (UK)',
|
||||
'value': '£3',
|
||||
'url': 'https://monzo.me/petermolnar/3',
|
||||
},
|
||||
]
|
||||
tips = {
|
||||
'paypal': 'https://paypal.me/petermolnar/3GBP',
|
||||
'monzo': 'https://monzo.me/petermolnar/3',
|
||||
}
|
||||
|
||||
dateformat = {
|
||||
'iso': 'YYYY-MM-DDTHH:mm:ssZZ',
|
||||
|
|
6
sync
6
sync
|
@ -1,6 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
set -euo pipefail
|
||||
IFS=$'\n\t'
|
||||
|
||||
rsync -avuhH --delete-after ../www/ liveserver:/web/petermolnar.net/web/
|
|
@ -19,26 +19,26 @@
|
|||
{% endif %}
|
||||
{% 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>
|
||||
{% if category.display == 'flat' %}
|
||||
<h3 itemprop="name headline" class="p-name entry-title">
|
||||
<h3 class="p-name entry-title">
|
||||
{% else %}
|
||||
<h2 itemprop="name headline" class="p-name entry-title">
|
||||
<h2 class="p-name entry-title">
|
||||
{% endif %}
|
||||
{% if post.is_reply %}
|
||||
<svg class="icon" width="16" height="16">
|
||||
<use xlink:href="#icon-reply" />
|
||||
</svg>
|
||||
<a href="{{ post.url }}/" class="u-url bookmark" itemprop="url mainEntityOfPage">
|
||||
<a href="{{ post.url }}/" class="u-url u-uuid" rel="bookmark">
|
||||
RE:
|
||||
</a>
|
||||
<a href="{{ post.is_reply }}" class="u-in-reply-to">
|
||||
{{ post.is_reply }}
|
||||
</a>
|
||||
{% else %}
|
||||
<a href="{{ post.url }}" title="{{ post.title }}" class="u-url bookmark" itemprop="url mainEntityOfPage">
|
||||
<span class="entry-title p-name">{{ post.title }}</span>
|
||||
<a href="{{ post.url }}" title="{{ post.title }}" rel="bookmark" class="u-url u-uuid">
|
||||
{{ post.title }}
|
||||
</a>
|
||||
{% endif %}
|
||||
{% if category.display == 'flat' %}
|
||||
|
@ -49,7 +49,7 @@
|
|||
</header>
|
||||
|
||||
{% if post.summary %}
|
||||
<div class="e-summary entry-summary" itemprop="description">
|
||||
<div class="e-summary entry-summary">
|
||||
{{ post.html_summary }}
|
||||
<p class="more">
|
||||
<a href="{{ post.url }}" title="{{ post.title }}">
|
||||
|
@ -58,7 +58,7 @@
|
|||
</p>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="e-content entry-content" itemprop="articleBody">
|
||||
<div class="e-content entry-content">
|
||||
{{ post.html_content }}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
{% if href != src %}
|
||||
<a href="{{ href }}">
|
||||
{% 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 %}
|
||||
</a>
|
||||
{% endif %}
|
||||
|
|
|
@ -13,5 +13,21 @@ if(! isset($payload['secret']) || $payload['secret'] != '{{ callback_secret }}'
|
|||
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');
|
||||
|
|
|
@ -36,7 +36,7 @@
|
|||
}
|
||||
</script>
|
||||
</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 %}
|
||||
|
||||
|
@ -103,9 +103,9 @@
|
|||
|
||||
{% block content %}
|
||||
<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>
|
||||
<h1 class="entry-title p-name" itemprop="name headline">
|
||||
<h1 class="entry-title p-name">
|
||||
{% if post.is_reply %}
|
||||
<span>
|
||||
<svg width="16" height="16">
|
||||
|
@ -128,35 +128,32 @@
|
|||
|
||||
{% if post.review %}
|
||||
<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>
|
||||
<p>
|
||||
By
|
||||
<span class="p-author h-card vcard reviewer" itemprop="author" itemscope="" itemtype="http://schema.org/Person">
|
||||
<a class="fn p-name url u-url u-uid" href="{{ author.url }}" itemprop="url">
|
||||
<span itemprop="name">{{ author.name }}</span>
|
||||
</a></span> at <time class="dt-published dtreviewed" datetime="{{ post.pubtime }}" itemprop="datePublished">{{ post.pubdate }}</time>
|
||||
<span class="p-author h-card vcard reviewer">
|
||||
<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>
|
||||
</p>
|
||||
<p>
|
||||
<span class="rating" itemprop="reviewRating" itemscope itemtype="http://schema.org/Rating">
|
||||
<meta itemprop="worstRating" content = "1">
|
||||
<span class="value" itemprop="ratingValue">{{ post.review.rating }}</span>
|
||||
<span class="rating">
|
||||
<span class="value">{{ post.review.rating }}</span>
|
||||
out of
|
||||
<span class="best" itemprop="bestRating">5</span>
|
||||
<span class="best">5</span>
|
||||
</span>
|
||||
</p>
|
||||
<p class="p-summary summary" itemprop="reviewBody">{{ post.review.summary }}</p>
|
||||
<p class="p-summary summary">{{ post.review.summary }}</p>
|
||||
</div>
|
||||
<hr/>
|
||||
{% endif %}
|
||||
|
||||
{% if post.summary %}
|
||||
<div class="e-summary entry-summary" itemprop="description">
|
||||
<div class="e-summary entry-summary">
|
||||
{{ post.html_summary }}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="e-content entry-content" itemprop="articleBody">
|
||||
<div class="e-content entry-content">
|
||||
{{ post.html_content }}
|
||||
</div>
|
||||
|
||||
|
@ -167,35 +164,27 @@
|
|||
<dd class="published">
|
||||
<time class="dt-published"
|
||||
datetime="{{ post.pubtime }}"
|
||||
itemprop="dateModified datePublished"
|
||||
>{{ post.pubdate }}</time>
|
||||
</dd>
|
||||
|
||||
<dt>Author</dt>
|
||||
<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"
|
||||
src="{{ author.avatar }}"
|
||||
alt="Photo of {{ author.name }}"
|
||||
itemprop="image" />
|
||||
/>
|
||||
<a class="fn p-name url u-url u-uid"
|
||||
href="{{ author.url }}"
|
||||
rel="author"
|
||||
itemprop="url">
|
||||
<span itemprop="name">{{ author.name }}</span>
|
||||
</a>
|
||||
<a class="u-email email" href="mailto:{{ author.email }}">
|
||||
<span itemprop="email">{{ author.email }}</span>
|
||||
</a>
|
||||
>{{ author.name }}</a>
|
||||
<<a class="u-email email" href="mailto:{{ author.email }}">{{ author.email }}</a>>
|
||||
</p>
|
||||
</dd>
|
||||
|
||||
<dt>Entry URL</dt>
|
||||
<dd>
|
||||
{% if not post.has_mainimg %}
|
||||
<img aria-hidden="true" src="{{ author.avatar }}" itemprop="image" />
|
||||
{% endif %}
|
||||
<a class="u-url u-uuid" rel="bookmark" href="{{ post.url }}" itemprop="url mainEntityOfPage">
|
||||
<a class="u-url u-uuid" rel="bookmark" href="{{ post.url }}">
|
||||
{{ post.url }}
|
||||
</a>
|
||||
</dd>
|
||||
|
@ -203,7 +192,7 @@
|
|||
<dt>License</dt>
|
||||
<dd class="license">
|
||||
{% 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>
|
||||
<li>you can share it</li>
|
||||
<li>you can republish it</li>
|
||||
|
@ -212,7 +201,7 @@
|
|||
<li>you always need to make a link back here</li>
|
||||
</ul>
|
||||
{% 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>
|
||||
<li>you can share it</li>
|
||||
<li>you can republish it</li>
|
||||
|
@ -222,7 +211,7 @@
|
|||
</ul>
|
||||
For commercial use, please contact me.
|
||||
{% 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>
|
||||
<li>you can share it</li>
|
||||
<li>you can't modify it</li>
|
||||
|
@ -332,27 +321,21 @@
|
|||
{% 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>
|
||||
<a href="https://creativecommons.org/">CC</a>,
|
||||
1999-2018,
|
||||
<span itemprop="logo" itemscope itemtype="https://schema.org/ImageObject">
|
||||
<img class="photo avatar u-photo u-avatar"
|
||||
src="{{ author.avatar }}"
|
||||
alt="Photo of {{ author.name }}"
|
||||
itemprop="url" />
|
||||
</span>
|
||||
<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>
|
||||
alt="Photo of {{ author.name }}" />
|
||||
<a class="fn p-name url u-url u-uid" rel="me" href="{{ site.url }}/about.html">{{ author.name }}</a>
|
||||
<<a rel="me" class="u-email email" href="mailto:{{ author.email }}">{{ author.email }}</a>>
|
||||
</p>
|
||||
<nav>
|
||||
<ul>
|
||||
{% for name, value in author.contact.items() %}
|
||||
<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">
|
||||
<use xlink:href="#icon-{{ name }}"></use>
|
||||
</svg>
|
||||
|
|
Loading…
Reference in a new issue