Browse Source

render markdown serverside and put it in the noscript tag for people

with no JS
pull/1/head
wes 7 years ago
parent
commit
72df80489e
  1. 2
      blog.ini
  2. 2
      blog.service
  3. 2
      requirements.txt
  4. 41
      src/posts.py
  5. 6
      src/scripts/app.tag
  6. 2
      src/scripts/editor.js
  7. 2
      src/scripts/post.tag
  8. 2
      src/scripts/riotblog.js
  9. 6
      src/templates/index.html
  10. 13
      src/website.py

2
blog.ini

@ -8,7 +8,7 @@ uid = http
gid = http gid = http
master = true master = true
processes = 5 processes = 7
socket = /tmp/blog.sock socket = /tmp/blog.sock
chown-socket = http:http chown-socket = http:http

2
blog.service

@ -6,7 +6,7 @@ After=network.target
User=http User=http
Group=http Group=http
WorkingDirectory=/srv/http/riotblog WorkingDirectory=/srv/http/riotblog
ExecStart=/usr/bin/uwsgi --ini /srv/http/riotblog/blog.ini ExecStart=/usr/bin/uwsgi --single-interpreter --ini /srv/http/riotblog/blog.ini
Environment="RIOTBLOG_SETTINGS=/srv/http/riotblog/riotblog_prod.cfg" Environment="RIOTBLOG_SETTINGS=/srv/http/riotblog/riotblog_prod.cfg"
[Install] [Install]

2
requirements.txt

@ -23,6 +23,7 @@ Jinja2==2.9.4
lxml==3.7.2 lxml==3.7.2
MarkupSafe==0.23 MarkupSafe==0.23
marshmallow==2.13.5 marshmallow==2.13.5
mistune==0.7.4
packaging==16.8 packaging==16.8
paramiko==2.1.1 paramiko==2.1.1
pbr==1.10.0 pbr==1.10.0
@ -34,6 +35,7 @@ python-dateutil==2.6.0
python-memcached==1.58 python-memcached==1.58
pyxdg==0.25 pyxdg==0.25
requests==2.18.3 requests==2.18.3
simplejson==3.11.1
six==1.10.0 six==1.10.0
stevedore==1.20.0 stevedore==1.20.0
urllib3==1.22 urllib3==1.22

41
src/posts.py

@ -1,11 +1,21 @@
#! /usr/bin/python #! /usr/bin/python
import couchdb import couchdb
import mistune
from werkzeug.local import Local, LocalProxy, LocalManager
from couchdb.http import ResourceConflict, ResourceNotFound from couchdb.http import ResourceConflict, ResourceNotFound
from flask import jsonify from flask import jsonify, g
from flask_marshmallow import Marshmallow from flask_marshmallow import Marshmallow
def get_mistune():
markdown = getattr(g, "markdown", None)
if markdown is None:
markdown = g._markdown = mistune.Markdown()
return markdown
markdown = LocalProxy(get_mistune)
class Posts: class Posts:
def __init__(self, user, password, host=None, port=None): def __init__(self, user, password, host=None, port=None):
if host is None: if host is None:
@ -41,11 +51,19 @@ class Posts:
results = self.db.iterview("blogPosts/blog-posts", 1, include_docs=True, startkey=_id) results = self.db.iterview("blogPosts/blog-posts", 1, include_docs=True, startkey=_id)
post = [result.doc for result in results][0] post = [result.doc for result in results][0]
post["content"] = markdown(post["content"])
return jsonify(post) if json else post return jsonify(post) if json else post
def getinitial(self): def getinitial(self):
results = list(self.db.iterview("blogPosts/blog-posts", 2, include_docs=True)) results = list(self.db.iterview("blogPosts/blog-posts", 2, include_docs=True))
return [result.doc for result in results][0]
post = [result.doc for result in results][0]
post["content"] = markdown(post["content"])
return post
def iterpost(self, endkey=False, startkey=False, category="programming"): def iterpost(self, endkey=False, startkey=False, category="programming"):
if startkey and not endkey: if startkey and not endkey:
@ -55,22 +73,25 @@ class Posts:
else: else:
results = self.db.iterview("blogPosts/blog-posts", 2, include_docs=True) results = self.db.iterview("blogPosts/blog-posts", 2, include_docs=True)
doc_ids = [result.doc for result in results] docs = [result.doc for result in results]
for doc in docs:
doc["content"] = markdown(doc["content"])
if not doc_ids: if not docs:
return jsonify("end") return jsonify("end")
if endkey and not startkey: if endkey and not startkey:
if len(doc_ids) < 2 or doc_ids[0] == endkey: if len(docs) < 2 or docs[0] == endkey:
return jsonify("start") return jsonify("start")
return jsonify(doc_ids[-2]) return jsonify(docs[-2])
if len(doc_ids) == 1: if len(docs) == 1:
return jsonify(doc_ids[0]) return jsonify(docs[0])
if doc_ids: if docs:
# if no startkey or endkey were specified, return the 0th post # if no startkey or endkey were specified, return the 0th post
return jsonify(doc_ids[1 if startkey else 0]) return jsonify(docs[1 if startkey else 0])
return jsonify("end") return jsonify("end")

6
src/scripts/app.tag

@ -27,7 +27,7 @@
class="mobile-menu tab tab-block menu"> class="mobile-menu tab tab-block menu">
<li <li
each="{page in ['posts', 'projects', 'links', 'about']}" each="{page in ['posts', 'projects', 'links', 'about']}"
class={"navigate-tab tab-item animated fadeIn " + (parent.active.get(page) ? "active" : "")} class={"navigate-tab tab-item " + (parent.active.get(page) ? "active" : "")}
data-is="navtab" data-is="navtab"
active={parent.active.get(page)} active={parent.active.get(page)}
to={parent.to(page)} to={parent.to(page)}
@ -41,7 +41,7 @@
<ul class="hide-md hide-sm hide-xs navigate tab tab-block"> <ul class="hide-md hide-sm hide-xs navigate tab tab-block">
<li <li
each="{page in ['posts', 'projects', 'links', 'about']}" each="{page in ['posts', 'projects', 'links', 'about']}"
class={"navigate-tab tab-item animated fadeIn " + (parent.active.get(page) ? "active" : "")} class={"navigate-tab tab-item " + (parent.active.get(page) ? "active" : "")}
data-is="navtab" data-is="navtab"
active={parent.active.get(page)} active={parent.active.get(page)}
to={parent.to(page)} to={parent.to(page)}
@ -129,7 +129,7 @@ self.state = {
"author" : self.opts.author, "author" : self.opts.author,
"title" : self.opts.title, "title" : self.opts.title,
"loaded" : false, "loaded" : false,
"initial" : decodeURIComponent(self.opts.initial_post), "initial" : document.getElementsByTagName("noscript")[0].textContent,
"links" : JSON.parse(decodeURIComponent(self.opts.links)) "links" : JSON.parse(decodeURIComponent(self.opts.links))
}; };

2
src/scripts/editor.js

@ -2,11 +2,13 @@ import riot from 'riot';
import { default as RiotControl } from 'riotcontrol'; import { default as RiotControl } from 'riotcontrol';
import { default as promise } from 'es6-promise'; import { default as promise } from 'es6-promise';
import { default as smooth } from 'smoothscroll-polyfill'; import { default as smooth } from 'smoothscroll-polyfill';
import { default as showdown } from 'showdown';
import 'element-closest'; import 'element-closest';
promise.Promise.polyfill(); promise.Promise.polyfill();
smooth.polyfill(); smooth.polyfill();
window.showdown = showdown;
window.RiotControl = RiotControl; window.RiotControl = RiotControl;
RiotControl.addStore(new riot.observable()); RiotControl.addStore(new riot.observable());

2
src/scripts/post.tag

@ -23,7 +23,7 @@
</social> </social>
<p class="post-content centered text-break"> <p class="post-content centered text-break">
<raw <raw
content="{ window.converter.makeHtml(content) }" content="{ content }"
> >
</raw> </raw>
</p> </p>

2
src/scripts/riotblog.js

@ -6,13 +6,11 @@ import './projects.tag';
import './app.tag'; import './app.tag';
import './grid.js'; import './grid.js';
import { default as promise } from 'es6-promise'; import { default as promise } from 'es6-promise';
import { default as showdown } from 'showdown';
import { default as smooth } from 'smoothscroll-polyfill'; import { default as smooth } from 'smoothscroll-polyfill';
import 'element-closest'; import 'element-closest';
import fetchCached from 'fetch-cached'; import fetchCached from 'fetch-cached';
import 'whatwg-fetch'; import 'whatwg-fetch';
window.converter = new showdown.Converter();
window.cache = {}; window.cache = {};
window.riot = riot; window.riot = riot;
window.RiotControl = RiotControl; window.RiotControl = RiotControl;

6
src/templates/index.html

@ -5,6 +5,7 @@
<title>{{ postcontent["title"] }}</title> <title>{{ postcontent["title"] }}</title>
</head> </head>
<body> <body>
<noscript>{{ postcontent['content']|safe }}</noscript>
<div data-is="app"></div> <div data-is="app"></div>
<footer> <footer>
<script async type="text/javascript" src="/scripts/riotblog.min.js"></script> <script async type="text/javascript" src="/scripts/riotblog.min.js"></script>
@ -16,7 +17,7 @@
} }
</script> </script>
<div id="fb-root"></div> <div id="fb-root"></div>
<script>window.twttr=function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0],t=window.twttr||{};if(d.getElementById(id))return t;js=d.createElement(s);js.id=id;js.async=true;js.src="https://platform.twitter.com/widgets.js";fjs.parentNode.insertBefore(js,fjs);t._e=[];t.ready=function(f){t._e.push(f)};return t}(document,"script","twitter-wjs");(function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0];if(d.getElementById(id))return;js=d.createElement(s);js.id=id;js.async=true;js.src="//connect.facebook.net/en_US/sdk.js#xfbml=1&version=v2.8";fjs.parentNode.insertBefore(js,fjs)})(document,"script","facebook-jssdk");</script> <script>window.twttr=function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0],t=window.twttr||{};if(d.getElementById(id))return t;js=d.createElement(s);js.id=id;js.async=true;js.src="https://platform.twitter.com/widgets.js";fjs.parentNode.insertBefore(js,fjs);t._e=[];t.ready=function(f){t._e.push(f)};return t}(document,"script","twitter-wjs");(function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0];if(d.getElementById(id))return;js=d.createElement(s);js.id=id;js.async=true;js.src="//connect.facebook.net/en_US/all.js#xfbml=1&version=v2.8";fjs.parentNode.insertBefore(js,fjs)})(document,"script","facebook-jssdk");</script>
<script> <script>
window.addEventListener("load", function() { window.addEventListener("load", function() {
window.riot.mount("app", window.riot.mount("app",
@ -26,8 +27,7 @@
author : "{{ postcontent['author'] }}", author : "{{ postcontent['author'] }}",
postid : "{{ postid }}", postid : "{{ postid }}",
title : "{{ postcontent['title'] }}", title : "{{ postcontent['title'] }}",
csrf_token : "{{ csrf_token() }}", csrf_token : "{{ csrf_token() }}"
initial_post: "{{ quote(postcontent['content']) }}"
}); });
}); });
</script> </script>

13
src/website.py

@ -95,10 +95,11 @@ def NeverWhere(configfile=None):
@cache.cached(timeout=50) @cache.cached(timeout=50)
@app.route("/blog/posts/", methods=("GET",)) @app.route("/blog/posts/", methods=("GET",))
def renderInitial(): def renderInitial():
post = dict(initial_post)
return render_template("index.html", return render_template("index.html",
postid=initial_post["_id"], postid=initial_post["_id"],
page="posts", page="posts",
postcontent=dict(initial_post)) postcontent=post)
@cache.cached(timeout=50) @cache.cached(timeout=50)
@app.route("/blog/projects", methods=("GET",)) @app.route("/blog/projects", methods=("GET",))
@ -126,16 +127,18 @@ def NeverWhere(configfile=None):
return renderInitial() return renderInitial()
# get the next post # get the next post
@cache.cached(timeout=50)
@app.route("/blog/posts/<_id>", methods=("GET",)) @app.route("/blog/posts/<_id>", methods=("GET",))
def renderPost(_id): def renderPost(_id):
post_content = loads( post_content = dict(loads(
cacheit(_id, cacheit(_id,
lambda: dumps(posts.getpost(_id, json=False))) lambda: dumps(posts.getpost(_id, json=False)))
) ))
return render_template("index.html", return render_template("index.html",
page="posts", page="posts",
postcontent=dict(post_content)) postcontent=post_content)
@cache.cached(timeout=50) @cache.cached(timeout=50)
@ -160,7 +163,7 @@ def NeverWhere(configfile=None):
def allposts(): def allposts():
return posts.allposts() return posts.allposts()
@cache.cached(timeout=50) @cache.cached(timeout=10000)
@app.route("/blog/categories") @app.route("/blog/categories")
def categories(): def categories():
return posts.categories() return posts.categories()

Loading…
Cancel
Save