diff options
-rw-r--r-- | README | 16 | ||||
-rw-r--r-- | server.py | 184 | ||||
-rw-r--r-- | templates/base.html | 4 | ||||
-rw-r--r-- | templates/site/tutorials/py-style.html | 0 | ||||
-rw-r--r-- | views.py | 180 |
5 files changed, 204 insertions, 180 deletions
@@ -24,11 +24,11 @@ elsewhere. Why not just use a database or the million other CMS software packages out there? Zero reason not to! I just wanted to see what it would look like to build something from the ground up WITHOUT having to copy HTML over and over -again. Databases are great, but so are filesystems, and I see no reason to +again. Databases are great, but so are file systems, and I see no reason to overcomplicate when making a simple home page. This software is primarily geared towards bloggers or people who want a home page like it's 1999, but Flask has great documentation so I think you'll find it a pleasure to build on -top of. +top of if you want to get creative. ### Why not just neocities? @@ -58,8 +58,10 @@ $ python server.py ``` Your server will (by default) be hosted on http://127.0.0.1:5000 -and have the `templates/site/` directory mounted. You should see `home.html` -render. +and have the `templates/site/` directory delivered to your users when they +access http://127.0.0.1:5000/site/ + +You should see `home.html` render on the root directory. ## Adding Pages @@ -70,7 +72,7 @@ particular is special since it contains the top-level folders which will be used to navigate your site, but any folders beneath will be automatically indexed. -As an excercise, add a file to the `templates/site/thoughts/rants` folder +As an exercise, add a file to the `templates/site/thoughts/rants` folder called `myrant.html` and put the following content: `<p>I don't like spam!</p>` @@ -185,7 +187,7 @@ replace the HTML with your own license (or none), by editing `templates/site/license.html` -### Other tips +### Other Tips There are a few special directories linked that are needed to customize your site. First the `static` directory, which holds your static @@ -204,6 +206,8 @@ documented. A quickstart looks like this: ```bash $ sudo pip install uwsgi +$ sudo mkdir /var/path/to/your-flask/ +$ sudo chown www-data -R /var/path/to/your-flask $ uwsgi -s /var/path/to/your-flask.sock --manage-script-name --mount /=server:app --virtualenv ./env ``` @@ -1,183 +1,15 @@ import os from flask import Flask -from flask import request, send_from_directory, abort -from flask import render_template, render_template_string from siteconfig import siteconfig app = Flask(__name__) -# bit of a hack. -# Brackets don't play nicely with Jinja so instead of using .format, -# we just replace the special charcater $ -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. - - 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 - """ - dirs = [] - files = [] - links = [] - description = "" - 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: - with open(path + '/' + obj) as f: - description = f.read() - 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, diretories won't actually be "hidden" but - they are still not indexed by this program - """ - return path.split('/')[-1].startswith('.') - - -@app.route("/") -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': '/' - } - ) - return render_template("site/home.html", **context) - - -# from: https://pythonise.com/series/learning-flask/sending-files-with-flask -@app.route("/<path:path>") -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': '/' + '/'.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_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/<path:path>") -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 - """ - return send_from_directory('template/site/', path, - mimetype=siteconfig.MIMETYPES.get( - f".{ path.split('.')[-1] }", siteconfig.DEFAULT_MIMETYPE - ) - ) - - -@app.route("/static/<path:path>") -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 - ) - ) - +from views import * def setup(): """ setup - sets up the app according to the settings specified (or not - speified) in `siteconfig` + specified) in `siteconfig` """ if siteconfig.SECRET_KEY: app.config['SECRET_KEY'] = siteconfig.SECRET_KEY @@ -188,12 +20,20 @@ def setup(): if siteconfig.MAIN_SITE_DIRS: app.config.update({'MAIN_SITE_DIRS': siteconfig.MAIN_SITE_DIRS}) else: - app.config.update({'MAIN_SITE_DIRS': index_dir('./templates/site')[0]}) + s = './templates/site/' + top_dirs = [ + x for x in os.listdir(s) if os.path.isdir(s + x) + ] + app.config.update( + { + 'MAIN_SITE_DIRS': sorted(top_dirs) + } + ) app.config.update({'DOMAIN': siteconfig.DOMAIN}) app.config.update({'HOME_TITLE': siteconfig.HOME_TITLE}) -# Need to come first to be compatible with wsgi +# Setup needs to come first to be compatible with wsgi setup() if __name__ == '__main__': app.run() diff --git a/templates/base.html b/templates/base.html index 5c4e189..6476091 100644 --- a/templates/base.html +++ b/templates/base.html @@ -13,7 +13,7 @@ <div class="navbar"> <b>|</b> <!-- This makes the bar symmetrical --> {% for dir in navbar %} - <b> <a href="/{{ dir }}">{{ dir }}/</a> |</b> + <b> <a href="/site/{{ dir }}">{{ dir }}/</a> |</b> {% endfor %} </div> <h2>{{ title }}</h2> @@ -21,7 +21,7 @@ {% block content %} {% endblock %} <h3>{{ errors }}</h3> - {% if parent_dir != '/' %} + {% if parent_dir != '/site/' %} <h3><a href="{{ parent_dir }}">Go up to parent folder ({{ parent_dir }})</a></h3> {% endif %} </div> diff --git a/templates/site/tutorials/py-style.html b/templates/site/tutorials/py-style.html deleted file mode 100644 index e69de29..0000000 --- a/templates/site/tutorials/py-style.html +++ /dev/null diff --git a/views.py b/views.py new file mode 100644 index 0000000..c4e3fe2 --- /dev/null +++ b/views.py @@ -0,0 +1,180 @@ +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 free text. Both are displayed on the index.html + for the given `path` + """ + dirs = [] + files = [] + links = [] + description = "" + 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: + with open(path + '/' + obj) as f: + description = f.read() + 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/<path:path>") +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_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/<path:path>") +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/<path:path>") +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 + ) + ) |