Compare commits

..

No commits in common. "0b43103ab2c371c05785d5232d35ad87b2928550" and "e0d7d339c9c81f5c9db3e58651b6ad6015144cdd" have entirely different histories.

7 changed files with 134 additions and 126 deletions

View File

@ -1,7 +1,6 @@
# Proboards Website Generator # Proboards Website Generator
A quick way to visualise the data obtained by my A quick way to visualise the data obtained by my [proboards-saver](https://code.vanwa.ch/shu/Proboards-Saver) tool.
[proboards-saver](/shu/proboards-saver) tool.
Python3 with Jinja2 is needed. Python3 with Jinja2 is needed.

View File

@ -5,72 +5,45 @@
# To the extent possible under law, the author(s) have dedicated all copyright # To the extent possible under law, the author(s) have dedicated all copyright
# and related and neighboring rights to this software to the public domain # and related and neighboring rights to this software to the public domain
# worldwide. This software is distributed without any warranty. # worldwide. This software is distributed without any warranty.
# See http://creativecommons.org/publicdomain/zero/1.0/ for a description # See http://creativecommons.org/publicdomain/zero/1.0/ for a description of CC0.
# of CC0.
import argparse import argparse, json, os, re, shutil
import json
import os
import re
import shutil
from datetime import datetime from datetime import datetime
from jinja2 import Environment, FileSystemLoader from jinja2 import Environment, FileSystemLoader
css = """ import sys
body { #reload(sys); sys.setdefaultencoding('utf-8')
margin: 2em auto;
max-width: 50em;
line-height: 1.6;
font-size: 1.1em;
color: #444;
padding: 0 1em;
}
h1,h2,h3 {
line-height: 1.2;
}
"""
def fromunixtime(value): def fromunixtime(value):
return datetime.fromtimestamp(value).strftime('%Y-%m-%d %H:%M:%S') return datetime.fromtimestamp(value).strftime('%Y-%m-%d %H:%M:%S')
def url_replacer(value): def url_replacer(value):
return re.sub('[^a-zA-Z0-9\n\.]', '_', value) return value.replace('/', '_').replace(' ', '_').replace('?', '').replace('<', '').replace('>', '')
def user_replacer(match): def user_replacer(match):
return '[url=../../user/' + url_replacer(match.group(2)) + '.html]' + match.group(2) + '[/url]' return '[url=../../user/' + url_replacer(match.group(2)) + '.html]' + match.group(2) + '[/url]'
def tohtml(value): def tohtml(value):
value = value.replace('{{baseurl}}', 'static') value = value.replace('{{baseurl}}', 'static')
value = value.replace('\n', '<br />') value = value.replace('\n', '<br />')
value = re.sub(r'\[url=\/user\/(.*?)\](.*?)\[\/url\]', value = re.sub(r'\[url=\/user\/(.*?)\](.*?)\[\/url\]', user_replacer, value)
user_replacer, value) value = re.sub(r'\[url=(.*?)\](.*?)\[/url\]', r'<a href="\1">\2</a>', value)
value = re.sub(r'\[url=(.*?)\](.*?)\[/url\]',
r'<a href="\1">\2</a>', value)
value = re.sub(r'\[video\](.*?)\[/video\]', r'<a href="\1">\1</a>', value) value = re.sub(r'\[video\](.*?)\[/video\]', r'<a href="\1">\1</a>', value)
value = re.sub(r'\[colour=(.*?)\](.*?)\[/colour\]', value = re.sub(r'\[color=(.*?)\](.*?)\[/color\]', r'<font color="\1">\2</font>', value)
r'<font color="\1">\2</font>', value)
value = re.sub(r'\[b\](.*?)\[/b\]', r'<b>\1</b>', value) value = re.sub(r'\[b\](.*?)\[/b\]', r'<b>\1</b>', value)
value = re.sub(r'\[i\](.*?)\[/i\]', r'<i>\1</i>', value) value = re.sub(r'\[i\](.*?)\[/i\]', r'<i>\1</i>', value)
value = re.sub(r'\[u\](.*?)\[/u\]', r'<u>\1</u>', value) value = re.sub(r'\[u\](.*?)\[/u\]', r'<u>\1</u>', value)
value = re.sub(r'\[img\](.*?)\[/img\]', r'<img src="\1">', value) value = re.sub(r'\[img\](.*?)\[/img\]', r'<img src="\1">', value)
for i in range(25): # ugly hack but works good enough for i in range(25): # ugly hack but works good enough
value = re.sub(r'\[quote=(.+?)\](.+)\[/quote\]', '<fieldset><legend>' + '<a href="../../user/' + value = re.sub(r'\[quote=(.+?)\](.+)\[/quote\]', '<fieldset><legend>' + '<a href="../../user/' + url_replacer(r'\1') + '.html">' + r'\1</a></legend>\2</fieldset>', value, count=1)
url_replacer(r'\1') + '.html">' + r'\1</a></legend>\2</fieldset>', value, count=1)
for i in range(25): # same here, shut up for i in range(25): # same here, shut up
value = re.sub(r'\[quote\](.*?)\[/quote\]', value = re.sub(r'\[quote\](.*?)\[/quote\]', r'<fieldset>\1</fieldset>', value)
r'<fieldset>\1</fieldset>', value)
return value return value
def write_render(rendered, name, outpath): def write_render(rendered, name, outpath):
if not os.path.exists(os.path.join(outpath, 'board', 'thread')): if not os.path.exists(os.path.join(outpath, 'board', 'thread')):
os.makedirs(os.path.join(outpath, 'board', 'thread')) os.makedirs(os.path.join(outpath, 'board', 'thread'))
@ -81,7 +54,6 @@ def write_render(rendered, name, outpath):
with open(os.path.join(outpath, name), 'w') as f: with open(os.path.join(outpath, name), 'w') as f:
f.write(rendered) f.write(rendered)
def find_unregistered_users(data): def find_unregistered_users(data):
unregistered_users = [] unregistered_users = []
for board in data['boards']: for board in data['boards']:
@ -99,44 +71,29 @@ def find_unregistered_users(data):
return unregistered_users return unregistered_users
def find_board_id(board):
return board['link'].split('/')[-2]
def render_boards(boards, template_board, template_thread, outpath, title): def render_boards(boards, template_board, template_thread, outpath, title):
for board in boards: for board in boards:
rendered_board = template_board.render( rendered_board = template_board.render(board=board, title=title + ' - ' + board['title'])
board=board, title=title + ' - ' + board['title']) write_render(rendered_board, os.path.join('board', url_replacer(board['title']) + '.html'), outpath)
write_render(rendered_board, os.path.join('board', find_board_id(
board) + '_' + url_replacer(board['title']) + '.html'), outpath)
for thread in board['threads']: for thread in board['threads']:
rendered_thread = template_thread.render( rendered_thread = template_thread.render(thread=thread, title=title + ' - ' + thread['title'])
board=board, thread=thread, title=title + ' - ' + thread['title']) write_render(rendered_thread, os.path.join('board', 'thread', url_replacer(thread['title']) + '.html'), outpath)
write_render(rendered_thread, os.path.join('board', 'thread', thread[
'id'] + '_' + url_replacer(thread['title']) + '.html'), outpath)
render_boards(board['boards'], template_board,
template_thread, outpath, title)
render_boards(board['boards'], template_board, template_thread, outpath, title)
def render(inputfile, staticpath, outpath, title): def render(inputfile, staticpath, outpath, title):
write_render(css, 'styles.css', outpath)
with open(inputfile) as data_file: with open(inputfile) as data_file:
data = json.load(data_file) data = json.load(data_file)
unregistered_users = find_unregistered_users(data) unregistered_users = find_unregistered_users(data)
for unregistered_user in unregistered_users: for unregistered_user in unregistered_users:
data['users'].append( data['users'].append({ 'name': unregistered_user, 'registered': None })
{'name': unregistered_user, 'registered': None})
env = Environment(loader=FileSystemLoader('./templates')) env = Environment(loader=FileSystemLoader('./templates'))
env.filters['fromunixtime'] = fromunixtime env.filters['fromunixtime'] = fromunixtime
env.filters['tohtml'] = tohtml env.filters['tohtml'] = tohtml
env.filters['url_replacer'] = url_replacer env.filters['url_replacer'] = url_replacer
env.filters['find_board_id'] = find_board_id
template_users = env.get_template('users.html.j2') template_users = env.get_template('users.html.j2')
template_user = env.get_template('user.html.j2') template_user = env.get_template('user.html.j2')
@ -144,39 +101,27 @@ def render(inputfile, staticpath, outpath, title):
template_board = env.get_template('board.html.j2') template_board = env.get_template('board.html.j2')
template_thread = env.get_template('thread.html.j2') template_thread = env.get_template('thread.html.j2')
rendered_users = template_users.render( rendered_users = template_users.render(users=data['users'], title=title + ' - Users')
users=data['users'], title=title + ' - Users') rendered_boards = template_boards.render(boards=data['boards'], title=title + ' - Boards')
rendered_boards = template_boards.render(
boards=data['boards'], title=title + ' - Boards')
write_render(rendered_users, 'users.html', outpath) write_render(rendered_users, 'users.html', outpath)
write_render(rendered_boards, 'boards.html', outpath) write_render(rendered_boards, 'boards.html', outpath)
shutil.rmtree(os.path.join(outpath, 'board', shutil.rmtree(os.path.join(outpath, 'board', 'thread', 'static'), ignore_errors=True)
'thread', 'static'), ignore_errors=True) shutil.copytree(staticpath, os.path.join(outpath, 'board', 'thread', 'static'))
shutil.copytree(staticpath, os.path.join(
outpath, 'board', 'thread', 'static'))
for user in data['users']: for user in data['users']:
rendered_user = template_user.render( rendered_user = template_user.render(user=user, title=title + ' - ' + user['name'])
user=user, title=title + ' - ' + user['name']) write_render(rendered_user, os.path.join('user', url_replacer(user['name']) + '.html'), outpath)
write_render(rendered_user, os.path.join(
'user', url_replacer(user['name']) + '.html'), outpath)
render_boards(data['boards'], template_board, render_boards(data['boards'], template_board, template_thread, outpath, title)
template_thread, outpath, title)
if __name__ == "__main__": if __name__ == "__main__":
parser = argparse.ArgumentParser( parser = argparse.ArgumentParser(description='build a static website out of a proboard json dump')
description='build a static website out of a proboard json dump') parser.add_argument('--data', default='board.json', help='board data (json file)')
parser.add_argument('--data', default='board.json', parser.add_argument('--static', default='static', help='path to the static files (images, attachments)')
help='board data (json file)') parser.add_argument('--out', default='rendered', help='path where the website gets rendered to')
parser.add_argument('--static', default='static', parser.add_argument('--title', default='Proboard', help='title for your pages')
help='path to the static files (images, attachments)')
parser.add_argument('--out', default='rendered',
help='path where the website gets rendered to')
parser.add_argument('--title', default='Proboard',
help='title for your pages')
args = parser.parse_args() args = parser.parse_args()

View File

@ -3,25 +3,36 @@
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width"> <meta name="viewport" content="width=device-width">
<link href="/styles.css" rel="stylesheet" type="text/css" /> <style type="text/css">
body {
margin: 40px auto;
max-width: 650px;
line-height: 1.6;
font-size: 18px;
color: #444;
padding: 0 10px
}
h1,
h2,
h3 {
line-height: 1.2
}
</style>
<title>{{ title }}</title> <title>{{ title }}</title>
</head> </head>
<body> <body>
<ul>
<li><a href="../boards.html">Boards</a></li>
</ul>
<hr>
{% if board.boards |length > 0 %} {% if board.boards |length > 0 %}
<ul> <ul>
{% for board in board.boards %} {% for board in board.boards %}
<li><a href={{ "../board/" + board |find_board_id + '_' + board.title |url_replacer + ".html" }}>{{ board.title }}</a></li> <li><a href={{ "../board/" + board.title |url_replacer + ".html" }}>{{ board.title }}</a></li>
{% endfor %} {% endfor %}
</ul> </ul>
<hr> <hr>
{% endif %} {% endif %}
<ul> <ul>
{% for thread in board.threads %} {% for thread in board.threads %}
<li><a href={{ "thread/" + thread.id + '_' + thread.title |url_replacer + ".html" }}>{{ thread.title }}</a></li> <li><a href={{ "thread/" + thread.title |url_replacer + ".html" }}>{{ thread.title }}</a></li>
{% endfor %} {% endfor %}
</ul> </ul>
</body> </body>

View File

@ -3,19 +3,33 @@
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width"> <meta name="viewport" content="width=device-width">
<link href="/styles.css" rel="stylesheet" type="text/css" /> <style type="text/css">
body {
margin: 40px auto;
max-width: 650px;
line-height: 1.6;
font-size: 18px;
color: #444;
padding: 0 10px
}
h1,
h2,
h3 {
line-height: 1.2
}
</style>
<title>{{ title }}</title> <title>{{ title }}</title>
</head> </head>
<body> <body>
<ul> <ul>
<li><a href="index.html">Home</a></li>
<li><a href="users.html">Users</a></li> <li><a href="users.html">Users</a></li>
</ul> </ul>
<hr> <hr>
<ul> <ul>
{% for board in boards %} {% for board in boards %}
<li> <li>
<a href={{ "board/" + board |find_board_id + '_' + board.title |url_replacer + ".html" }}>{{ board.title }}</a><br> <b><a href={{ "board/" + board.title.replace('/', '_').replace(' ', '_').replace('?', '') + ".html" }}>{{ board.title }}</a></b>
<i>{{ board.description }}</i> <i>{{ board.description }}</i>
</li> </li>
{% endfor %} {% endfor %}

View File

@ -3,18 +3,29 @@
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width"> <meta name="viewport" content="width=device-width">
<link href="/styles.css" rel="stylesheet" type="text/css" /> <style type="text/css">
body {
margin: 40px auto;
max-width: 650px;
line-height: 1.6;
font-size: 18px;
color: #444;
padding: 0 10px
}
h1,
h2,
h3 {
line-height: 1.2
}
</style>
<title>{{ title }}</title> <title>{{ title }}</title>
</head> </head>
<body> <body>
<ul>
<li><a href={{ "../" + board |find_board_id + '_' + board.title |url_replacer + ".html" }}>{{ board.title }}</a></li>
</ul>
<hr>
<h1>{{ thread.title }}</h1> <h1>{{ thread.title }}</h1>
{% for post in thread.posts %} {% for post in thread.posts %}
<a href="#{{ loop.index }}">{{ loop.index }} </a><i>by </i> <a href="#{{ loop.index }}">{{ loop.index }} - </a>
<a name="{{ loop.index }}" href={{ "../../user/" + post.user.name |url_replacer + ".html" }}>{{ post.user.name }}</a> <b><a name="{{ loop.index }}" href={{ "../../user/" + post.user.name |url_replacer + ".html" }}>{{ post.user.name }}</a></b>
<i>({{ post.timestamp | fromunixtime }})</i> <i>({{ post.timestamp | fromunixtime }})</i>
<p>{{ post.message | tohtml }}</p> <p>{{ post.message | tohtml }}</p>
{% for attachment in post.attachments %} {% for attachment in post.attachments %}

View File

@ -1,16 +1,29 @@
<!doctype html> <!doctype html>
<html> <html>
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width"> <meta name="viewport" content="width=device-width">
<link href="/styles.css" rel="stylesheet" type="text/css" /> <style type="text/css">
body {
margin: 40px auto;
max-width: 650px;
line-height: 1.6;
font-size: 18px;
color: #444;
padding: 0 10px
}
h1,
h2,
h3 {
line-height: 1.2
}
</style>
<title>{{ title }}</title> <title>{{ title }}</title>
</head> </head>
<body> <body>
<ul>
<li><a href="../users.html">Users</a></li>
</ul>
<hr>
<h1>{{ user.name }}</h1> <h1>{{ user.name }}</h1>
<h3>{{ user.status }}</h3> <h3>{{ user.status }}</h3>
<p> <p>
@ -20,4 +33,5 @@
<hr> <hr>
<i>{{ user.signature | tohtml }}</i> {% endif %} <i>{{ user.signature | tohtml }}</i> {% endif %}
</body> </body>
</html> </html>

View File

@ -3,12 +3,26 @@
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width"> <meta name="viewport" content="width=device-width">
<link href="/styles.css" rel="stylesheet" type="text/css" /> <style type="text/css">
body {
margin: 40px auto;
max-width: 650px;
line-height: 1.6;
font-size: 18px;
color: #444;
padding: 0 10px
}
h1,
h2,
h3 {
line-height: 1.2
}
</style>
<title>{{ title }}</title> <title>{{ title }}</title>
</head> </head>
<body> <body>
<ul> <ul>
<li><a href="index.html">Home</a></li>
<li><a href="boards.html">Boards</a></li> <li><a href="boards.html">Boards</a></li>
</ul> </ul>
<hr> <hr>