aboutsummaryrefslogtreecommitdiffstats
path: root/server.py
blob: 8614fe3b78e8b299ef87a72b9fe7f679d68984a0 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
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) as f:
                content = f.read()
            print(path.split('/')[-2] + '/')
            return render_template_string(CONTENT_BLOCK.replace('$', content), **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
                                   )
                               )


def setup():
    """
    setup - sets up the app according to the settings specified (or not
    speified) in `siteconfig`
    """
    if siteconfig.SECRET_KEY:
        app.config['SECRET_KEY'] = siteconfig.SECRET_KEY
    else:
        SECRET_KEY = os.urandom(32)
        app.config['SECRET_KEY'] = SECRET_KEY

    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]})

    app.config.update({'DOMAIN': siteconfig.DOMAIN})
    app.config.update({'HOME_TITLE': siteconfig.HOME_TITLE})


if __name__ == '__main__':
    setup()
    app.run(host=siteconfig.HOST, port=siteconfig.PORT)