From 760179744536564b64bff5f61a50d142dc1a2471 Mon Sep 17 00:00:00 2001 From: mb Date: Wed, 29 Nov 2023 19:06:13 +0100 Subject: [PATCH] very wip: adding template pad + links next to buttons + different background color for this branch to see the difference --- .gitignore | 2 + octomode.py | 58 ++++++++++++++++++------- static/main.css | 23 ++++++++-- templates/base.html | 15 ++++--- templates/default.template.html | 76 +++++++++++++++++++++++++++++++++ templates/preview.html | 9 +--- 6 files changed, 151 insertions(+), 32 deletions(-) create mode 100644 templates/default.template.html diff --git a/.gitignore b/.gitignore index 4c49bd7..32ac21b 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,3 @@ +.venv .env +__pycache__ diff --git a/octomode.py b/octomode.py index e4af7b2..0a97c51 100755 --- a/octomode.py +++ b/octomode.py @@ -2,7 +2,8 @@ import os import json from flask import Flask, request, render_template, redirect, url_for from urllib.request import urlopen -from urllib.parse import urlencode +from urllib.parse import urlencode, urlparse +import subprocess # To sanitize Flask input fields from markupsafe import Markup, escape @@ -23,9 +24,9 @@ def get_pad_content(pad_name, ext=""): if ext: pad_name = f'{ pad_name }{ ext }' - print(pad_name) + # print(pad_name) - arguments = { + arguments = { 'padID' : pad_name, 'apikey' : APP.config['PAD_API_KEY'] } @@ -43,7 +44,7 @@ def get_pad_content(pad_name, ext=""): return content def all_pads(): - arguments = { + arguments = { 'apikey' : APP.config['PAD_API_KEY'], } api_call = 'listAllPads' @@ -62,10 +63,12 @@ def create_pad_on_first_run(name, ext): default_template = 'templates/default.md' elif 'css' in ext: default_template = 'templates/default.css' + elif 'template' in ext: + default_template = 'templates/default.template.html' default_template = open(default_template).read() # Create pad and add the default template - arguments = { + arguments = { 'padID' : pad, 'apikey' : APP.config['PAD_API_KEY'], 'text' : default_template @@ -73,16 +76,22 @@ def create_pad_on_first_run(name, ext): 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): - # Convert Markdown to HTML - # html = markdown.markdown(md_pad_content, extensions=['meta', 'attr_list']) # attr_list does not work - html = pypandoc.convert_text(md_pad_content, 'html', format='md') +def md_to_html(md_pad_content, name): + # Convert Markdown to HTML, using the template from the NAME.template.html pad + protocol = urlparse(request.base_url).scheme + "://" + hostname = urlparse(request.base_url).netloc + domain = protocol + hostname + template_url = f"{ domain }{ APP.config['APPLICATION_ROOT'] }{ name }/template.html" + md_url = f"{ domain }{ APP.config['APPLICATION_ROOT'] }{ name }/pad.md" + # html = pypandoc.convert_text(md_pad_content, 'html', format='md', extra_args=[f'--template={ template_url }', '--standalone']) + result = subprocess.run(["pandoc", "--from", "markdown", "--to", "html", "--template", f"{ template_url }", md_url], capture_output=True, text=True) + html = result.stdout # Sanitize the Markdown # html = bleach.clean(html) # Another built-in Flask way to sanitize - # html = escape(html) + # html = escape(html) html = Markup(html) return html @@ -102,10 +111,10 @@ 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: + if name: # This is when the environment is "created" # The pads are filled with the default templates (pad, stylesheet, template) - exts = ['.md', '.css'] + exts = ['.md', '.css', '.template.html'] for ext in exts: create_pad_on_first_run(name, ext) return redirect(url_for("pad", name=name)) @@ -126,6 +135,11 @@ 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('//template/') +def template(name): + url = f"{ APP.config['PAD_URL'] }/{ name }.template.html" + return render_template('iframe.html', url=url, name=name.strip(), pad_url=APP.config['PAD_URL']) + @APP.route('//html/') def html(name): # only here we need application root to make all the URLs work..... @@ -151,7 +165,7 @@ def pdf(name): return render_template('pdf.html', url=url, name=name.strip(), pad_url=APP.config['PAD_URL']) # ////////////////// -# RENDERED RESOURCES +# RENDERED RESOURCES # ////////////////// # (These are not saved as a file on the server) @@ -162,11 +176,25 @@ def css(name): return css, 200, {'Content-Type': 'text/css; charset=utf-8'} +@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('//template.html') +def pandoctemplate(name): + # TO GENERATE THE PANDOC TEMPLATE + template_pad_content = get_pad_content(name, ext='.template.html') + + return template_pad_content, 200, {'Content-Type': 'text/plain; 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) + html = md_to_html(md_pad_content, name) metadata = get_md_metadata(md_pad_content) if metadata: lang = metadata['language'][0] @@ -181,7 +209,7 @@ def preview(name): def pagedjs(name): # TO GENERATE THE PAGED.JS WEBPAGE md_pad_content = get_pad_content(name, ext='.md') - html = md_to_html(md_pad_content) + html = md_to_html(md_pad_content, name) metadata = get_md_metadata(md_pad_content) lang = metadata['language'][0] title = metadata['title'][0] diff --git a/static/main.css b/static/main.css index 9139c36..27d3d97 100644 --- a/static/main.css +++ b/static/main.css @@ -1,14 +1,20 @@ -@charset "utf-8"; +@charset "utf-8"; + +:root{ + --highlightcolor: forestgreen; +} body{ min-width: 900px; + background-color: darkkhaki; } /* GENERAL RULES */ /* main title element that says "in octomode" */ h1 em.octomode{ - color: darkorchid; + color: var(--highlightcolor); + text-decoration: underline; } /* navigation */ @@ -31,6 +37,17 @@ div#nav{ margin: 0.5em 15px; float: right; } + div#nav div#buttons a.link{ + text-decoration: none; + } + div#nav div#buttons button{ + border: 2px groove var(--highlightcolor); + padding: 0.2em 1em 0.3em; + border-radius: 1em; + } + button:hover{ + cursor: pointer; + } div#nav span.info{ font-size: 16px; line-height: 0; @@ -48,7 +65,7 @@ div#nav{ div#nav input{ min-width: 300px; } - /* click logic (CSS only) */ + /* click logic (CSS only) */ span#click_md { cursor: pointer; } diff --git a/templates/base.html b/templates/base.html index db3da7b..2e3e278 100644 --- a/templates/base.html +++ b/templates/base.html @@ -24,17 +24,20 @@ window.addEventListener('load', function () {

{{ name }} in octomode

- - 🌐 - + + 🔗 - - 🌐 - + + 🔗 + + + 🔗 + 🔗 + 🔗
`; document.body.insertBefore(nav, document.body.firstChild); diff --git a/templates/default.template.html b/templates/default.template.html new file mode 100644 index 0000000..0c5fc2a --- /dev/null +++ b/templates/default.template.html @@ -0,0 +1,76 @@ + + + + + + +$for(author-meta)$ + +$endfor$ +$if(date-meta)$ + +$endif$ +$if(keywords)$ + +$endif$ +$if(description-meta)$ + +$endif$ + $if(title-prefix)$$title-prefix$ – $endif$$pagetitle$ + +$for(css)$ + +$endfor$ +$for(header-includes)$ + $header-includes$ +$endfor$ +$if(math)$ +$if(mathjax)$ + +$endif$ + $math$ +$endif$ + + + +$for(include-before)$ +$include-before$ +$endfor$ +$if(title)$ +
+

$title$

+$if(subtitle)$ +

$subtitle$

+$endif$ +$for(author)$ +

$author$

+$endfor$ +$if(date)$ +

$date$

+$endif$ +$if(abstract)$ +
+
$abstract-title$
+$abstract$ +
+$endif$ +
+$endif$ +$if(toc)$ + +$endif$ +$body$ +$for(include-after)$ +$include-after$ +$endfor$ + + diff --git a/templates/preview.html b/templates/preview.html index 4e23ea4..6f767fa 100644 --- a/templates/preview.html +++ b/templates/preview.html @@ -7,13 +7,6 @@ {{ title }} -
-

{{ title }}

-
-
- -
-
{{ pad_content }}
-
+{{ pad_content }}