#!/usr/bin/env python # -*- coding: utf-8 -*- # 2015 by Sebastian Hugentobler # 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 # worldwide. This software is distributed without any warranty. # See http://creativecommons.org/publicdomain/zero/1.0/ for a description of CC0. import argparse, json, os, re, shutil from datetime import datetime from jinja2 import Environment, FileSystemLoader import sys #reload(sys); sys.setdefaultencoding('utf-8') def fromunixtime(value): return datetime.fromtimestamp(value).strftime('%Y-%m-%d %H:%M:%S') def url_replacer(value): return re.sub('[^a-zA-Z0-9\n\.]', '_', value) def user_replacer(match): return '[url=../../user/' + url_replacer(match.group(2)) + '.html]' + match.group(2) + '[/url]' def tohtml(value): value = value.replace('{{baseurl}}', 'static') value = value.replace('\n', '
') value = re.sub(r'\[url=\/user\/(.*?)\](.*?)\[\/url\]', user_replacer, value) value = re.sub(r'\[url=(.*?)\](.*?)\[/url\]', r'\2', value) value = re.sub(r'\[video\](.*?)\[/video\]', r'\1', value) value = re.sub(r'\[colour=(.*?)\](.*?)\[/colour\]', r'\2', value) value = re.sub(r'\[b\](.*?)\[/b\]', r'\1', value) value = re.sub(r'\[i\](.*?)\[/i\]', r'\1', value) value = re.sub(r'\[u\](.*?)\[/u\]', r'\1', value) value = re.sub(r'\[img\](.*?)\[/img\]', r'', value) for i in range(25): # ugly hack but works good enough value = re.sub(r'\[quote=(.+?)\](.+)\[/quote\]', '
' + '' + r'\1\2
', value, count=1) for i in range(25): # same here, shut up value = re.sub(r'\[quote\](.*?)\[/quote\]', r'
\1
', value) return value def write_render(rendered, name, outpath): if not os.path.exists(os.path.join(outpath, 'board', 'thread')): os.makedirs(os.path.join(outpath, 'board', 'thread')) if not os.path.exists(os.path.join(outpath, 'user')): os.makedirs(os.path.join(outpath, 'user')) with open(os.path.join(outpath, name), 'w') as f: f.write(rendered) def find_unregistered_users(data): unregistered_users = [] for board in data['boards']: for thread in board['threads']: for post in thread['posts']: if post['user']['name'] not in unregistered_users: found = False for user in data['users']: if post['user']['name'] == user['name']: found = True break if not found: unregistered_users.append(post['user']['name']) return unregistered_users def render_boards(boards, template_board, template_thread, outpath, title): for board in boards: rendered_board = template_board.render(board=board, title=title + ' - ' + board['title']) write_render(rendered_board, os.path.join('board', url_replacer(board['title']) + '.html'), outpath) for thread in board['threads']: rendered_thread = template_thread.render(board=board, thread=thread, title=title + ' - ' + thread['title']) write_render(rendered_thread, os.path.join('board', 'thread', url_replacer(thread['title']) + '.html'), outpath) render_boards(board['boards'], template_board, template_thread, outpath, title) def render(inputfile, staticpath, outpath, title): with open(inputfile) as data_file: data = json.load(data_file) unregistered_users = find_unregistered_users(data) for unregistered_user in unregistered_users: data['users'].append({ 'name': unregistered_user, 'registered': None }) env = Environment(loader=FileSystemLoader('./templates')) env.filters['fromunixtime'] = fromunixtime env.filters['tohtml'] = tohtml env.filters['url_replacer'] = url_replacer template_users = env.get_template('users.html.j2') template_user = env.get_template('user.html.j2') template_boards = env.get_template('boards.html.j2') template_board = env.get_template('board.html.j2') template_thread = env.get_template('thread.html.j2') rendered_users = template_users.render(users=data['users'], title=title + ' - Users') rendered_boards = template_boards.render(boards=data['boards'], title=title + ' - Boards') write_render(rendered_users, 'users.html', outpath) write_render(rendered_boards, 'boards.html', outpath) shutil.rmtree(os.path.join(outpath, 'board', 'thread', 'static'), ignore_errors=True) shutil.copytree(staticpath, os.path.join(outpath, 'board', 'thread', 'static')) for user in data['users']: rendered_user = template_user.render(user=user, title=title + ' - ' + user['name']) write_render(rendered_user, os.path.join('user', url_replacer(user['name']) + '.html'), outpath) render_boards(data['boards'], template_board, template_thread, outpath, title) if __name__ == "__main__": parser = argparse.ArgumentParser(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('--static', default='static', 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() render(args.data, args.static, args.out, args.title)