micropub queue support added; licence now links to spdx.org and uses the identifiers from there
@@ -67,28 +67,41 @@ RE_PRECODE = re.compile(
r'<pre class="([^"]+)"><code>' ) +def url2slug(url, limit=200): + return slugify( + re.sub(r"^https?://(?:www)?", "", url), + only_ascii=True, + lower=True + )[:limit] + def writepath(fpath, content, mtime=0): d = os.path.dirname(fpath) if not os.path.isdir(d): logger.debug('creating directory tree %s', d) os.makedirs(d) - with open(fpath, 'wt') as f: + + if isinstance(content, str): + mode = 'wt' + else: + mode = 'wb' + + with open(fpath, mode) as f: logger.info('writing file %s', fpath) f.write(content) # TODO - #if (mtime > 0): + # if (mtime > 0): -#def relurl(url,base=settings.site.get('url')): +# def relurl(url,base=settings.site.get('url')): #url =urlparse(url) #base = urlparse(base) - #if base.netloc != url.netloc: + # if base.netloc != url.netloc: #raise ValueError('target and base netlocs do not match') #base_dir='.%s' % (os.path.dirname(base.path)) #url = '.%s' % (url.path) - #return os.path.relpath(url,start=base_dir) + # return os.path.relpath(url,start=base_dir) class cached_property(object): """ extermely simple cached_property decorator:@@ -96,6 +109,7 @@ whenever something is called as @cached_property, on first run, the
result is calculated, then the class method is overwritten to be a property, contaning the result from the method """ + def __init__(self, method, name=None): self.method = method self.name = name or method.__name__@@ -106,6 +120,7 @@ return self
result = self.method(inst) setattr(inst, self.name, result) return result + class Webmention(object): def __init__(self, source, target, stime):@@ -132,10 +147,10 @@ return True
else: return False - async def save(self, content): + def save(self, content): writepath(self.fpath, content) - async def send(self): + def send(self): if self.exists: return telegraph_url = 'https://telegraph.p3k.io/webmention'@@ -153,7 +168,7 @@ )
if r.status_code not in [200, 201, 202]: logger.error('sending failed: %s %s', r.status_code, r.text) else: - await self.save(r.text) + self.save(r.text) class MarkdownDoc(object):@@ -174,7 +189,8 @@ return self._parsed[1]
def __pandoc(self, 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) return c @cached_property@@ -192,6 +208,7 @@ if hasattr(self, 'images') and len(self.images):
for match, img in self.images.items(): c = c.replace(match, '') return self.__pandoc(c) + class Comment(MarkdownDoc): def __init__(self, fpath):@@ -429,7 +446,7 @@ urls.append("https://fed.brid.gy/")
return urls def baseN(self, num, b=36, - numerals="0123456789abcdefghijklmnopqrstuvwxyz"): + numerals="0123456789abcdefghijklmnopqrstuvwxyz"): """ Creates short, lowercase slug for a number (an epoch) passed """@@ -614,16 +631,8 @@ self.summary,
self.content, ]) - #async def update(self): - #fm = frontmatter.loads('') - #fm.metadata = self.meta - #fm.content = self.content - #with open(fpath, 'wt') as f: - #logger.info("updating %s", fpath) - #f.write(frontmatter.dumps(fm)) - async def copyfiles(self): - exclude=['.md', '.jpg', '.png', '.gif']; + exclude = ['.md', '.jpg', '.png', '.gif'] files = glob.glob(os.path.join( os.path.dirname(self.fpath), '*.*'@@ -638,7 +647,8 @@ settings.paths.get('build'),
self.name, os.path.basename(f) ) - if os.path.exists(t) and os.path.getmtime(f) <= os.path.getmtime(t): + if os.path.exists(t) and os.path.getmtime( + f) <= os.path.getmtime(t): continue logger.info("copying '%s' to '%s'", f, t) cp(f, t)@@ -999,11 +1009,12 @@ self._tasks = []
self._loop = asyncio.get_event_loop() def add(self, job): - task = self._loop.create_task(job) - self._tasks.append(task) + #task = self._loop.create_task(job) + self._tasks.append(asyncio.ensure_future(job)) def run(self): self._loop.run_until_complete(asyncio.wait(self._tasks)) + class PHPFile(object): @property@@ -1066,10 +1077,12 @@ notindexed=mtime,
tokenize=porter )''' ) + self.is_changed = False def __exit__(self): - self.db.commit() - self.db.execute('PRAGMA auto_vacuum;') + if self.is_changed: + self.db.commit() + self.db.execute('PRAGMA auto_vacuum;') self.db.close() def check(self, name):@@ -1112,6 +1125,7 @@ title,
category, content )) + self.is_changed = True @property def renderfile(self):@@ -1170,9 +1184,7 @@ 'site': settings.site,
'gones': self.gone, 'redirects': self.redirect }) - with open(self.renderfile, 'wt') as f: - logger.info("rendering to %s", self.renderfile) - f.write(r) + writepath(self.renderfile, r) class WebhookPHP(PHPFile):@@ -1193,9 +1205,27 @@ 'author': settings.author,
'webmentionio': keys.webmentionio, 'zapier': keys.zapier, }) - with open(self.renderfile, 'wt') as f: - logger.info("rendering to %s", self.renderfile) - f.write(r) + writepath(self.renderfile, r) + + +class MicropubPHP(PHPFile): + @property + def renderfile(self): + return os.path.join( + settings.paths.get('build'), + 'micropub.php' + ) + + @property + def templatefile(self): + return 'Micropub.j2.php' + + async def _render(self): + r = J2.get_template(self.templatefile).render({ + 'site': settings.site, + 'paths': settings.paths + }) + writepath(self.renderfile, r) class Category(dict):@@ -1214,12 +1244,6 @@ value.fpath,
) ) dict.__setitem__(self, key, value) - - def get_posts(self, start=0, end=-1): - return [ - self[k].tmplvars - for k in self.sortedkeys[start:end] - ] @property def sortedkeys(self):@@ -1263,6 +1287,50 @@ )
else: return settings.paths.get('build') + @property + def mtime(self): + return arrow.get(self[self.sortedkeys[0]].published).timestamp + + @property + def rssfeedfpath(self): + return os.path.join( + self.dpath, + 'feed', + 'index.xml' + ) + + @property + def atomfeedfpath(self): + return os.path.join( + self.dpath, + 'feed', + 'atom.xml' + ) + + def get_posts(self, start=0, end=-1): + return [ + self[k].tmplvars + for k in self.sortedkeys[start:end] + ] + + def is_uptodate(self, fpath, ts): + if settings.args.get('force'): + return False + if not os.path.exists(fpath): + return False + if os.path.getmtime(fpath) >= ts: + return True + return False + + def newest(self, start=0, end=-1): + if start == end: + end = -1 + s = sorted( + [self[k].mtime for k in self.sortedkeys[start:end]], + reverse=True + ) + return s[0] + def navlink(self, ts): label = ts.format(self.trange) if arrow.utcnow().format(self.trange) == label:@@ -1305,43 +1373,6 @@ },
'posts': posts, } - @property - def mtime(self): - return arrow.get(self[self.sortedkeys[0]].published).timestamp - - # @property - # def exists(self): - # if settings.args.get('force'): - # return False - # ismissing = False - # for f in [ - # os.path.join(self.renderdir, 'feed', 'index.xml'), - # ]: - # if not os.path.exists(f): - # ismissing = True - # elif self.mtime > os.path.getmtime(f): - # ismissing = True - # if ismissing: - # return False - # else: - # return True - - @property - def rssfeedfpath(self): - return os.path.join( - self.dpath, - 'feed', - 'index.xml' - ) - - @property - def atomfeedfpath(self): - return os.path.join( - self.dpath, - 'feed', - 'atom.xml' - ) - def indexfpath(self, subpath=None): if subpath: return os.path.join(@@ -1385,7 +1416,7 @@ fe.title(post.get('title'))
fe.author({ 'name': settings.author.get('name'), - 'email':settings.author.get('email') + 'email': settings.author.get('email') }) fe.category({@@ -1418,81 +1449,28 @@ "%d" % enc.get('size'),
enc.get('mime') ) elif xmlformat == 'atom': - fe.link(href=post.get('url'), rel='alternate', type='text/html') + fe.link( + href=post.get('url'), + rel='alternate', + type='text/html') fe.content(src=post.get('url'), type='text/html') fe.summary(post.get('summary')) if xmlformat == 'rss': fg.link(href=self.feedurl) - writepath(self.rssfeedfpath, '%s' % fg.rss_str(pretty=True)) + writepath(self.rssfeedfpath, fg.rss_str(pretty=True)) elif xmlformat == 'atom': fg.link(href=self.feedurl, rel='self') fg.link(href=settings.meta.get('hub'), rel='hub') - writepath(self.atomfeedfpath, '%s' % fg.atom_str(pretty=True)) + writepath(self.atomfeedfpath, fg.atom_str(pretty=True)) async def render_flat(self): r = J2.get_template(self.template).render( self.tmplvars(self.get_posts()) - #[self[k].tmplvars for k in self.sortedkeys] ) writepath(self.indexfpath(), r) - def is_uptodate(self, fpath, ts): - if not os.path.exists(fpath): - return False - if os.path.getmtime(fpath) >= ts: - return True - return False - - def newest(self, start=0, end=-1): - if start == end: - end = -1 - s = sorted( - [self[k].mtime for k in self.sortedkeys[start:end]], - reverse=True - ) - return s[0] - - async def render(self): - newest = self.newest() - if not self.is_uptodate(self.rssfeedfpath, newest): - logger.info( - '%s RSS feed outdated, generating new', - self.name - ) - await self.render_feed('rss') - else: - logger.info( - '%s RSS feed up to date', - self.name - ) - - if not self.is_uptodate(self.atomfeedfpath, newest): - logger.info( - '%s ATOM feed outdated, generating new', - self.name - ) - await self.render_feed('atom') - else: - logger.info( - '%s ATOM feed up to date', - self.name - ) - - if self.display == 'flat': - if not self.is_uptodate(self.indexfpath(), newest): - logger.info( - '%s flat index outdated, generating new', - self.name - ) - await self.render_flat() - else: - logger.info( - '%s flat index is up to date', - self.name - ) - return - + async def render_archives(self): by_time = {} for key in self.sortedkeys: trange = arrow.get(key).format(self.trange)@@ -1503,7 +1481,7 @@ })
by_time[trange].append(key) keys = list(by_time.keys()) - for p, c, n in zip([None]+keys[:-1], keys, keys[1:]+[None]): + for p, c, n in zip([None] + keys[:-1], keys, keys[1:] + [None]): form = c.format(self.trange) if arrow.utcnow().format(self.trange) == form: fpath = self.indexfpath()@@ -1515,8 +1493,11 @@ 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.info('newest called with start: %s, end: %s', start, end) - logger.error('calling newest failed with %s for %s', self.name, c) + logger.error( + 'calling newest failed with %s for %s', + self.name, + c + ) continue if self.is_uptodate(fpath, newest):@@ -1542,6 +1523,49 @@ )
) writepath(fpath, r) + async def render(self): + newest = self.newest() + if not self.is_uptodate(self.rssfeedfpath, newest): + logger.info( + '%s RSS feed outdated, generating new', + self.name + ) + await self.render_feed('rss') + else: + logger.info( + '%s RSS feed up to date', + self.name + ) + + if not self.is_uptodate(self.atomfeedfpath, newest): + logger.info( + '%s ATOM feed outdated, generating new', + self.name + ) + await self.render_feed('atom') + else: + logger.info( + '%s ATOM feed up to date', + self.name + ) + + if self.display == 'flat': + if not self.is_uptodate(self.indexfpath(), newest): + logger.info( + '%s flat index outdated, generating new', + self.name + ) + await self.render_flat() + else: + logger.info( + '%s flat index is up to date', + self.name + ) + return + else: + await self.render_archives() + + class Sitemap(dict): @property def mtime(self):@@ -1560,7 +1584,8 @@ return
with open(self.renderfile, 'wt') as f: f.write("\n".join(sorted(self.keys()))) -def mkcomment(webmention): + +def makecomment(webmention): if 'published_ts' in webmention.get('data'): maybe = webmention.get('data').get('published') if not maybe or maybe == 'None':@@ -1569,19 +1594,21 @@ else:
dt = arrow.get(webmention.get('data').get('published')) slug = webmention.get('target').strip('/').split('/')[-1] + if slug == settings.site.get('domain'): + return fdir = glob.glob(os.path.join(settings.paths.get('content'), '*', slug)) if not len(fdir): logger.error( "couldn't find post for incoming webmention: %s", webmention - ) + ) return elif len(fdir) > 1: logger.error( "multiple posts found for incoming webmention: %s", webmention - ) + ) return fdir = fdir.pop()@@ -1606,9 +1633,7 @@ if not c:
fm.content = '' else: fm.content = c - with open(fpath, 'wt') as f: - logger.info("saving webmention to %s", fpath) - f.write(frontmatter.dumps(fm)) + writepath(fpath, frontmatter.dumps(fm)) def makecomments():@@ -1635,19 +1660,69 @@ return
try: mentions = webmentions.json() for webmention in mentions.get('links'): - mkcomment(webmention) + makecomment(webmention) except ValueError as e: logger.error('failed to query webmention.io: %s', e) pass + +def makepost(fpath): + try: + fname = os.path.basename(fpath) + mtime = arrow.get(fname.split('.')[0]) + with open(fpath, 'r') as f: + payload = json.loads(f.read()) + pprint(payload) + if 'content' not in payload: + logger.error('missing content from %s', fname) + return False + + fm = frontmatter.loads('') + fm.metadata = { + 'published': mtime.format( + settings.dateformat.get('iso') + ), + 'tags': payload.get('category', []) + } + fm.content = payload.get('content') + + for maybe in ['title', 'summary', 'in-reply-to']: + x = payload.get(maybe, None) + if x: + fm.metadata.update({maybe: x}) + + slug = payload.get('slug', '') + if not len(slug): + if 'in-reply-to' in fm.metadata: + slug = "re-%s" % (url2slug(fm.metadata.get('in-reply-to'))) + else: + slug = mtime.format(settings.dateformat.get('fname')) + + fpath = os.path.join( + settings.paths.get('micropub'), + slug, + 'index.md' + ) + writepath(fpath, frontmatter.dumps(fm)) + return True + except Exception as e: + logger.error('parsing entry at %s failed: %s', fpath, e) + return False -def url2slug(url, limit=200): - return slugify( - re.sub(r"^https?://(?:www)?", "", url), - only_ascii=True, - lower=True - )[:limit] +def makeposts(): + logger.info('getting micropub queue...') + os.system( + "rsync -avuhH --remove-source-files %s/ %s/" % ( + '%s/%s' % (settings.syncserver, settings.paths.get('remotequeue')), + '%s' % (settings.paths.get('queue')) + ) + ) + logger.info('...done') + for js in glob.glob(os.path.join(settings.paths.get('queue'), '*.json')): + logging.info('processing micropub post %s', js) + if makepost(js): + os.unlink(js) def make(): start = int(round(time.time() * 1000))@@ -1658,11 +1733,16 @@ makecomments()
except Exception as e: logger.error('failed to make comments - are we offline?') + makeposts(); + content = settings.paths.get('content') worker = AsyncWorker() - webmentions = AsyncWorker() + webmentions = [] rules = IndexPHP() + micropub = MicropubPHP() + worker.add(micropub.render()) + webhook = WebhookPHP() worker.add(webhook.render())@@ -1676,7 +1756,7 @@ post = Singular(e)
for i in post.images.values(): worker.add(i.downsize()) for i in post.to_ping: - webmentions.add(i.send()) + webmentions.append(i) worker.add(post.render()) worker.add(post.copyfiles())@@ -1706,8 +1786,7 @@ search.__exit__()
worker.add(search.render()) worker.add(sitemap.render()) - - for e in glob.glob(os.path.join(content, '*', '*.ptr')): + for e in glob.glob(os.path.join(content, '*', '*.del')): post = Gone(e) if post.mtime > last: last = post.mtime@@ -1718,7 +1797,6 @@ if post.mtime > last:
last = post.mtime rules.add_redirect(post.source, post.target) worker.add(rules.render()) - for category in categories.values(): worker.add(category.render())@@ -1730,8 +1808,6 @@ # copy static
staticfiles = [] staticpaths = [ os.path.join(content, '*.*'), - #os.path.join(settings.paths.get('tmpl'), '*.js'), - #os.path.join(settings.paths.get('tmpl'), '*.css') ] for p in staticpaths: staticfiles = staticfiles + glob.glob(p)@@ -1752,17 +1828,19 @@ logger.info('starting syncing')
os.system( "rsync -avuhH --delete-after %s/ %s/" % ( settings.paths.get('build'), - settings.syncserver + '%s/%s' % (settings.syncserver, + settings.paths.get('remotewww')) ) ) logger.info('syncing finished') - logger.info('sending webmentions') - try: - webmentions.run() - except Exception as e: - logger.error('failed to send webmentions - are we offline?') - logger.info('sending webmentions finished') + logger.info('sending webmentions') + try: + for i in webmentions: + i.send() + except Exception as e: + logger.error('failed to send webmentions - are we offline?') + logger.info('sending webmentions finished') if __name__ == '__main__':
@@ -4,7 +4,7 @@ import argparse
import logging base = os.path.abspath(os.path.expanduser('~/Projects/petermolnar.net')) -syncserver = 'liveserver:/web/petermolnar.net/web' +syncserver = 'liveserver:/web/petermolnar.net' site = { 'title': 'Peter Molnar',@@ -16,7 +16,7 @@ 'article',
'photo', 'journal' ], - 'licence': 'by-nc-nd', + 'licence': 'CC-BY-NC-ND-4.0', } categorydisplay = {@@ -26,8 +26,8 @@ 'photo': 'gallery',
} licence = { - 'article': 'by', - 'journal': 'by-nc', + 'article': 'CC-BY-4.0', + 'journal': 'CC-BY-NC-4.0', } meta = {@@ -36,6 +36,8 @@ 'pingback': 'https://webmention.io/petermolnar.net/xmlrpc',
'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 = {@@ -62,6 +64,10 @@ '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'), + 'queue': os.path.join(base, 'queue'), + 'remotewww': 'web', + 'remotequeue': 'queue', + 'micropub': os.path.join(base, 'content', 'note'), } photo = {@@ -83,6 +89,7 @@
dateformat = { 'iso': 'YYYY-MM-DDTHH:mm:ssZZ', 'display': 'YYYY-MM-DD HH:mm', + 'fname': 'YYYYMMDDHHmmssZ', } loglevels = {
@@ -1,5 +1,10 @@
{% extends "base.j2.html" %} {% block lang %}{% endblock %} + +{% block licence %} + <link rel="license" href="https://spdx.org/licenses/{{ site.licence }}.html" type="{{ site.licence }}" /> +{% endblock %} + {% block title %}{{ category.title }}{% endblock %} {% block meta %} <link rel="alternate" type="application/rss+xml" title="{{ category.title }} RSS feed" href="{{ category.feed }}" />
@@ -0,0 +1,114 @@
+<?php + +function _syslog($msg) { + $trace = debug_backtrace(); + $caller = $trace[1]; + $parent = $caller['function']; + if (isset($caller['class'])) + $parent = $caller['class'] . '::' . $parent; + + return error_log( "{$parent}: {$msg}" ); +} + +function unauthorized($text) { + header('HTTP/1.1 401 Unauthorized'); + die($text); +} + +function badrequest($text) { + header('HTTP/1.1 400 Bad Request'); + die($text); +} + +function httpok($text) { + header('HTTP/1.1 200 OK'); + echo($text); + exit(0); +} + +function accepted() { + header('HTTP/1.1 202 Accepted'); + #header('Location: {{ site.url }}'); + exit(0); +} + +if (!empty($_GET)) { + if ( ! isset($_GET['q']) ) { + badrequest('please POST a micropub request'); + } + + if ( isset($_GET['q']['config']) ) { + httpok(json_encode(array('tags' => array()))); + } + + if(isset($_GET['q']['syndicate-to'])) { + httpok(json_encode(array('syndicate-to' => array()))); + } + + badrequest('please POST a micropub request'); +} + +$raw = file_get_contents("php://input"); +print_r($raw); +try { + $decoded = json_decode($raw, true); +} catch (Exception $e) { + _syslog('failed to decode JSON, trying decoding form data'); + try { + parse_str($raw, $decoded); + } + catch (Exception $e) { + _syslog('failed to decoding form data as well'); + badrequest('invalid POST contents'); + } +} +print_r($decoded); + +$token = ''; +if ( isset($decoded['access_token']) ) { + $token = $decoded['access_token']; + unset($decoded['access_token']); +} +elseif ( isset($_SERVER['HTTP_AUTHORIZATION']) ) { + $token = trim(str_replace('Bearer', '', $_SERVER['HTTP_AUTHORIZATION'])); +} + +if (empty($token)) { + unauthorized('missing token'); +} + +$request = curl_init(); +curl_setopt($request, CURLOPT_URL, 'https://tokens.indieauth.com/token'); +curl_setopt($request, CURLOPT_HTTPHEADER, array( + 'Content-Type: application/x-www-form-urlencoded', + sprintf('Authorization: Bearer %s', $token) +)); +curl_setopt($request, CURLOPT_RETURNTRANSFER, 1); +$response = curl_exec($request); +curl_close($request); +parse_str(urldecode($response), $verification); +if (! isset($verification['scope']) ) { + unauthorized('missing "scope"'); +} +if (! isset($verification['me']) ) { + unauthorized('missing "me"'); +} +if ( ! stristr($verification['me'], '{{ site.url }}') ) { + unauthorized('wrong domain'); +} +if ( ! stristr($verification['scope'], 'create') ) { + unauthorized('invalid scope'); +} + +$user = posix_getpwuid(posix_getuid()); +$now = time(); +$decoded['mtime'] = $now; +$fname = sprintf( + '%s/%s/%s.json', + $user['dir'], + '{{ paths.remotequeue }}', + microtime(true) +); + +file_put_contents($fname, json_encode($decoded, JSON_PRETTY_PRINT)); +accepted();
@@ -3,7 +3,6 @@ {% block meta %}
<meta name="author" content="{{ author.name }} <{{ author.email }}>" /> <meta name="description" content="{{ post.summary|e }}" /> <link rel="canonical" href="{{ post.url }}" /> - <link rel="license" href="https://creativecommons.org/licenses/4.0/{{ post.licence }}" /> {% if post.has_code %} <style media="all"> {% include 'prism.css' %}@@ -17,3 +16,6 @@ {% include 'prism.js' %}
</script> {% endif %} {% endblock %} +{% block licence %} + <link rel="license" href="https://spdx.org/licenses/{{ post.licence }}.html" type="{{ post.licence }}" /> +{% endblock %}
@@ -5,6 +5,7 @@ <title>{% block title %}{{ post.title }} - {{ site.domain }}{% endblock %}</title>
<meta charset="UTF-8" /> <meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1" /> <link rel="icon" href="{{ site.url }}/favicon.ico" /> + {% block licence %}{% endblock %} {% for key, value in meta.items() %} <link rel="{{ key }}" href="{{ value }}" /> {% endfor %}@@ -12,7 +13,7 @@ {% block meta %}{% endblock %}
<style media="all"> {% include 'style.css' %} </style> - <style id="css_alt" media="aural"> + <style id="css_alt" media="speech"> {% include 'style-alt.css' %} </style> <style media="print">@@ -31,7 +32,7 @@ function toggleStylesheet(trigger){
var setto = 'all'; var e = document.querySelector('#css_alt'); if (e.getAttribute("media") == 'all') { - setto = 'aural'; + setto = 'speech'; } localStorage.setItem("stylesheet", setto); e.setAttribute("media", setto);@@ -231,8 +232,8 @@ </dd>
<dt>License</dt> <dd class="license"> - {% if post.licence == 'by' %} - <a rel="license" href="https://creativecommons.org/licenses/by/4.0/" class="u-license" property="u-licence" itemprop="license">CC BY 4.0</a> + {% if post.licence == 'CC-BY-4.0' %} + <a rel="license" href="https://creativecommons.org/licenses/by/4.0/" class="u-license" property="u-licence" itemprop="license">{{ post.licence }}</a> <ul> <li>you can share it</li> <li>you can republish it</li>@@ -240,8 +241,8 @@ <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.text == 'by-nc' %} - <a rel="license" href="https://creativecommons.org/licenses/by-nc/4.0/" class="u-license" property="u-licence" itemprop="license">CC BY-NC 4.0</a> + {% elif post.licence == 'CC-BY-NC-4.0' %} + <a rel="license" href="https://creativecommons.org/licenses/by-nc/4.0/" class="u-license" property="u-licence" itemprop="license">{{ post.licence }}</a> <ul> <li>you can share it</li> <li>you can republish it</li>@@ -250,8 +251,8 @@ <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. - {% else %} - <a rel="license" href="https://creativecommons.org/licenses/by-nc-nd/4.0/" class="u-license" property="u-licence" itemprop="license">CC BY-NC-ND 4.0</a> + {% 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="u-licence" itemprop="license">{{ post.licence }}</a> <ul> <li>you can share it</li> <li>you can't modify it</li>