Compare commits

...

10 Commits

  1. 3
      .gitignore
  2. 2
      Makefile
  3. 4
      README.md
  4. 226
      octomode.py
  5. 1
      requirements.txt
  6. 1
      settings.py
  7. 33
      static/main.css
  8. 6614
      static/paged.polyfill.js
  9. 26
      templates/base.html
  10. 76
      templates/default.pandoc-template.html
  11. 15
      templates/default.wildcard-template.html
  12. 24
      templates/pagedjs-ck.html
  13. 23
      templates/pagedjs-myk.html
  14. 2
      templates/pagedjs.html
  15. 16
      templates/podcast.rss
  16. 11
      templates/preview.html
  17. 1
      templates/stylesheet.css

3
.gitignore

@ -1 +1,4 @@
.venv
.env
__pycache__
error.log

2
Makefile

@ -13,4 +13,4 @@ local:
action:
@if [ ! -f ".venv/bin/gunicorn" ]; then .venv/bin/pip install gunicorn; fi
@SCRIPT_NAME=${OCTOMODE_APPLICATION_ROOT} .venv/bin/gunicorn -b localhost:${OCTOMODE_PORTNUMBER} --reload octomode:APP
@SCRIPT_NAME=${OCTOMODE_APPLICATION_ROOT} .venv/bin/gunicorn --error-logfile error.log --capture-output -b localhost:${OCTOMODE_PORTNUMBER} --reload octomode:APP

4
README.md

@ -1,4 +1,6 @@
# octomode
# octomode's technodisobedience branch
Currently emerging from the Constant office ;).
> *work-in-progress*

226
octomode.py

@ -1,8 +1,11 @@
import os
import json
from flask import Flask, request, render_template, redirect, url_for
from flask import Flask, request, render_template, render_template_string, redirect, url_for
from urllib.request import urlopen
from urllib.parse import urlencode
from urllib.parse import urlencode, urlparse
import subprocess
from jinja2 import Template
import re
# To sanitize Flask input fields
from markupsafe import Markup, escape
@ -23,7 +26,7 @@ def get_pad_content(pad_name, ext=""):
if ext:
pad_name = f'{ pad_name }{ ext }'
print(pad_name)
# print(pad_name)
arguments = {
'padID' : pad_name,
@ -62,6 +65,10 @@ def create_pad_on_first_run(name, 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
@ -73,9 +80,24 @@ 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
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
@ -95,6 +117,30 @@ def get_md_metadata(md_pad_content):
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'])
@ -105,7 +151,7 @@ def index():
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', '.pandoc-template.html', '.wildcard-template.html']
for ext in exts:
create_pad_on_first_run(name, ext)
return redirect(url_for("pad", name=name))
@ -126,30 +172,52 @@ 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):
# only here 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']
app_root = get_app_root()
url = f"{ app_root }/{ name }/preview.html"
return render_template('iframe.html', url=url, name=name.strip(), pad_url=APP.config['PAD_URL'])
return render_template('iframe.html', url=url, name=name.strip())
@APP.route('/<name>/pdf/')
def pdf(name):
# only here 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']
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
# //////////////////
@ -157,16 +225,31 @@ def pdf(name):
@APP.route('/<name>/stylesheet.css')
def css(name):
css = get_pad_content(name, '.css')
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)
html = md_to_html(md_pad_content, name)
metadata = get_md_metadata(md_pad_content)
if metadata:
lang = metadata['language'][0]
@ -179,15 +262,102 @@ def preview(name):
@APP.route('/<name>/pagedjs.html')
def pagedjs(name):
# TO GENERATE THE PAGED.JS WEBPAGE
# TO GENERATE THE PAGED.JS PAGE
md_pad_content = get_pad_content(name, ext='.md')
html = md_to_html(md_pad_content)
### 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__':

1
requirements.txt

@ -1,4 +1,5 @@
flask
pandoc
pypandoc
markdown
python-dotenv

1
settings.py

@ -5,6 +5,7 @@ from dotenv import load_dotenv
load_dotenv()
# Bind them to Python variables
APPLICATION_HOSTNAME = os.environ.get('OCTOMODE_APPLICATION_HOSTNAME', 'http://localhost:5555')
APPLICATION_ROOT = os.environ.get('OCTOMODE_APPLICATION_ROOT', '/')
PORTNUMBER = int(os.environ.get('OCTOMODE_PORTNUMBER', 5001))
PAD_URL = os.environ.get('OCTOMODE_PAD_URL', 'https://pad.vvvvvvaria.org')

33
static/main.css

@ -1,14 +1,20 @@
@charset "utf-8";
:root{
--highlightcolor: #f3c6ff;
}
body{
min-width: 900px;
background-color: #8798d6;
}
/* GENERAL RULES */
/* main title element that says "in octomode" */
h1 em.octomode{
color: darkorchid;
color: var(--highlightcolor);
text-decoration: underline;
}
/* navigation */
@ -26,11 +32,32 @@ div#nav{
line-height: 0;
margin: 0.75em 15px;
float: left;
font-size: 32px;
z-index: -1;
}
div#nav div#buttons{
margin: 0.5em 15px;
float: right;
}
div#nav div#buttons a,
div#nav div#buttons a:visited,
div#nav div#buttons a.link{
text-decoration: none;
color: inherit;
}
div#nav div#buttons a.link{
padding-right: 0.25em;
}
div#nav div#buttons button{
padding: 0.2em 1em 0.4em;
font-variant-caps: all-small-caps;
font-family: monospace;
/* border: 2px groove var(--highlightcolor); */
/* border-radius: 1em; */
}
button:hover{
cursor: pointer;
}
div#nav span.info{
font-size: 16px;
line-height: 0;
@ -89,6 +116,10 @@ body.start-page *{
font-size: 115%;
font-weight: bold;
}
body.start-page h1 em.octomode{
font-size: 32px;
}
/* Z-INDEX */
div#wrapper,

6614
static/paged.polyfill.js

File diff suppressed because it is too large

26
templates/base.html

@ -25,16 +25,34 @@ window.addEventListener('load', function () {
<div id="buttons">
<a href="{{ url_for('pad', name=name) }}"><button>pad</button></a>
<span id="click_md" class="info" tabindex="1">🌐</span>
<div id="show_md" class="hidden"><input type="text" name="pad" value="{{ pad_url }}/{{ name }}.md"></div>
<a class="link" href="{{ pad_url }}/{{ name }}.md" target="_blank">🔗</a>
<a href="{{ url_for('stylesheet', name=name) }}"><button>stylesheet</button></a>
<span id="click_css" class="info" tabindex="1">🌐</span>
<div id="show_css" class="hidden"><input type="text" name="pad" value="{{ pad_url }}/{{ name }}.css"></div>
<a class="link" href="{{ pad_url }}/{{ name }}.css" target="_blank">🔗</a>
<!--
<a href="{{ url_for('pandoctemplate', name=name) }}"><button>pandoc template</button></a>
<a class="link" href="{{ pad_url }}/{{ name }}.pandoc-template.html" target="_blank">🔗</a>
-->
<a href="{{ url_for('html', name=name) }}"><button>html</button></a>
<a class="link" href="{{ url_for('preview', name=name) }}" target="_blank">🔗</a>
<a href="{{ url_for('pdf', name=name) }}"><button>pdf</button></a>
<a class="link" href="{{ url_for('pagedjs', name=name) }}" target="_blank">🔗</a>
<a href="{{ url_for('pdfck', name=name) }}"><button>pdf-ck</button></a>
<a class="link" href="{{ url_for('pagedjsck', name=name) }}" target="_blank">🔗</a>
<a href="{{ url_for('pdfmyk', name=name) }}"><button>pdf-myk</button></a>
<a class="link" href="{{ url_for('pagedjsmyk', name=name) }}" target="_blank">🔗</a>
<a href="{{ url_for('wildcardtemplate', name=name) }}"><button>* template</button></a>
<a class="link" href="{{ pad_url }}/{{ name }}.wildcard-template.html" target="_blank">🔗</a>
<a href="{{ url_for('wildcard', name=name) }}"><button>*</button></a>
<a class="link" href="{{ url_for('wildcardpage', name=name) }}" target="_blank"> 🔗</a>
</div>`;
document.body.insertBefore(nav, document.body.firstChild);

76
templates/default.pandoc-template.html

@ -0,0 +1,76 @@
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" lang="$lang$" xml:lang="$lang$"$if(dir)$ dir="$dir$"$endif$>
<head>
<meta charset="utf-8" />
<meta name="generator" content="pandoc" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" />
$for(author-meta)$
<meta name="author" content="$author-meta$" />
$endfor$
$if(date-meta)$
<meta name="dcterms.date" content="$date-meta$" />
$endif$
$if(keywords)$
<meta name="keywords" content="$for(keywords)$$keywords$$sep$, $endfor$" />
$endif$
$if(description-meta)$
<meta name="description" content="$description-meta$" />
$endif$
<title>$if(title-prefix)$$title-prefix$ – $endif$$pagetitle$</title>
<style>
$styles.html()$
</style>
$for(css)$
<link rel="stylesheet" href="$css$" />
$endfor$
$for(header-includes)$
$header-includes$
$endfor$
$if(math)$
$if(mathjax)$
<script src="https://polyfill.io/v3/polyfill.min.js?features=es6"></script>
$endif$
$math$
$endif$
<!--[if lt IE 9]>
<script src="//cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv-printshiv.min.js"></script>
<![endif]-->
</head>
<body>
$for(include-before)$
$include-before$
$endfor$
$if(title)$
<header id="title-block-header">
<h1 class="title">$title$</h1>
$if(subtitle)$
<p class="subtitle">$subtitle$</p>
$endif$
$for(author)$
<p class="author">$author$</p>
$endfor$
$if(date)$
<p class="date">$date$</p>
$endif$
$if(abstract)$
<div class="abstract">
<div class="abstract-title">$abstract-title$</div>
$abstract$
</div>
$endif$
</header>
$endif$
$if(toc)$
<nav id="$idprefix$TOC" role="doc-toc">
$if(toc-title)$
<h2 id="$idprefix$toc-title">$toc-title$</h2>
$endif$
$table-of-contents$
</nav>
$endif$
$body$
$for(include-after)$
$include-after$
$endfor$
</body>
</html>

15
templates/default.wildcard-template.html

@ -0,0 +1,15 @@
PODCAST RSS FEED FOR: {{ name }}
<br><br>
audio found:
<br><br>
{% for link in audio %}
<audio src="{{ link }}" controls></audio>
<br><br>
{% endfor %}

24
templates/pagedjs-ck.html

@ -0,0 +1,24 @@
<!DOCTYPE html>
<html lang="{{ lang }}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="{{ url_for('static', filename='paged.polyfill.js') }}" type="text/javascript"></script>
<link href="{{ url_for('static', filename='pagedjs.css') }}" rel="stylesheet" type="text/css" media="screen">
<link href="/octomode/{{ name }}/stylesheet.css" rel="stylesheet" type="text/css" media="print">
<title>{{ title }}</title>
<style>
:root{
--purple:#000000 !important;
--orange:transparent !important;
}
@page spreadLayout{
background:white !important;
}
</style>
</head>
<body class="only-ck">
{{ pad_content }}
</body>
</html>

23
templates/pagedjs-myk.html

@ -0,0 +1,23 @@
<!DOCTYPE html>
<html lang="{{ lang }}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="{{ url_for('static', filename='paged.polyfill.js') }}" type="text/javascript"></script>
<link href="{{ url_for('static', filename='pagedjs.css') }}" rel="stylesheet" type="text/css" media="screen">
<link href="/octomode/{{ name }}/stylesheet.css" rel="stylesheet" type="text/css" media="print">
<title>{{ title }}</title>
<style>
:root{
--orange: #000000 !important;
--purple: transparent !important;
}
@page cover{
background-color:white !important;
}
</style>
</head>
<body class='only-myk'>
{{ pad_content }}
</body>
</html>

2
templates/pagedjs.html

@ -5,7 +5,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="{{ url_for('static', filename='paged.polyfill.js') }}" type="text/javascript"></script>
<link href="{{ url_for('static', filename='pagedjs.css') }}" rel="stylesheet" type="text/css" media="screen">
<link href="/{{ name }}/stylesheet.css" rel="stylesheet" type="text/css" media="print">
<link href="/octomode/{{ name }}/stylesheet.css" rel="stylesheet" type="text/css" media="print">
<title>{{ title }}</title>
</head>
<body>

16
templates/podcast.rss

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
<channel>
<title>{{ title }}</title>
<link>{{ octomode_link }}</link>
<description>Podcast generated etcot.</description>
<lastBuildDate>builtdate..todo</lastBuildDate>
<atom:link href="{{ rss_link }}" rel="self" type="application/rss+xml" />
{% for link in audio %}
<item>
<pubDate>todo</pubDate>
<enclosure url="{{ link }}" type="audio/mpeg" />
</item>
{% endfor %}
</channel>
</rss>

11
templates/preview.html

@ -3,17 +3,10 @@
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link href="/{{ name }}/stylesheet.css" rel="stylesheet" type="text/css" media="screen">
<link href="/octomode/{{ name }}/stylesheet.css" rel="stylesheet" type="text/css" media="screen">
<title>{{ title }}</title>
</head>
<body>
<section id="cover">
<h1 id="cover_title">{{ title }}</h1>
<div id="cover_container"></div>
</section>
<section id="main">
<div id="wrapper">{{ pad_content }}</div>
</section>
{{ pad_content }}
</body>
</html>

1
templates/stylesheet.css

@ -0,0 +1 @@
{{ content }}
Loading…
Cancel
Save