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 # 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('//') def main(name): return redirect(url_for("pad", name=name)) @APP.route('//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('//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('//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('//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('//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('//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('//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('//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('//stylesheet.css') def css(name): css = get_pad_content(name, '.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('//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('//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('//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('//pagedjs.html') def pagedjs(name): # TO GENERATE THE PAGED.JS PAGE md_pad_content = get_pad_content(name, ext='.md') 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('//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 = [] for line in md_pad_content.splitlines(): if ".mp3" in line: audio.append(line) elif ".ogg" in line: audio.append(line) return render_template_string(wildcard_template_pad_content, name=name.strip(), audio=audio) @APP.route('//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)