import os from flask import request, send_from_directory, abort from flask import render_template, render_template_string from siteconfig import siteconfig from server import app # bit of a hack. # Brackets don't play nicely with Jinja so instead of using .format, # we just replace the special character $ CONTENT_BLOCK = "{% extends 'base.html' %}{% block content %}${% endblock %}" def default_context(): """ default_context - returns the minimum info needed to render a template--the domain name (for the home directory), and the top site directories which make up the navbar """ return { 'domain': app.config['DOMAIN'], 'navbar': sorted(app.config['MAIN_SITE_DIRS']), } def index_dir(path): """ index_dir - Given a directory at `path`, list it's contents, and sort each item as a file or a directory or a special file. return - a tuple with the values: > a list of directories in `path`, > a list of files in `path`, > (if present), a list of external links to add to the index.html of the directory > (if present), a short description (string) of what the directory contains Lists are sorted alphabetically. *Special files include '.links' and '.description' the format of these files is unquoted CSV and text/html. Both are displayed on the index.html for the given `path` """ dirs = [] files = [] links = [] description = False contents = os.listdir(path) for obj in contents: if os.path.isfile(path + '/' + obj): if obj == siteconfig.LINKS_FILE: with open(path + '/' + obj) as f: links = f.readlines() elif obj == siteconfig.DESC_FILE: description = True elif obj.startswith('.'): continue else: files.append(obj) elif os.path.isdir(path + '/' + obj): if obj.startswith('.'): continue else: dirs.append(obj) return sorted(dirs), sorted(files), sorted(links), description def is_hidden_path(path): """ Tests if last object specified in `path` is hidden. Inspired by Unix. On Windows, directories won't actually be "hidden" but they are still not indexed by this program """ return path.split('/')[-1].startswith('.') @app.route("/") @app.route("/site") def home(): """ home - renders the template `home.html` as the main index file If you'd like to customize your home page, that is the file you want to edit, though you can optionally change the title here if you wish """ context = default_context() context.update({'title': app.config['HOME_TITLE'], 'parent_dir': '/site/'}) return render_template("site/home.html", **context) # from: https://pythonise.com/series/learning-flask/sending-files-with-flask @app.route("/site/") def render_file(path): """ render_file - renders an HTML document for the given `path`. If `path` is an HTML file it is rendered within the base template, otherwise, the raw file is returned. If `path` points to a directory, this function instead creates an index for the directory containing it's files, links, and other info. """ if is_hidden_path(path): abort(404) abs_path = "./templates/site/" + path context = default_context() context.update( { 'title': path.split('.')[0].upper(), 'parent_dir': '/site/' + '/'.join(path.split('/')[:-1]), } ) if os.path.isfile(abs_path): if abs_path.endswith('.html'): with open(abs_path, 'rb') as f: content = f.read().decode("UTF-8") return render_template_string( CONTENT_BLOCK.replace('$', content), **context ) elif abs_path.endswith('.html!'): return render_template("site/" + path, **context) else: # not an html file, so don't render it return send_from_directory( 'templates/site/', path, mimetype=siteconfig.MIMETYPES.get( f".{ path.split('.')[-1] }", siteconfig.DEFAULT_MIMETYPE ), ) elif os.path.isdir(abs_path): dirs, files, links, description = index_dir(abs_path) context.update( { 'cur_path': 'site/' + path, 'cur_dir': path.split('/')[-1] + '/', 'dirs': dirs, 'files': files, 'links': links, 'description': description, } ) return render_template("index.html", **context) else: context.update({'errors': "404 File not found"}) return render_template("base.html", **context) @app.route("/raw/") def send_file_from_site(path): """ send_file - instead of rendering a file within a template as with `render_file`, send the raw file to the user, when site is replace with path. """ return send_from_directory( 'templates/site/', path, mimetype=siteconfig.MIMETYPES.get( f".{ path.split('.')[-1] }", siteconfig.DEFAULT_MIMETYPE ), ) @app.route("/static/") def send_file_from_static(path): """ send_file - instead of rendering a file within a template as with `render_file`, send the raw file to the user """ return send_from_directory( 'static/', path, mimetype=siteconfig.MIMETYPES.get( f".{ path.split('.')[-1] }", siteconfig.DEFAULT_MIMETYPE ), )