I'm using Flask to expose a local directory of HTML files on a web page.
I am also using a jinja2
to generate the sitemap in the lefthand div
of my main endpoint.
I am unable to correctly specify the URL to the endpoint of my subfolders.
As mentioned in the code below, how would I dynamically build a relative link from /docs
(i.e. /docs/folder1/subfolder1/SubFolder1Page.html
)?
The way I am currently setting the value for href
obviously does not work.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Docs Demo</title>
<link rel="stylesheet" href="{{ url_for('static', filename='styles.css') }}">
</head>
<body>
<div id="container">
<div class="left_frame">
<h1>{{ tree.name }}</h1>
<ul>
{%- for item in tree.children recursive %}
<!-- How would I build a relative link from /docs/ i.e. /docs/folder1/subfolder1/SubFolder1Page.html -->
<li><a href="docs/{{ item.name }}" target="iframe1">{{ item.name }}
{%- if item.children -%}
<ul>{{ loop(item.children) }}</ul>
{%- endif %}</a></li>
{%- endfor %}
</ul>
</div>
<div class="right_frame">
<iframe name="iframe1"></iframe>
</div>
</div>
</body>
</html>
Folder structure example:
How it looks overall displaying the contents of file1.html
:
So I figured out a satisfying way of solving my own issue.
I managed to get this very functional result:
Do note that my template is only good for files with .html
extension, though it can be easily enhanced to support other file extensions.
Here is my finalized templates\template.html
file:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Docs Demo</title>
<link rel="stylesheet" href="{{ url_for('static', filename='styles.css') }}">
</head>
<body>
<div id="container">
<div class="left_frame">
<h1>{{ tree.name }}</h1>
<ul>
{%- for item in tree.children recursive %}
{% if '.html' in item.name %}
<li><a href="docs/{{ item.name }}" target="iframe1">
{{ item.name.split('/')[-1:][0] }}
{%- if item.children -%}
<ul>{{ loop(item.children) }}</ul>
{%- endif %}</a></li>
{% else %}
<li>{{ item.name }}
{%- if item.children -%}
<ul>{{ loop(item.children) }}</ul>
{%- endif %}</li>
{% endif %}
{%- endfor %}
</ul>
</div>
<div class="right_frame">
<iframe name="iframe1"></iframe>
</div>
</div>
</body>
</html>
You can refer to King Reload's answer for an analysis of what I've changed in the template.html
file to make this work correctly.
And here is the demo_app.py
script that serves my document HTML files via Flask:
import threading
import os
import webbrowser
from flask import Flask, render_template, send_from_directory
app = Flask(__name__, static_folder='static')
ROOT = os.path.dirname(os.path.abspath(__file__))
DOCS_ROOT = os.path.join(app.static_folder, 'docs')
@app.route('/')
def docs_tree():
return render_template('template.html', tree=make_tree(DOCS_ROOT))
@app.route('/docs/<path:filename>')
def send_docs(filename):
return send_from_directory(directory=DOCS_ROOT, 'docs'), filename=filename)
def make_tree(path):
tree = dict(name=os.path.basename(path), children=[])
try:
lst = os.listdir(path)
except OSError:
pass # ignore errors
else:
for name in lst:
fn = os.path.join(path, name)
if os.path.isdir(fn):
tree['children'].append(make_tree(fn))
else:
np = os.path.join(path.replace(DOCS_ROOT, ''), name).replace('\\', '/')
if np.startswith('/'):
np = np[1:]
tree['children'].append(dict(name=np))
return tree
if __name__ == '__main__':
host = 'localhost'
port = '8888'
url = 'http://{h}:{p}'.format(h=host, p=port)
threading.Timer(3, lambda: webbrowser.open(url)).start()
app.run(host=host, port=port, debug=False)
Most notable changes in demo_app.py
since asking my original question were the following:
app
, I set DOCS_ROOT
using app.static_folder
;send_docs()
, I changed the send_from_directory()
's directory
argument to use DOCS_ROOT
;Inside of make_tree()
, inside the else
block of the for
loop, I added:
np = os.path.join(path.replace(DOCS_ROOT, ''), name).replace('\\', '/')
if np.startswith('/'):
np = np[1:]
All this does is take the absolute path of name
, remove what matches DOCS_ROOT
, leaving only the relative path (and then replacing the \\
for /
), resulting in a simple relative path from static/docs
. If the relative path starts with a /
, I remove it (since there is a trailing /
from docs
in template.html
.
For anyone interested in the simplistic stylesheet (static\styles.css
) I used (along with some updated enhancements):
html {
min-height:100%;
position:relative;
}
body {
overflow:hidden;
}
.container {
width:100%;
overflow:auto;
}
.left_frame {
float:left;
background:#E8F1F5;
width:25%;
height:100vh;
}
.right_frame {
float:right;
background:#FAFAFA;
width:75%;
height:100vh;
}
.right_frame iframe {
display:block;
width:100%;
height:100%;
border:none;
}
To add onto the solution of @HEADLESS_0NE:
He added a few more if
statements in the for loop
, like so:
{%- for item in tree.children recursive %}
-> {% if '.html' in item.name %}
<li><a href="docs/{{ item.name }}" target="iframe1">
-> {{ item.name.split('/')[-1:][0] }}
{%- if item.children -%}
<ul>{{ loop(item.children) }}</ul>
{%- endif %}</a></li>
-> {% else %}
-> <li>{{ item.name }}
-> {%- if item.children -%}
-> <ul>{{ loop(item.children) }}</ul>
-> {%- endif %}</li>
-> {% endif %}
{%- endfor %}
Everything with an ->
has been changed in the html
, I couldn't find what he added in his python
or css
, but in short:
if
to check if there's a .html
in the item.name
.item.name
, so the /
gets removed.else
statement if there's no .html
in the item.name
.This basically adds the ul
's and the li
's in the correct format.
For a more detailed explanation I hope HEADLESS_0NE could provide us of more information what he might've changed in the python
script.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With