Add more features

Cross-reference tracking/redlinks
Page title customisation
Last modified
This commit is contained in:
RunasSudo 2020-12-20 01:13:27 +11:00
parent a32795fbe6
commit 205a9a8c82
Signed by: RunasSudo
GPG Key ID: 7234E476BF21C61A
4 changed files with 122 additions and 28 deletions

View File

@ -123,12 +123,13 @@ def page_view(path):
return flask.render_template('page_rendered.html', page={ return flask.render_template('page_rendered.html', page={
'path': path, 'path': path,
'title': path.split('/')[-1], 'title': md.meta['title'] if 'title' in md.meta else path.split('/')[-1],
'content': page_content, 'content': page_content,
'toc': md.toc_tokens, 'toc': md.toc_tokens,
'meta': md.meta, 'meta': md.meta,
'children': children 'children': children,
}) 'xrefs': index['xrefs'].get(path, [])
}, collapsed='collapsed' in flask.request.args)
@app.route('/preview/<path:path>') @app.route('/preview/<path:path>')
def page_preview(path): def page_preview(path):
@ -214,11 +215,12 @@ def cli_index():
with app.app_context(): with app.app_context():
tags = {} tags = {}
xrefs = {}
base_path = './data/pages' base_path = './data/pages'
for dirpath, dirnames, filenames in os.walk(base_path): for dirpath, dirnames, filenames in os.walk(base_path):
for fname in filenames: for fname in filenames:
if fname.endswith('.md'): if fname.endswith('.md') and not os.path.islink(flask.safe_join(dirpath, fname)):
page_path = dirpath[len(base_path)+1:] + '/' + fname[:-3] page_path = dirpath[len(base_path)+1:] + '/' + fname[:-3]
with(open(flask.safe_join(dirpath, fname), 'r')) as f: with(open(flask.safe_join(dirpath, fname), 'r')) as f:
@ -230,6 +232,16 @@ def cli_index():
if tag not in tags: if tag not in tags:
tags[tag] = [] tags[tag] = []
tags[tag].append({'kind': 'page', 'path': page_path}) tags[tag].append({'kind': 'page', 'path': page_path})
for ref in md.meta.get('refs', []):
fname_ref = flask.safe_join('./data/pages', ref) + '.md'
if os.path.islink(fname_ref):
ref = '/'.join(ref.split('/')[:-1]) + '/' + os.path.splitext(os.readlink(fname_ref))[0]
if ref not in xrefs:
xrefs[ref] = []
if page_path not in xrefs[ref]:
xrefs[ref].append(page_path)
base_path = './data/images' base_path = './data/images'
for dirpath, dirnames, filenames in os.walk(base_path): for dirpath, dirnames, filenames in os.walk(base_path):
@ -251,5 +263,32 @@ def cli_index():
with open('index.pickle', 'wb') as f: with open('index.pickle', 'wb') as f:
pickle.dump({ pickle.dump({
'tags': tags 'tags': tags,
'xrefs': xrefs
}, f) }, f)
@app.cli.command('redlinks')
def cli_redlinks():
app.config['SERVER_NAME'] = 'localhost'
redlinks = set()
with app.app_context():
base_path = './data/pages'
for dirpath, dirnames, filenames in os.walk(base_path):
for fname in filenames:
if fname.endswith('.md'):
page_path = dirpath[len(base_path)+1:] + '/' + fname[:-3]
with(open(flask.safe_join(dirpath, fname), 'r')) as f:
page_source = f.read()
md = WNMarkdown()
md.convert(page_source)
for redlink in md.meta.get('redlinks', []):
redlinks.add(redlink)
redlinks = sorted(list(redlinks))
for redlink in redlinks:
print(redlink)

View File

@ -37,24 +37,41 @@
{% endblock %} {% endblock %}
{% block leftbox %} {% block leftbox %}
{% if page.toc or page.meta.tags %} {% if page.toc or page.meta.tags or (page.xrefs and not collapsed) %}
<div> <div>
{% if page.toc %} {% if page.toc %}
<ul class="toc"> <ul class="toc">
{% for item in page.toc %} {% for item in page.toc %}
<li><a href="#{{ item.id }}">{{ item.name }}</a></li> <li><a href="#{{ item.id }}">{{ item.name }}</a></li>
{% endfor %} {% endfor %}
</ul> </ul>
{% endif %} {% endif %}
{% if page.meta.tags %} {% if page.meta.tags %}
<p style="font-size: small;"> <p style="font-size: small;">
<em>Tags:</em> <em>Tags:</em>
{% for tag in page.meta.tags %} {% for tag in page.meta.tags %}
<a href="{{ url_for('tag_view', name=tag) }}">{{ tag }}</a>{% if not loop.last %},{% endif %} <a href="{{ url_for('tag_view', name=tag) }}">{{ tag }}</a>{% if not loop.last %},{% endif %}
{% endfor %} {% endfor %}
</p> </p>
{% endif %} {% endif %}
</div>
{% if page.xrefs and not collapsed %}
<div style="font-size: small;">
<p><em>Cross-references:</em></p>
<ul>
{% for xref in page.xrefs %}
<li><a href="{{ url_for('page_view', path=xref) }}">{{ xref.replace('/', ' › ') }}</a></li>
{% endfor %}
</ul>
</div>
{% endif %}
{% if page.meta.lastmod %}
<div style="font-size: small;">
<p><em>Last modified:</em> {{ page.meta.lastmod }}</p>
</div>
{% endif %}
</div>
{% endif %} {% endif %}
{% endblock %} {% endblock %}

View File

@ -50,6 +50,16 @@ def capitalise(n):
return '(' + capitalise(n[1:]) return '(' + capitalise(n[1:])
return n[0].upper() + n[1:] return n[0].upper() + n[1:]
class DirectiveTitle(Directive):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.md.meta['title'] = self.arg
def render(self):
return DirectiveElement('div')
directives['title'] = DirectiveTitle
class DirectiveTag(Directive): class DirectiveTag(Directive):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
@ -60,6 +70,16 @@ class DirectiveTag(Directive):
directives['tag'] = DirectiveTag directives['tag'] = DirectiveTag
class DirectiveLastmod(Directive):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.md.meta['lastmod'] = self.arg
def render(self):
return DirectiveElement('div')
directives['lastmod'] = DirectiveLastmod
class DirectiveInclude(Directive): class DirectiveInclude(Directive):
def render(self): def render(self):
el = DirectiveElement('div') el = DirectiveElement('div')
@ -102,6 +122,8 @@ def make_role_ref(is_upper):
div.set('class', 'tooltip-content') div.set('class', 'tooltip-content')
div.text = 'Loading…' div.text = 'Loading…'
self.md.meta['refs'] = self.md.meta.get('refs', []) + [path]
return el return el
return RoleRef return RoleRef
@ -119,6 +141,9 @@ class RoleImage(Role):
img.set('src', flask.url_for('image_view', name=image)) img.set('src', flask.url_for('image_view', name=image))
if style: if style:
img.set('style', style) img.set('style', style)
self.md.meta['refs'] = self.md.meta.get('refs', []) + [image]
return el return el
roles['image'] = RoleImage roles['image'] = RoleImage

View File

@ -77,7 +77,7 @@ html, body {
max-height: calc(100% - 1cm); max-height: calc(100% - 1cm);
} }
aside.leftbox > div :last-child, aside.rightbox > div :last-child { aside.leftbox > div p:last-child, aside.rightbox > div p:last-child {
margin-bottom: 0; margin-bottom: 0;
} }
} }
@ -141,9 +141,11 @@ h4, h5, h6 {
font-size: 1rem; /* 10.5pt */ font-size: 1rem; /* 10.5pt */
margin-top: 0.4cm; margin-top: 0.4cm;
font-weight: normal; font-weight: normal;
font-style: italic;
} }
section > div.two-columns > aside > h3:first-child { section > div.two-columns > aside > h3:first-child,
section > div.two-columns > aside > h4:first-child {
margin-top: 0.15cm; /* Adjust for 0.25cm of heading above */ margin-top: 0.15cm; /* Adjust for 0.25cm of heading above */
} }
@ -243,7 +245,8 @@ div.two-columns > aside:not(:last-of-type) {
/* Remove trailing margins */ /* Remove trailing margins */
div.admonition > p:last-child, div.admonition > p:last-child, div.admonition > div > p:last-child,
div.admonition > ul:last-child, div.admonition > ol:last-child,
td > ul:last-child { td > ul:last-child {
margin-bottom: 0; margin-bottom: 0;
} }
@ -291,13 +294,23 @@ div.admonition.pharm a.drug {
color: black; color: black;
font-weight: 600; font-weight: 600;
} }
div.admonition.pharm span.alt-drugs { span.alt-drugs {
color: #888a85; color: #888a85;
text-decoration: underline; text-decoration: underline;
text-decoration-style: dashed; text-decoration-style: dashed;
cursor: pointer; cursor: pointer;
} }
div.admonition.buzzword {
background-color: #d8f6bcff;
display: flex;
align-items: center;
}
div.admonition.buzzword > img {
width: 32px;
margin-right: 8px;
}
/* Tooltips */ /* Tooltips */
.tooltip { .tooltip {
position: relative; position: relative;