Collective PDF rendering environment (work-in-progress) https://cc.vvvvvvaria.org/wiki/Octomode
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

366 lines
13 KiB

import os
import json
from flask import Flask, request, render_template, render_template_string, redirect, url_for
from urllib.request import urlopen
from urllib.parse import urlencode, urlparse
import subprocess
from jinja2 import Template
import re
# To sanitize Flask input fields
from markupsafe import Markup, escape
# To sanitize Markdown input
import pypandoc
# import bleach
# To read the Markdown metadat
import markdown
APP = Flask(__name__)
APP.config.from_pyfile('settings.py')
# ---
def get_pad_content(pad_name, ext=""):
if ext:
pad_name = f'{ pad_name }{ ext }'
# print(pad_name)
arguments = {
'padID' : pad_name,
'apikey' : APP.config['PAD_API_KEY']
}
api_call = 'getText'
response = json.load(urlopen(f"{ APP.config['PAD_API_URL'] }/{ api_call }", data=urlencode(arguments).encode()))
# create pad in case it does not yet exist
if response['code'] == 1 and 'padID does not exist' == response['message']:
api_call = 'createPad'
urlopen(f"{ APP.config['PAD_API_URL'] }/{ api_call }", data=urlencode(arguments).encode())
api_call = 'getText'
response = json.load(urlopen(f"{ APP.config['PAD_API_URL'] }/{ api_call }", data=urlencode(arguments).encode()))
content = response['data']['text']
return content
def all_pads():
arguments = {
'apikey' : APP.config['PAD_API_KEY'],
}
api_call = 'listAllPads'
response = json.load(urlopen(f"{ APP.config['PAD_API_URL'] }/{ api_call }", data=urlencode(arguments).encode()))
return response
def create_pad_on_first_run(name, ext):
pads = all_pads()
pad = name+ext
if pad not in pads['data']['padIDs']:
# Select default template
if 'md' in ext:
default_template = 'templates/default.md'
elif 'css' in ext:
default_template = 'templates/default.css'
elif 'pandoc-template' in ext:
default_template = 'templates/default.pandoc-template.html'
elif 'wildcard-template' in ext:
default_template = 'templates/default.wildcard-template.html'
default_template = open(default_template).read()
# Create pad and add the default template
arguments = {
'padID' : pad,
'apikey' : APP.config['PAD_API_KEY'],
'text' : default_template
}
api_call = 'createPad'
json.load(urlopen(f"{ APP.config['PAD_API_URL'] }/{ api_call }", data=urlencode(arguments).encode()))
def md_to_html(md_pad_content, name):
# Convert Markdown to HTML, using the pandoc template from the NAME.template.html pad
# ----------------------------------------------------------------------------------
#app_hostname = get_app_hostname()
#app_root = get_app_root()
#template_url = f"{ app_hostname }{ app_root }/{ name }/template.html"
#template_url = "http://127.0.0.1:5555/octomode/techno/template.html"
#md_url = f"{ app_hostname }{ app_root }/{ name }/pad.md"
#md_url = "http://127.0.0.1:5555/octomode/techno/pad.md"
#APP.logger.info("template_url", template_url)
#APP.logger.info("md_url", md_url)
#result = subprocess.run(["pandoc", "--from", "markdown", "--to", "html", "--template", f"{ template_url }", md_url], capture_output=True, text=True)
#html = result.stdout
#APP.logger.info("html", html)
#APP.logger.info("result.stderr", result.stderr)
# Convert Markdown to HTML, using the default pandoc template
# ----------------------------------------------------------------------------------
html = pypandoc.convert_text(md_pad_content, 'html', format='md')
# Sanitize the Markdown
# html = bleach.clean(html)
# Another built-in Flask way to sanitize
# html = escape(html)
html = Markup(html)
return html
def get_md_metadata(md_pad_content):
# Read the metadata from the Markdown
md = markdown.Markdown(extensions=['meta'])
md.convert(md_pad_content)
metadata = md.Meta
return metadata
def get_app_root():
# we need application root to make all the URLs work.....
if APP.config['APPLICATION_ROOT'] == '/':
app_root = ''
elif APP.config['APPLICATION_ROOT'].endswith('/'):
app_root = APP.config['APPLICATION_ROOT'][:-1]
else:
app_root = APP.config['APPLICATION_ROOT']
return app_root
def get_app_hostname():
# function to get the hostname that the application runs on
# for example: localhost:5555, or https://cc.vvvvvvaria.org
# TODO: not working atm, this always returns localhost:5555 ......
# --------------------------------------------
# app_protocol = urlparse(request.base_url).scheme + "://"
# app_url = urlparse(request.base_url).netloc
# app_hostname = app_protocol + app_url
app_hostname = APP.config['APPLICATION_HOSTNAME']
return app_hostname
# ---
@APP.route('/', methods=['GET', 'POST'])
def index():
name = False
if request.values.get('name'):
name = escape(request.values.get('name')) # Returns a Markup() object, which is "None" when False
if name:
# This is when the environment is "created"
# The pads are filled with the default templates (pad, stylesheet, template)
exts = ['.md', '.css', '.pandoc-template.html', '.wildcard-template.html']
for ext in exts:
create_pad_on_first_run(name, ext)
return redirect(url_for("pad", name=name))
else:
return render_template('start.html', pad_url=APP.config['PAD_URL'])
@APP.route('/<name>/')
def main(name):
return redirect(url_for("pad", name=name))
@APP.route('/<name>/pad/')
def pad(name):
url = f"{ APP.config['PAD_URL'] }/{ name }.md"
return render_template('iframe.html', url=url, name=name.strip(), pad_url=APP.config['PAD_URL'])
@APP.route('/<name>/stylesheet/')
def stylesheet(name):
url = f"{ APP.config['PAD_URL'] }/{ name }.css"
return render_template('iframe.html', url=url, name=name.strip(), pad_url=APP.config['PAD_URL'])
@APP.route('/<name>/pandoc-template/')
def pandoctemplate(name):
url = f"{ APP.config['PAD_URL'] }/{ name }.pandoc-template.html"
return render_template('iframe.html', url=url, name=name.strip(), pad_url=APP.config['PAD_URL'])
@APP.route('/<name>/html/')
def html(name):
app_root = get_app_root()
url = f"{ app_root }/{ name }/preview.html"
return render_template('iframe.html', url=url, name=name.strip())
@APP.route('/<name>/pdf/')
def pdf(name):
app_root = get_app_root()
url = f"{ app_root }/{name}/pagedjs.html"
return render_template('pdf.html', url=url, name=name.strip(), pad_url=APP.config['PAD_URL'])
@APP.route('/<name>/pdfmyk/')
def pdfmyk(name):
app_root = get_app_root()
url = f"{ app_root }/{name}/pagedjs-myk.html"
return render_template('pdf.html', url=url, name=name.strip(), pad_url=APP.config['PAD_URL'])
@APP.route('/<name>/pdfck/')
def pdfck(name):
app_root = get_app_root()
url = f"{ app_root }/{name}/pagedjs-ck.html"
return render_template('pdf.html', url=url, name=name.strip(), pad_url=APP.config['PAD_URL'])
@APP.route('/<name>/wildcard-template/')
def wildcardtemplate(name):
url = f"{ APP.config['PAD_URL'] }/{ name }.wildcard-template.html"
return render_template('iframe.html', url=url, name=name.strip(), pad_url=APP.config['PAD_URL'])
@APP.route('/<name>/wildcard/')
def wildcard(name):
app_root = get_app_root()
url = url_for("wildcardpage", name=name)
return render_template('iframe.html', url=url, name=name.strip())
@APP.route('/<name>/podcast/')
def podcast(name):
app_root = get_app_root()
url = f"{ app_root }/{ name }/podcast.rss"
return render_template('iframe.html', url=url, name=name.strip())
# //////////////////
# RENDERED RESOURCES
# //////////////////
# (These are not saved as a file on the server)
@APP.route('/<name>/stylesheet.css')
def css(name):
css = get_pad_content(name, ext='.css')
# Insert CSS sanitizer here.
return css, 200, {'Content-Type': 'text/css; charset=utf-8'}
# only used for the pandoc command using the subprocess
# ---
#@APP.route('/<name>/pad.md')
#def md(name):
# # TO GENERATE THE CONTENT IN MARKDOWN
# template_pad_content = get_pad_content(name, ext='.md')
#
# return template_pad_content, 200, {'Content-Type': 'text/plain; charset=utf-8'}
@APP.route('/<name>/pandoc-template.html')
def pandoctemplatehtml(name):
# TO GENERATE THE TEMPLATE AS HTML FILE
template_pad_content = get_pad_content(name, ext='.pandoc-template.html')
return template_pad_content, 200, {'Content-Type': 'text/html; charset=utf-8'}
@APP.route('/<name>/preview.html')
def preview(name):
# TO GENERATE THE PREVIEW WEBPAGE
md_pad_content = get_pad_content(name, ext='.md')
html = md_to_html(md_pad_content, name)
metadata = get_md_metadata(md_pad_content)
if metadata:
lang = metadata['language'][0]
title = metadata['title'][0]
else:
lang = "en"
title = "No title"
return render_template('preview.html', name=name.strip(), pad_content=html, lang=lang, title=title)
@APP.route('/<name>/pagedjs.html')
def pagedjs(name):
# TO GENERATE THE PAGED.JS PAGE
md_pad_content = get_pad_content(name, ext='.md')
### HACKSS START HERE
md_pad_content=re.sub(r"(!.+/publication/images/)(.+)(\){.colorz})", r"\1.myk-ck/\2.jpg\3", md_pad_content)
md_pad_content=re.sub(r"(!.+/publication/images/)(.+)(\){.mono})", r"\1.ck/\2-col.jpg\3", md_pad_content)
md_pad_content=re.sub(r"(!.+/publication/images/)(.+)(\){.othermono})", r"\1.myk/\2-col.jpg\3", md_pad_content)
### HACKSS END HERE
html = md_to_html(md_pad_content, name)
metadata = get_md_metadata(md_pad_content)
lang = metadata['language'][0]
title = metadata['title'][0]
return render_template('pagedjs.html', name=name.strip(), pad_content=html, lang=lang, title=title)
@APP.route('/<name>/pagedjs-myk.html')
def pagedjsmyk(name):
# TO GENERATE THE PAGED.JS PAGE
md_pad_content = get_pad_content(name, ext='.md')
### HACKSS START HERE
md_pad_content=re.sub(r"(!.+/publication/images/)(.+)(\){.colorz})", r"\1.myk/\2.jpg\3", md_pad_content)
md_pad_content=re.sub(r"(!.+/publication/images/)(.+)(\){.othermono})", r"\1.myk/\2.jpg\3", md_pad_content)
### HACKSS END HERE
html = md_to_html(md_pad_content, name)
metadata = get_md_metadata(md_pad_content)
lang = metadata['language'][0]
title = metadata['title'][0]
return render_template('pagedjs-myk.html', name=name.strip(), pad_content=html, lang=lang, title=title)
@APP.route('/<name>/pagedjs-ck.html')
def pagedjsck(name):
# TO GENERATE THE PAGED.JS PAGE
md_pad_content = get_pad_content(name, ext='.md')
### HACKSS START HERE
md_pad_content=re.sub(r"(!.+/publication/images/)(.+)(\){.colorz})", r"\1.ck/\2.jpg\3", md_pad_content)
md_pad_content=re.sub(r"(!.+/publication/images/)(.+)(\){.mono})", r"\1.ck/\2.jpg\3", md_pad_content)
### HACKSS END HERE
html = md_to_html(md_pad_content, name)
metadata = get_md_metadata(md_pad_content)
lang = metadata['language'][0]
title = metadata['title'][0]
return render_template('pagedjs-ck.html', name=name.strip(), pad_content=html, lang=lang, title=title)
@APP.route('/<name>/wildcard.html')
def wildcardpage(name):
md_pad_content = get_pad_content(name, ext='.md')
wildcard_template_pad_content = get_pad_content(name, ext='.wildcard-template.html')
audio = []
pdf = []
images = []
allall = []
for line in md_pad_content.splitlines():
slines=re.split("\s|\"|\(|\)|>|<",line)
for sline in slines:
if "http" in sline and sline not in allall:
if ".mp3" in sline:
audio.append(sline)
allall.append(sline)
elif ".ogg" in sline:
audio.append(sline)
allall.append(sline)
elif ".pdf" in sline:
pdf.append(sline)
allall.append(sline)
elif ".jpg" in sline:
images.append(sline)
allall.append(sline)
return render_template_string(wildcard_template_pad_content, name=name.strip(), allall=allall, pdf=pdf, images=images, audio=audio)
@APP.route('/<name>/podcast.rss')
def rss(name):
# parse for audio links
md_pad_content = get_pad_content(name, ext='.md')
audio = []
for line in md_pad_content.splitlines():
if ".mp3" in line:
audio.append(line)
# parse metadata
metadata = get_md_metadata(md_pad_content)
title = metadata['title'][0]
app_url = get_app_hostname() + get_app_root()
octomode_link = app_url + url_for("main", name=name)
rss_link = app_url + url_for("rss", name=name)
# render podcast.rss template
with open("templates/podcast.rss", "r") as t:
template = Template(t.read())
rss = template.render(name=name.strip(), title=title, audio=audio, octomode_link=octomode_link, rss_link=rss_link)
return rss, 200, {'Content-Type': 'text/xml; charset=utf-8'}
# //////////////////
if __name__ == '__main__':
APP.debug = True
APP.env = "development"
APP.run(host="0.0.0.0", port=APP.config["PORTNUMBER"], threaded=True)