summaryrefslogtreecommitdiffstats
path: root/kyrias_website
diff options
context:
space:
mode:
Diffstat (limited to 'kyrias_website')
-rw-r--r--kyrias_website/__init__.py66
-rw-r--r--kyrias_website/config.py13
-rw-r--r--kyrias_website/static/base.css88
-rw-r--r--kyrias_website/templates/archive.html12
-rw-r--r--kyrias_website/templates/base.html41
-rw-r--r--kyrias_website/templates/entry.html41
-rw-r--r--kyrias_website/templates/page.html27
-rw-r--r--kyrias_website/templates/tags.html19
-rw-r--r--kyrias_website/util.py14
-rw-r--r--kyrias_website/views.py38
10 files changed, 359 insertions, 0 deletions
diff --git a/kyrias_website/__init__.py b/kyrias_website/__init__.py
new file mode 100644
index 0000000..474663d
--- /dev/null
+++ b/kyrias_website/__init__.py
@@ -0,0 +1,66 @@
+import os
+
+import flask_assets
+from flask import Flask
+from flask_flatpages import FlatPages
+from werkzeug.utils import cached_property
+
+
+class CustomFlatPages(FlatPages):
+ def __init__(self, app=None, name=None):
+ super(CustomFlatPages, self).__init__(app=app, name=name)
+ self.app = app
+
+ @cached_property
+ def _pages(self):
+ """Forget all pages and nuke our own cache"""
+ try:
+ del app.__dict__['journal_entries']
+ del app.__dict__['journal_tags']
+ except KeyError:
+ pass
+ return super(CustomFlatPages, self)._pages
+
+
+class CustomFlask(Flask):
+ def __init__(self, *args, **kwargs):
+ super(CustomFlask, self).__init__(*args, **kwargs)
+
+ config = {
+ 'default': 'kyrias_website.config.BaseConfig',
+ 'development': 'kyrias_website.config.DevelopmentConfig',
+ }
+
+ config_name = os.getenv('WEBSITE_CONFIGURATION', 'default')
+ self.config.from_object(config[config_name])
+
+ assets = flask_assets.Environment(self)
+ css = flask_assets.Bundle('base.css', filters='cssmin', output='screen.css')
+ assets.register('css_all', css)
+
+ self.pages = CustomFlatPages(self, 'pages')
+ self.journal = CustomFlatPages(self, 'journal')
+
+ @cached_property
+ def journal_entries(self):
+ return sorted(self.journal, key=lambda x: x.meta.get('date'))
+
+ @cached_property
+ def journal_tags(self):
+ tags = {}
+ for entry in self.journal_entries:
+ if 'tags' not in entry.meta:
+ tags.setdefault('untagged', set()).add(entry)
+ else:
+ for tag in entry.meta.get('tags'):
+ tags.setdefault(tag, set()).add(entry)
+
+ return tags
+
+
+app = CustomFlask(__name__)
+
+import kyrias_website.views
+
+if __name__ == '__main__':
+ app.run(port=5000)
diff --git a/kyrias_website/config.py b/kyrias_website/config.py
new file mode 100644
index 0000000..6c22a09
--- /dev/null
+++ b/kyrias_website/config.py
@@ -0,0 +1,13 @@
+class BaseConfig():
+ DEBUG = False
+
+ FLATPAGES_PAGES_EXTENSION = '.rst'
+ FLATPAGES_PAGES_HTML_RENDERER = 'kyrias_website.util.rst_renderer'
+
+ FLATPAGES_JOURNAL_EXTENSION = '.rst'
+ FLATPAGES_JOURNAL_HTML_RENDERER = 'kyrias_website.util.rst_renderer'
+ FLATPAGES_JOURNAL_ROOT = 'entries'
+
+
+class DevelopmentConfig(BaseConfig):
+ DEBUG = True
diff --git a/kyrias_website/static/base.css b/kyrias_website/static/base.css
new file mode 100644
index 0000000..55d2ee0
--- /dev/null
+++ b/kyrias_website/static/base.css
@@ -0,0 +1,88 @@
+body {
+ font-family: "Linux Libertine", "Linux Libertine O", "TeX Gyre Termes", "Constantia", "Liberation Serif", "Cambria", "Times New Roman", "Times", serif;
+ text-rendering: optimizeLegibility;
+ max-width: 37.5rem;
+}
+a, a:visited {
+ text-decoration: none;
+ color: #32609C;
+}
+a:hover {
+ color: #339;
+}
+a.permalink {
+ color: #eeeeee;
+ border: none;
+}
+:hover > a.permalink {
+ color: #cccccc;
+}
+a.permalink:hover,
+:target a.permalink {
+ color: #aaaaaa;
+}
+header nav ul {
+ padding: 0rem;
+ list-style-type: none;
+}
+header nav ul li {
+ display: inline;
+ margin-right: 0.3125rem;
+}
+header nav a, header nav a:visited {
+ color: #444;
+}
+header nav a:hover {
+ color: #111;
+}
+.padded-box {
+ font-size: 1rem;
+ padding-left: 0.625rem;
+ padding-right: 0.625rem;
+}
+hr {
+ margin-top: 1.25rem;
+ margin-bottom: 1.25rem;
+}
+article header h1 {
+ margin-top: 1.25rem;
+ margin-bottom: 0.3125rem;
+}
+article p {
+ white-space: pre-wrap;
+ text-align: justify;
+}
+#tags {
+ padding-bottom: 0.15625rem;
+}
+#post-nav {
+ padding-bottom: 0rem;
+}
+ul#archive-list,
+ul#tag-list {
+ list-style-type: none;
+ padding-left: 0;
+}
+ul.contact-addresses {
+ list-style-type: circle;
+ padding-left: 1.25rem;
+}
+footer#bottom div {
+ margin-bottom: 0.15625rem;
+}
+footer#bottom p#copy {
+ margin: 0rem;
+ padding-top: 0.15625rem;
+ padding-bottom: 0.15625rem;
+}
+footer#bottom ul#contact-info {
+ margin: 0;
+ padding: 0rem;
+ padding-top: 0.15625rem;
+ padding-bottom: 0.15625rem;
+ list-style-type: none;
+}
+footer#bottom ul#contact-info li {
+ display: inline;
+ margin-right: 0.15625rem;
+}
diff --git a/kyrias_website/templates/archive.html b/kyrias_website/templates/archive.html
new file mode 100644
index 0000000..2399c90
--- /dev/null
+++ b/kyrias_website/templates/archive.html
@@ -0,0 +1,12 @@
+{% extends 'base.html' %}
+
+{% block content %}
+<section>
+ <h1>Archive</h1>
+ <ul id="archive-list">
+ {% for entry in entries %}
+ <li>{{ entry.date }} — <a href="{{ url_for('entry', name=entry.path) }}">{{ entry.meta.title }}</a></li>
+ {% endfor %}
+ </ul>
+</section>
+{% endblock %}
diff --git a/kyrias_website/templates/base.html b/kyrias_website/templates/base.html
new file mode 100644
index 0000000..7664e57
--- /dev/null
+++ b/kyrias_website/templates/base.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<html lang="en">
+ <head>
+ <meta charset="UTF-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1">
+ {% assets "css_all" %}
+ <link rel="stylesheet" type="text/css" href="{{ ASSET_URL }}">
+ {% endassets %}
+{% block head %}{% endblock %}
+ </head>
+
+ <body>
+ <header class="padded-box">
+ <nav>
+ <ul>
+ <li><a href="{{ url_for("index") }}">index</a></li>
+ <li><a href="{{ url_for("archive") }}">archive</a></li>
+ <li><a href="{{ url_for("tags") }}">tags</a></li>
+ <li><a href="{{ url_for("page", path='about') }}">about</a></li>
+ </ul>
+ </nav>
+ </header>
+ <hr />
+ <div class="padded-box">
+{% block content %}{% endblock %}
+ </div>
+ <hr />
+ <footer id="bottom" class="padded-box">
+ <div>
+ <p id="copy">© 2017 Johannes Löthberg</p>
+ </div>
+
+ <div>
+ <ul id="contact-info">
+ <li><a rel="me" href="mailto:johannes@kyriasis.com">Email</a></li>
+ <li><a rel="me" href="https://twitter.com/kyriasis">Twitter</a></li>
+ </ul>
+ </div>
+ </footer>
+ </body>
+</html>
diff --git a/kyrias_website/templates/entry.html b/kyrias_website/templates/entry.html
new file mode 100644
index 0000000..8391505
--- /dev/null
+++ b/kyrias_website/templates/entry.html
@@ -0,0 +1,41 @@
+{% extends 'base.html' %}
+
+{% block head %}
+<title>{{ entry.title }}</title>
+{% endblock %}
+
+{% block content %}
+<article itemscope itemtype="http://schema.org/Article">
+ <header>
+ <h1>{{ entry.title }}</h1>
+ <time itemprop="datePublished" datetime="{{ entry.meta.date }}">{{ entry.meta.date }}</time>
+ </header>
+ <div>
+ {{ entry.html|safe }}
+ </div>
+ <footer>
+ {% if 'tags' in entry.meta %}
+ <div id="tags">
+ Tags:
+ {% set comma = joiner(",") %}
+ {% for tag in entry.meta.tags %}{{ comma() }}
+ <a href="{{ url_for('tags', _anchor=tag) }}">{{ tag }}</a>{% endfor %}
+ </div>
+ {% endif %}
+ </footer>
+</article>
+{% if older or newer %}
+<nav>
+<div id="post-nav">
+{% if older and newer %}
+ <a href="{{ url_for('entry', name=older.path) }}">Older ({{ older.meta.title }})</a> — <a href="{{ url_for('entry', name=newer.path) }}">Newer ({{ newer.meta.title }})</a>
+{% elif older %}
+ <a href="{{ url_for('entry', name=older.path) }}">Older ({{ older.meta.title }})</a>
+{% elif newer %}
+ <a href="{{ url_for('entry', name=newer.path) }}">Newer ({{ newer.meta.title }})</a>
+{% endif %}
+</div>
+</nav>
+{% endif %}
+{% endblock %}
+
diff --git a/kyrias_website/templates/page.html b/kyrias_website/templates/page.html
new file mode 100644
index 0000000..cca59af
--- /dev/null
+++ b/kyrias_website/templates/page.html
@@ -0,0 +1,27 @@
+{% extends 'base.html' %}
+
+{% block head %}
+<title>{{ page.title }}</title>
+{% endblock %}
+
+{% block content %}
+<article itemscope itemtype="http://schema.org/Article">
+ <header>
+ <h1>{{ page.title }}</h1>
+ <time itemprop="datePublished" datetime="{{ page.meta.date }}">{{ page.meta.date }}</time>
+ </header>
+ <div>
+ {{ page.html|safe }}
+ </div>
+ <footer>
+ {% if 'tags' in page.meta %}
+ <div id="tags">
+ Tags:
+ {% set comma = joiner(",") %}
+ {% for tag in page.meta.tags %}{{ comma() }}
+ <a href="{{ url_for('tags', _anchor=tag) }}">{{ tag }}</a>{% endfor %}
+ </div>
+ {% endif %}
+ </footer>
+</article>
+{% endblock %}
diff --git a/kyrias_website/templates/tags.html b/kyrias_website/templates/tags.html
new file mode 100644
index 0000000..a553523
--- /dev/null
+++ b/kyrias_website/templates/tags.html
@@ -0,0 +1,19 @@
+{% extends 'base.html' %}
+
+{% block content %}
+<section>
+ <h1>Tags</h1>
+ {% for tag in tags %}
+ <div id="{{ tag }}">
+ <h2>{{ tag }} <a class="permalink" href="#{{ tag }}" title="Link to this tag">§</a></h2>
+
+ <ul id="tag-list">
+ {% for entry in tags[tag] %}
+ <li>{{ entry.meta.date }} — <a href="{{ url_for('entry', name=entry.path) }}">{{ entry.title }}</a></li>
+ {% endfor %}
+ </ul>
+ </div>
+ {% endfor %}
+</section>
+{% endblock %}
+
diff --git a/kyrias_website/util.py b/kyrias_website/util.py
new file mode 100644
index 0000000..10ede77
--- /dev/null
+++ b/kyrias_website/util.py
@@ -0,0 +1,14 @@
+from docutils.core import publish_parts
+
+def rst_renderer(text):
+ settings_overrides = {
+ 'footnote_references': 'superscript',
+ 'auto_id_prefix': 'id-',
+ 'initial_header_level': 3,
+ 'doctitle_xform': False,
+ }
+
+ parts = publish_parts(source=text,
+ writer_name='html5_polyglot',
+ settings_overrides=settings_overrides)
+ return parts['fragment']
diff --git a/kyrias_website/views.py b/kyrias_website/views.py
new file mode 100644
index 0000000..a7e7555
--- /dev/null
+++ b/kyrias_website/views.py
@@ -0,0 +1,38 @@
+from kyrias_website import app
+from flask import render_template
+
+
+# Render all static pages as fallback
+@app.route('/<path:path>/')
+def page(path):
+ page = app.pages.get_or_404(path)
+ return render_template('page.html', page=page)
+
+
+@app.route('/blog/')
+@app.route('/')
+def index():
+ current = app.journal_entries[-1]
+ older = next(iter(app.journal_entries[-2:-1]), None)
+ return render_template('entry.html', entry=current, older=older)
+
+
+@app.route('/archive/')
+def archive():
+ return render_template('archive.html', entries=app.journal_entries)
+
+
+@app.route('/tags/')
+def tags():
+ return render_template('tags.html', tags=app.journal_tags)
+
+
+@app.route('/blog/<path:name>/')
+def entry(name):
+ entry = app.journal.get_or_404(name)
+
+ index = app.journal_entries.index(entry)
+ older = next(iter(app.journal_entries[index-1:index]), None)
+ newer = next(iter(app.journal_entries[index+1:index+2]), None)
+
+ return render_template('entry.html', entry=entry, older=older, newer=newer)