# WikiNote3 # Copyright © 2020 Lee Yingtong Li (RunasSudo) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Affero General Public License for more details. # # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . from .markup import WNMarkdown import flask import os import pickle import xml.etree.ElementTree as ET app = flask.Flask(__name__, template_folder='jinja2') if os.path.exists('index.pickle'): try: with open('index.pickle', 'rb') as f: index = pickle.load(f) except: print('Error loading index.pickle') import traceback; traceback.print_exc() def get_children(path): def child_name(c): return os.path.splitext(c)[0] children = [] if os.path.isdir(flask.safe_join('./data/pages', path)): for child in os.listdir(flask.safe_join('./data/pages', path)): child_path = flask.safe_join('./data/pages', path, child) if child.startswith('_'): continue if os.path.islink(child_path): children.append((child_name(child), child_name(os.path.split(os.path.realpath(child_path))[1]))) else: children.append((child_name(child), None)) children.sort(key=lambda x: x[0][1:] if x[0].startswith('(') else x[0]) return children @app.route('/') def index_page(): children = get_children('') if not os.path.exists('./data/pages/Home.md'): return flask.render_template('page_404.html', page={ 'path': '', 'title': 'Home', 'children': children }) with(open(fname, 'r')) as f: page_source = f.read() md = WNMarkdown() page_content = md.convert(page_source) return flask.render_template('page_rendered.html', page={ 'path': '', 'title': 'Home', 'content': page_content, 'toc': md.toc_tokens, 'meta': md.meta, 'children': children }) @app.route('/page/') def page_view(path): fname = flask.safe_join('./data/pages', path) + '.md' if os.path.islink(fname): redir_page = '/'.join(path.split('/')[:-1]) + '/' + os.path.splitext(os.readlink(fname))[0] return flask.redirect(flask.url_for('page_view', path=redir_page)) children = get_children(path) if not os.path.exists(fname): return flask.render_template('page_404.html', page={ 'path': path, 'title': path.split('/')[-1], 'children': children }) with(open(fname, 'r')) as f: page_source = f.read() md = WNMarkdown() #page_content = md.convert(page_source) root = md.parse(page_source) # Add expand/collapse buttons to h2 for elem in root: if elem.tag == 'h2': elem.text += ' ' btn = ET.SubElement(elem, 'button') btn.text = '±' btn.set('class', 'expcol') btn.set('onclick', 'onClickExpCol(this);') # Handle collapsed sections if root == '': page_content = '' else: if 'collapsed' in flask.request.args: for elem in root: if elem.tag == 'section': elem.set('class', 'collapsed') page_content = md.serialise(root) return flask.render_template('page_rendered.html', page={ 'path': path, 'title': md.meta['title'] if 'title' in md.meta else path.split('/')[-1], 'content': page_content, 'toc': md.toc_tokens, 'meta': md.meta, 'children': children, 'xrefs': index['xrefs'].get(path, []) }, collapsed='collapsed' in flask.request.args) @app.route('/preview/') def page_preview(path): fname = flask.safe_join('./data/pages', path) + '.md' if not os.path.exists(fname): return '' with(open(fname, 'r')) as f: page_source = f.read() md = WNMarkdown() page_root = md.parse(page_source) # Delete all but first section for section in list(page_root[1:]): page_root.remove(section) # Delete all but introductory text for elem in list(page_root[0][1:]): page_root[0].remove(elem) # Delete footnotes def walk_tree(elem): last_child = None for child in list(elem): if child.get('class') == 'footnote-ref': if child.tail: # Combine tail text if last_child is not None: last_child.tail += child.tail else: elem.text += child.tail elem.remove(child) walk_tree(child) else: walk_tree(child) last_child = child walk_tree(page_root[0]) page_content = md.serialise(page_root) return page_content @app.route('/image/') def image_view(name): fname = flask.safe_join(os.getcwd(), './data/images', name[0].upper(), name) return flask.send_file(fname) @app.route('/image//about') def image_about(name): fname = flask.safe_join(os.getcwd(), './data/images', name[0].upper(), os.path.splitext(name)[0] + '.md') with(open(fname, 'r')) as f: page_source = f.read() md = WNMarkdown() page_content = md.convert(page_source) return flask.render_template('image_about.html', page={ 'path': name, 'title': name, 'content': page_content, 'meta': md.meta }) @app.route('/tag/') def tags_list(): return flask.render_template('tags_list.html', tags=sorted(index['tags'].keys())) @app.route('/tag/') def tag_view(name): if name not in index['tags']: return flask.render_template('tag_404.html', name=name) pages = index['tags'][name] pages.sort(key=lambda p: p['kind']) pages.sort(key=lambda p: p['path']) return flask.render_template('tag_view.html', name=name, pages=pages) @app.cli.command('index') def cli_index(): app.config['SERVER_NAME'] = 'localhost' with app.app_context(): tags = {} xrefs = {} base_path = './data/pages' for dirpath, dirnames, filenames in os.walk(base_path): for fname in filenames: if fname.endswith('.md') and not os.path.islink(flask.safe_join(dirpath, fname)): page_path = dirpath[len(base_path)+1:] + '/' + fname[:-3] with(open(flask.safe_join(dirpath, fname), 'r')) as f: page_source = f.read() md = WNMarkdown() md.convert(page_source) for tag in md.meta.get('tags', []): if tag not in tags: tags[tag] = [] tags[tag].append({'kind': 'page', 'path': page_path}) for ref in md.meta.get('refs', []): fname_ref = flask.safe_join('./data/pages', ref) + '.md' if os.path.islink(fname_ref): ref = '/'.join(ref.split('/')[:-1]) + '/' + os.path.splitext(os.readlink(fname_ref))[0] if ref not in xrefs: xrefs[ref] = [] if page_path not in xrefs[ref]: xrefs[ref].append(page_path) base_path = './data/images' for dirpath, dirnames, filenames in os.walk(base_path): for fname in filenames: if fname.endswith('.md'): continue md_path = flask.safe_join(dirpath, os.path.splitext(fname)[0] + '.md') if os.path.exists(md_path): with(open(md_path, 'r')) as f: page_source = f.read() md = WNMarkdown() md.convert(page_source) for tag in md.meta.get('tags', []): if tag not in tags: tags[tag] = [] tags[tag].append({'kind': 'image', 'path': fname}) with open('index.pickle', 'wb') as f: pickle.dump({ 'tags': tags, 'xrefs': xrefs }, f) @app.cli.command('redlinks') def cli_redlinks(): app.config['SERVER_NAME'] = 'localhost' redlinks = set() with app.app_context(): base_path = './data/pages' for dirpath, dirnames, filenames in os.walk(base_path): for fname in filenames: if fname.endswith('.md'): page_path = dirpath[len(base_path)+1:] + '/' + fname[:-3] with(open(flask.safe_join(dirpath, fname), 'r')) as f: page_source = f.read() md = WNMarkdown() md.convert(page_source) for redlink in md.meta.get('redlinks', []): redlinks.add(redlink) redlinks = sorted(list(redlinks)) for redlink in redlinks: print(redlink)