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 +        ) +    ) | 
