Browse Source

Initial commit with some functionality

* Readme is now relevant
* Pandoc converts everything in content/ and puts it in webResources
* Blog server can render pandoc output to a post template (though
my browser isn't formatting it correctly...)
master
Macoy Madson 3 years ago
parent
commit
03aa63ebec
  1. 130
      ContentConverter.py
  2. 3
      Generate_Certificates.sh
  3. 0
      ReadMe.org
  4. 103
      SimpleBlogServer.py
  5. 6
      content/TestPost.org
  6. 9
      templates/BlogPost.html
  7. 4
      webResources/TestPost.html
  8. 29
      webResources/styles.css

130
ContentConverter.py

@ -0,0 +1,130 @@
import os
import subprocess
contentDirectory = "content"
renderedDirectory = "webResources"
# Pairs of extension, pandoc read type
convertableContentTypes = [(".org", "org")]
contentExtensions = []
for contentType in convertableContentTypes:
contentExtensions.append(contentType[0])
contentExtensions = tuple(contentExtensions)
renderedContentTypes = [(".html", "html")]
defaultRenderedTypeIndex = 0
renderedContentExtensions = []
for contentType in renderedContentTypes:
renderedContentExtensions.append(contentType[0])
renderedContentExtensions = tuple(renderedContentExtensions)
def getContentReadType(contentFilename):
contentExtension = contentFilename[contentFilename.rfind("."):]
for contentType in convertableContentTypes:
if contentExtension == contentType[0]:
return contentType[1]
# Strip the content directory
def getContentLocalName(contentFilename):
return contentFilename[len(contentDirectory):]
# Strip the rendered directory
def getRenderedLocalName(filename):
return filename[len(renderedDirectory):]
def stripExtension(filename):
return filename[:filename.rfind(".")]
def contentFilenameToRenderedFilename(contentFilename):
return "{}{}{}".format(renderedDirectory,
stripExtension(getContentLocalName(contentFilename)),
renderedContentTypes[defaultRenderedTypeIndex][0])
def renderContent(contentFilename):
# Content modified recently; render it
print("\tRendering {}".format(contentFilename))
outputFilename = contentFilenameToRenderedFilename(contentFilename)
# TODO: Support multiple output formats?
subprocess.run(["pandoc",
"-r", getContentReadType(contentFilename),
"-w", renderedContentTypes[defaultRenderedTypeIndex][1],
"-o", outputFilename, contentFilename])
# List of all content files
contentCache = []
renderedCache = []
renderedDictionary = {}
def updateRenderedCache():
global renderedCache
renderedCache = []
# Get all rendered files
for root, dirs, files in os.walk(renderedDirectory):
for file in files:
if file.endswith(renderedContentExtensions):
renderedFile = os.path.join(root, file)
renderedCache.append(renderedFile)
# The path actually used to look up the content (strip '/' from front)
contentPath = getRenderedLocalName(stripExtension(renderedFile))[1:]
print("\t'{}' = '{}'".format(contentPath, renderedFile))
# No use for the value yet, we just want fast key lookups
renderedDictionary[contentPath] = renderedFile
def getRenderedBody(contentPath):
body = None
if contentPath in renderedDictionary:
renderedFilename = renderedDictionary[contentPath]
renderedFile = open(renderedFilename)
body = renderedFile.readlines()
body = "".join(body)
renderedFile.close()
return body
def checkForContentChange():
global contentCache
print("Checking for content updates...")
# Get all content files
contentCache = []
for root, dirs, files in os.walk(contentDirectory):
for file in files:
if file.endswith(contentExtensions):
contentCache.append(os.path.join(root, file))
updateRenderedCache()
numRenderedFiles = 0
# Compare timestamps to determine which files need regeneration
for contentFilename in contentCache:
contentModified = os.path.getmtime(contentFilename)
contentLocalName = getContentLocalName(contentFilename)
renderedFileFound = False
for renderedFile in renderedCache:
if stripExtension(renderedFile[len(renderedDirectory):]) != stripExtension(contentLocalName):
continue
renderedFileFound = True
renderedModified = os.path.getmtime(renderedFile)
# Modified content
if contentModified > renderedModified:
renderContent(contentFilename)
numRenderedFiles += 1
# New content file
if not renderedFileFound:
renderContent(contentFilename)
numRenderedFiles += 1
# We generated new content; make sure the cache is up-to-date
if numRenderedFiles:
updateRenderedCache()
print("Checking for content updates done. Rendered {} files".format(numRenderedFiles))

3
Generate_Certificates.sh

@ -0,0 +1,3 @@
#!/bin/bash
mkdir certificates
openssl req -x509 -nodes -days 365 -newkey rsa:1024 -keyout certificates/server_jupyter_based.crt.key -out certificates/server_jupyter_based.crt.pem

0
README.md → ReadMe.org

103
SimpleBlogServer.py

@ -0,0 +1,103 @@
#!/usr/bin/env python
import tornado.ioloop
import tornado.web
import tornado.websocket
import tornado.httpclient
import tornado.httpserver
import tornado.gen
import os
from datetime import datetime
import ContentConverter
#
# Tornado handlers
#
class HomeHandler(tornado.web.RequestHandler):
def get(self):
self.write('''<html><head><link rel="stylesheet" type="text/css" href="webResources/styles.css"></head>
<body><p>{}</p></body>
</html>'''.format("Hello, world!"))
class BlogHandler(tornado.web.RequestHandler):
def get(self, request):
contentTitle = "Blog: " + request
renderedBody = ContentConverter.getRenderedBody(request)
if not renderedBody:
renderedBody = "<p>The post under '{}' does not exist.</p>".format(request)
self.render("templates/BlogPost.html", title=contentTitle, postBody=renderedBody)
#
# Startup
#
def make_app():
return tornado.web.Application([
# Home page
(r'/', HomeHandler),
(r'/blog/(.*)', BlogHandler),
# # Handles messages for run script
# (r'/runScriptWebSocket', RunScriptWebSocket),
# Upload handler
# (r'/upload', UploadHandler),
# # Don't change this 'output' here without changing the other places as well
# (r'/output/(.*)', tornado.web.StaticFileHandler, {'path' : 'output'}),
# Static files. Keep this at the bottom because it handles everything else
# TODO put these in a subdir so everything isn't accessible
(r'/webResources/(.*)', tornado.web.StaticFileHandler, {'path' : 'webResources'}),
],
xsrf_cookies=True,
cookie_secret='this is my org blog')
if __name__ == '__main__':
# Before startup, convert anything which needs to be converted
ContentConverter.checkForContentChange()
port = 8888
print('\nStarting Simple Org Blog Server on port {}...'.format(port))
app = make_app()
#
# Notes on SSL
#
# Certificate generation (for localhost) (didn't actually work):
# https://medium.freecodecamp.org/how-to-get-https-working-on-your-local-development-environment-in-5-minutes-7af615770eec?gi=bd966500e56a
# Tornado instructions:
# https://stackoverflow.com/questions/18307131/how-to-create-https-tornado-server
# Note that I added the rootCA to Certificates trust in Firefox Preferences as well (didn't do anything)
#
# What I actually did:
# openssl req -x509 -nodes -days 365 -newkey rsa:1024 -keyout certificates/server_jupyter_based.crt.key -out certificates/server_jupyter_based.crt.pem
# (from https://jupyter-notebook.readthedocs.io/en/latest/public_server.html)
# I then had to tell Firefox to trust this certificate even though it is self-signing (because
# I want a free certificate for this non-serious project)
useSSL = True
if useSSL:
app.listen(port, ssl_options={"certfile":"certificates/server_jupyter_based.crt.pem",
"keyfile":"certificates/server_jupyter_based.crt.key"})
else:
# Show the warning only if SSL is not enabled
print('\n\tWARNING: Do NOT run this server on the internet (e.g. port-forwarded)'
' nor when\n\t connected to an insecure LAN! It is not protected against malicious use.\n')
app.listen(port)
ioLoop = tornado.ioloop.IOLoop.current()
# Periodically check to see if anything has changed which needs to be converted to .html
# Check every fifteen minutes
checkForContentChangeCallback = tornado.ioloop.PeriodicCallback(ContentConverter.checkForContentChange,
15 * 60 * 1000)
checkForContentChangeCallback.start()
ioLoop.start()

6
content/TestPost.org

@ -0,0 +1,6 @@
* Test post
This is a test post.
This is an update.
Here's another update

9
templates/BlogPost.html

@ -0,0 +1,9 @@
<html>
<head>
<title>{{ title }}</title>
<link rel="stylesheet" type="text/css" href="webResources/styles.css">
</head>
<body>
{{ postBody }}
</body>
</html>

4
webResources/TestPost.html

@ -0,0 +1,4 @@
<h1 id="test-post">Test post</h1>
<p>This is a test post.</p>
<p>This is an update.</p>
<p>Here's another update</p>

29
webResources/styles.css

@ -0,0 +1,29 @@
body {
margin: 0 auto;
width: 80%;
height: 100%;
background-color: #333333;
max-width: 800px;
}
p,
h1,
h2,
h3,
a,
li {
color: #cccccc;
}
label {
font-size: large;
color: #cccccc;
}
li {
margin-top: 0px;
margin-bottom: 0px;
margin-left: 20px;
font-size: small;
color: #aaaaaa;
}
Loading…
Cancel
Save