mirror of
https://github.com/tiyn/beaker-blog.git
synced 2025-04-19 15:27:47 +02:00
Compare commits
No commits in common. "00686923b40380f22bb85905f8604265cf299f10" and "a936fd5ee649844b54305d10ab4faca725859bd5" have entirely different histories.
00686923b4
...
a936fd5ee6
@ -13,9 +13,6 @@ via plain text files.
|
|||||||
- [x] HTML files (.html)
|
- [x] HTML files (.html)
|
||||||
- [x] Markdown Files (.md)
|
- [x] Markdown Files (.md)
|
||||||
- [x] Infinite-scroll blog page
|
- [x] Infinite-scroll blog page
|
||||||
- [x] Search page
|
|
||||||
- [x] Full-text search
|
|
||||||
- [x] Preview panel
|
|
||||||
- [x] Archive page
|
- [x] Archive page
|
||||||
- [x] Months as headings
|
- [x] Months as headings
|
||||||
- [x] Links to scrolling blog page
|
- [x] Links to scrolling blog page
|
||||||
|
81
src/app.py
81
src/app.py
@ -1,9 +1,10 @@
|
|||||||
from flask import Flask, abort, make_response, render_template, request
|
from flask import Flask, make_response, render_template, abort
|
||||||
from flask_font_awesome import FontAwesome
|
|
||||||
|
|
||||||
import config
|
|
||||||
import content as con_gen
|
import content as con_gen
|
||||||
from forms import SearchForm, register_csrf
|
import config
|
||||||
|
|
||||||
|
|
||||||
|
app = Flask(__name__)
|
||||||
|
|
||||||
TITLE = config.TITLE
|
TITLE = config.TITLE
|
||||||
STITLE = config.STITLE
|
STITLE = config.STITLE
|
||||||
@ -13,89 +14,34 @@ DESCRIPTION = config.DESCRIPTION
|
|||||||
WEBSITE = config.WEBSITE
|
WEBSITE = config.WEBSITE
|
||||||
MAIL = config.MAIL
|
MAIL = config.MAIL
|
||||||
|
|
||||||
app = Flask(__name__)
|
|
||||||
register_csrf(app)
|
|
||||||
font_awesome = FontAwesome(app)
|
|
||||||
|
|
||||||
|
|
||||||
@app.errorhandler(404)
|
@app.errorhandler(404)
|
||||||
def page_not_found(e):
|
def page_not_found(e):
|
||||||
return render_template("error.html",
|
return render_template("error.html", title=TITLE, stitle=STITLE, errorcode="404", style=STYLE, language=LANGUAGE), 404
|
||||||
title=TITLE,
|
|
||||||
stitle=STITLE,
|
|
||||||
errorcode="404",
|
|
||||||
style=STYLE,
|
|
||||||
language=LANGUAGE), 404
|
|
||||||
|
|
||||||
|
|
||||||
@app.route("/")
|
@app.route("/")
|
||||||
@app.route("/index.html")
|
@app.route("/index.html")
|
||||||
def index():
|
def index():
|
||||||
content = con_gen.gen_index_string()
|
content = con_gen.gen_index_string()
|
||||||
return render_template("index.html",
|
return render_template("index.html", title=TITLE, stitle=STITLE, content_string=content, style=STYLE, language=LANGUAGE)
|
||||||
title=TITLE,
|
|
||||||
stitle=STITLE,
|
|
||||||
content_string=content,
|
|
||||||
style=STYLE,
|
|
||||||
language=LANGUAGE)
|
|
||||||
|
|
||||||
|
|
||||||
@app.route("/search", methods=["GET", "POST"])
|
|
||||||
@app.route("/search.html", methods=["GET", "POST"])
|
|
||||||
def search():
|
|
||||||
form = SearchForm()
|
|
||||||
if request.method == "POST":
|
|
||||||
query_str = request.form["query_str"]
|
|
||||||
content = con_gen.gen_query_res_string(query_str)
|
|
||||||
return render_template("search.html",
|
|
||||||
title=TITLE,
|
|
||||||
stitle=STITLE,
|
|
||||||
style=STYLE,
|
|
||||||
form=form,
|
|
||||||
content=content,
|
|
||||||
language=LANGUAGE), 200
|
|
||||||
return render_template("search.html",
|
|
||||||
title=TITLE,
|
|
||||||
stitle=STITLE,
|
|
||||||
style=STYLE,
|
|
||||||
form=form,
|
|
||||||
content="",
|
|
||||||
language=LANGUAGE), 200
|
|
||||||
|
|
||||||
|
|
||||||
@app.route("/imprint")
|
@app.route("/imprint")
|
||||||
@app.route("/imprint.html")
|
@app.route("/imprint.html")
|
||||||
def imprint():
|
def imprint():
|
||||||
return render_template("imprint.html",
|
return render_template("imprint.html", title=TITLE, stitle=STITLE, mail=MAIL, style=STYLE, language=LANGUAGE)
|
||||||
title=TITLE,
|
|
||||||
stitle=STITLE,
|
|
||||||
mail=MAIL,
|
|
||||||
style=STYLE,
|
|
||||||
language=LANGUAGE)
|
|
||||||
|
|
||||||
|
|
||||||
@app.route("/archive")
|
@app.route("/archive")
|
||||||
@app.route("/archive.html")
|
@app.route("/archive.html")
|
||||||
def archive():
|
def archive():
|
||||||
content = con_gen.gen_arch_string()
|
content = con_gen.gen_arch_string()
|
||||||
return render_template("archive.html",
|
return render_template("archive.html", title=TITLE, stitle=STITLE, content_string=content, style=STYLE, language=LANGUAGE)
|
||||||
title=TITLE,
|
|
||||||
stitle=STITLE,
|
|
||||||
content_string=content,
|
|
||||||
style=STYLE,
|
|
||||||
language=LANGUAGE)
|
|
||||||
|
|
||||||
|
|
||||||
@app.route("/entry/<path>")
|
@app.route("/entry/<path>")
|
||||||
def entry(path):
|
def entry(path):
|
||||||
content = con_gen.gen_stand_string(path)
|
content = con_gen.gen_stand_string(path)
|
||||||
if content != "":
|
if content != "":
|
||||||
return render_template("standalone.html",
|
return render_template("standalone.html", title=TITLE, stitle=STITLE, content_string=content, style=STYLE, language=LANGUAGE)
|
||||||
title=TITLE,
|
|
||||||
stitle=STITLE,
|
|
||||||
content_string=content,
|
|
||||||
style=STYLE,
|
|
||||||
language=LANGUAGE)
|
|
||||||
abort(404)
|
abort(404)
|
||||||
|
|
||||||
|
|
||||||
@ -103,11 +49,8 @@ def entry(path):
|
|||||||
@app.route("/rss.xml")
|
@app.route("/rss.xml")
|
||||||
def feed():
|
def feed():
|
||||||
content = con_gen.get_rss_string()
|
content = con_gen.get_rss_string()
|
||||||
rss_xml = render_template("rss.xml",
|
rss_xml = render_template("rss.xml", content_string=content, title=TITLE,
|
||||||
content_string=content,
|
description=DESCRIPTION, website=WEBSITE)
|
||||||
title=TITLE,
|
|
||||||
description=DESCRIPTION,
|
|
||||||
website=WEBSITE)
|
|
||||||
response = make_response(rss_xml)
|
response = make_response(rss_xml)
|
||||||
response.headers["Content-Type"] = "application/rss+xml"
|
response.headers["Content-Type"] = "application/rss+xml"
|
||||||
return response
|
return response
|
||||||
|
@ -18,6 +18,3 @@ LANGUAGE = "en-us"
|
|||||||
|
|
||||||
# Mail address for the imprint
|
# Mail address for the imprint
|
||||||
MAIL = "dummy@mail.com"
|
MAIL = "dummy@mail.com"
|
||||||
|
|
||||||
# Directory to store entries in
|
|
||||||
ENTRY_DIR = "templates/entry"
|
|
||||||
|
@ -4,17 +4,17 @@ import pathlib
|
|||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from os import path
|
from os import path
|
||||||
|
|
||||||
|
import config
|
||||||
import markdown
|
import markdown
|
||||||
|
|
||||||
import config
|
ENTRY_DIR = "templates/entry"
|
||||||
import search
|
|
||||||
|
|
||||||
ENTRY_DIR = config.ENTRY_DIR
|
|
||||||
LANGUAGE = config.LANGUAGE
|
LANGUAGE = config.LANGUAGE
|
||||||
LOCAL = "de_DE.UTF-8" if LANGUAGE == "de-de" else "en_US.UTF-8"
|
LOCAL = "de_DE.UTF-8" if LANGUAGE == "de-de" else "en_US.UTF-8"
|
||||||
|
|
||||||
locale.setlocale(locale.LC_TIME, LOCAL)
|
locale.setlocale(locale.LC_TIME, LOCAL)
|
||||||
|
|
||||||
|
standalone_str = "Artikel" if LANGUAGE == "de-de" else "standalone"
|
||||||
|
|
||||||
|
|
||||||
def gen_arch_string():
|
def gen_arch_string():
|
||||||
"""
|
"""
|
||||||
@ -70,7 +70,7 @@ def gen_index_string():
|
|||||||
contents = sorted(full_list, key=os.path.getmtime)
|
contents = sorted(full_list, key=os.path.getmtime)
|
||||||
for file in reversed(contents):
|
for file in reversed(contents):
|
||||||
filename = pathlib.PurePath(file)
|
filename = pathlib.PurePath(file)
|
||||||
# purefile = filename
|
purefile = filename
|
||||||
title = open(filename).readline().rstrip("\n")
|
title = open(filename).readline().rstrip("\n")
|
||||||
text = open(filename).readlines()[1:]
|
text = open(filename).readlines()[1:]
|
||||||
filename = filename.name
|
filename = filename.name
|
||||||
@ -161,11 +161,11 @@ def get_rss_string():
|
|||||||
string: rss-string of everything that is in the ENTRY_DIR.
|
string: rss-string of everything that is in the ENTRY_DIR.
|
||||||
"""
|
"""
|
||||||
path_ex = ENTRY_DIR
|
path_ex = ENTRY_DIR
|
||||||
content_string = ""
|
|
||||||
if path.exists(path_ex):
|
if path.exists(path_ex):
|
||||||
name_list = os.listdir(path_ex)
|
name_list = os.listdir(path_ex)
|
||||||
full_list = [os.path.join(path_ex, i) for i in name_list]
|
full_list = [os.path.join(path_ex, i) for i in name_list]
|
||||||
contents = sorted(full_list, key=os.path.getmtime)
|
contents = sorted(full_list, key=os.path.getmtime)
|
||||||
|
content_string = ""
|
||||||
for file in reversed(contents):
|
for file in reversed(contents):
|
||||||
filename = pathlib.PurePath(file)
|
filename = pathlib.PurePath(file)
|
||||||
title = open(filename).readline().rstrip("\n")
|
title = open(filename).readline().rstrip("\n")
|
||||||
@ -186,66 +186,3 @@ def get_rss_string():
|
|||||||
content_string += "</description>\n"
|
content_string += "</description>\n"
|
||||||
content_string += "</item>\n"
|
content_string += "</item>\n"
|
||||||
return content_string
|
return content_string
|
||||||
|
|
||||||
|
|
||||||
def gen_query_res_string(query_str):
|
|
||||||
"""
|
|
||||||
Return the results of a query.
|
|
||||||
|
|
||||||
Parameters:
|
|
||||||
query_str (string): term to search
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
string: html-formated search result
|
|
||||||
"""
|
|
||||||
src_results = search.search(query_str)
|
|
||||||
res_string = ""
|
|
||||||
for result in src_results:
|
|
||||||
title = result["title"]
|
|
||||||
path = result["path"]
|
|
||||||
filename = pathlib.PurePath(path)
|
|
||||||
filename = filename.name
|
|
||||||
if filename[0] != ".":
|
|
||||||
filename = filename.split(".", 1)[0]
|
|
||||||
curr_date = datetime.fromtimestamp(os.path.getmtime(path)).strftime("%Y-%m-%d")
|
|
||||||
is_markdown = path.endswith(".md")
|
|
||||||
preview = create_preview(path, is_markdown)
|
|
||||||
path = "/entry/" + path.split("/", 2)[2]
|
|
||||||
res_string += "<div class=\"entry\">"
|
|
||||||
res_string += "<a href=\"" + path + "\"><h2>" + title + "</h2></a>"
|
|
||||||
res_string += "<small>"
|
|
||||||
res_string += "<a href=\"" + "/index.html#" + \
|
|
||||||
filename + "\">" + curr_date + "</a>"
|
|
||||||
res_string += "</small><br><br>"
|
|
||||||
res_string += preview + "</div>"
|
|
||||||
return res_string
|
|
||||||
|
|
||||||
|
|
||||||
def create_preview(path, is_markdown):
|
|
||||||
"""
|
|
||||||
Create a preview of a given article and return it.
|
|
||||||
|
|
||||||
Parameters:
|
|
||||||
path (string): path to the article
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
string: html-formated preview
|
|
||||||
"""
|
|
||||||
file = open(path, "r", encoding="utf-8")
|
|
||||||
first_lines = file.readlines()
|
|
||||||
preview = ""
|
|
||||||
preview_length = 3
|
|
||||||
for i, line in enumerate(first_lines):
|
|
||||||
if i == 0:
|
|
||||||
continue
|
|
||||||
if i > preview_length:
|
|
||||||
break
|
|
||||||
if not line.isspace():
|
|
||||||
if is_markdown:
|
|
||||||
preview += markdown.markdown(line)
|
|
||||||
else:
|
|
||||||
preview += line
|
|
||||||
else:
|
|
||||||
preview_length += 1
|
|
||||||
preview += "<br>...<br>"
|
|
||||||
return preview
|
|
||||||
|
16
src/forms.py
16
src/forms.py
@ -1,16 +0,0 @@
|
|||||||
import os
|
|
||||||
|
|
||||||
from flask_wtf import CSRFProtect, FlaskForm
|
|
||||||
from wtforms import StringField, SubmitField, ValidationError, validators
|
|
||||||
|
|
||||||
|
|
||||||
def register_csrf(app):
|
|
||||||
csrf = CSRFProtect()
|
|
||||||
SECRET_KEY = os.urandom(32)
|
|
||||||
app.secret_key = SECRET_KEY
|
|
||||||
csrf.init_app(app)
|
|
||||||
|
|
||||||
|
|
||||||
class SearchForm(FlaskForm):
|
|
||||||
query_str = StringField("Query", [validators.DataRequired("Please enter the search term")])
|
|
||||||
# submit = SubmitField("Search")
|
|
@ -1,6 +1,2 @@
|
|||||||
Flask
|
Flask
|
||||||
Markdown
|
Markdown
|
||||||
Whoosh
|
|
||||||
WTForms
|
|
||||||
Flask_WTF
|
|
||||||
Font-Awesome-Flask
|
|
||||||
|
@ -1,72 +0,0 @@
|
|||||||
import os
|
|
||||||
|
|
||||||
from whoosh import scoring
|
|
||||||
from whoosh.fields import ID, TEXT, Schema
|
|
||||||
from whoosh.index import create_in, open_dir
|
|
||||||
from whoosh.qparser import QueryParser
|
|
||||||
|
|
||||||
import config
|
|
||||||
|
|
||||||
INDEX_DIR = "indexdir"
|
|
||||||
DEF_TOPN = 10
|
|
||||||
ENTRY_DIR = config.ENTRY_DIR
|
|
||||||
|
|
||||||
|
|
||||||
def createSearchableData(root):
|
|
||||||
"""
|
|
||||||
|
|
||||||
Schema definition: title(name of file), path(as ID), content(indexed but not stored), textdata (stored text content)
|
|
||||||
source:
|
|
||||||
https://appliedmachinelearning.blog/2018/07/31/developing-a-fast-indexing-and-full-text-search-engine-with-whoosh-a-pure-pythhon-library/
|
|
||||||
"""
|
|
||||||
schema = Schema(title=TEXT(stored=True), path=ID(stored=True), content=TEXT)
|
|
||||||
if not os.path.exists(INDEX_DIR):
|
|
||||||
os.mkdir(INDEX_DIR)
|
|
||||||
ix = create_in(INDEX_DIR, schema)
|
|
||||||
writer = ix.writer()
|
|
||||||
for r, _, f in os.walk(root):
|
|
||||||
for file in f:
|
|
||||||
path = os.path.join(r, file)
|
|
||||||
fp = open(path, encoding="utf-8")
|
|
||||||
title = fp.readline()
|
|
||||||
text = title + fp.read()
|
|
||||||
writer.add_document(title=title, path=path, content=text)
|
|
||||||
fp.close()
|
|
||||||
writer.commit()
|
|
||||||
|
|
||||||
|
|
||||||
def search_times(query_str, topN):
|
|
||||||
"""
|
|
||||||
Search for a given term and returns a specific amount of results.
|
|
||||||
|
|
||||||
Parameters:
|
|
||||||
query_str (string): term to search for
|
|
||||||
topN (int): number of results to return
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
string: html-formatted string including the hits of the search
|
|
||||||
"""
|
|
||||||
ix = open_dir(INDEX_DIR)
|
|
||||||
results = []
|
|
||||||
with ix.searcher(weighting=scoring.BM25F) as s:
|
|
||||||
query = QueryParser("content", ix.schema).parse(query_str)
|
|
||||||
matches = s.search(query, limit=topN)
|
|
||||||
for match in matches:
|
|
||||||
results.append({"title": match["title"], "path": match["path"], "match": match.score})
|
|
||||||
return results
|
|
||||||
|
|
||||||
|
|
||||||
def search(query_str):
|
|
||||||
"""
|
|
||||||
Search for a given term and show the predefined amount of results.
|
|
||||||
|
|
||||||
Parameters:
|
|
||||||
query_str (string): term to search for
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
string: html-formatted string including the hits of the search
|
|
||||||
"""
|
|
||||||
return search_times(query_str, DEF_TOPN)
|
|
||||||
|
|
||||||
|
|
||||||
createSearchableData(ENTRY_DIR)
|
|
@ -3,7 +3,6 @@
|
|||||||
:root {
|
:root {
|
||||||
--bg0: rgb(29,32,33);
|
--bg0: rgb(29,32,33);
|
||||||
--color0: rgb(220,120,0);
|
--color0: rgb(220,120,0);
|
||||||
--color1: rgb(280,180,0);
|
|
||||||
--error: rgb(255,0,0);
|
--error: rgb(255,0,0);
|
||||||
--footerbg0: rgb(29,32,33);
|
--footerbg0: rgb(29,32,33);
|
||||||
--link0: rgb(220, 120, 0);
|
--link0: rgb(220, 120, 0);
|
||||||
@ -84,13 +83,3 @@ span {
|
|||||||
.entry h2 {
|
.entry h2 {
|
||||||
color: var(--text1);
|
color: var(--text1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
form.search button[type=submit] {
|
|
||||||
background: var(--color0);
|
|
||||||
color: var(--bg0);
|
|
||||||
}
|
|
||||||
|
|
||||||
form.search button[type=submit]:hover {
|
|
||||||
background: var(--color1);
|
|
||||||
}
|
|
||||||
|
@ -3,7 +3,6 @@
|
|||||||
:root {
|
:root {
|
||||||
--bg0: rgb(255,255,255);
|
--bg0: rgb(255,255,255);
|
||||||
--color0: rgb(0,0,120);
|
--color0: rgb(0,0,120);
|
||||||
--color1: rgb(0,0,200);
|
|
||||||
--error: rgb(255,0,0);
|
--error: rgb(255,0,0);
|
||||||
--footerbg0: rgb(192,192,192);
|
--footerbg0: rgb(192,192,192);
|
||||||
--link0: rgb(0,0,120);
|
--link0: rgb(0,0,120);
|
||||||
@ -92,12 +91,3 @@ span {
|
|||||||
.entry h2 {
|
.entry h2 {
|
||||||
color: var(--text1);
|
color: var(--text1);
|
||||||
}
|
}
|
||||||
|
|
||||||
form.search button[type=submit] {
|
|
||||||
background: var(--color0);
|
|
||||||
color: var(--bg0);
|
|
||||||
}
|
|
||||||
|
|
||||||
form.search button[type=submit]:hover {
|
|
||||||
background: var(--color1);
|
|
||||||
}
|
|
||||||
|
@ -157,10 +157,6 @@ footer .center {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
form {
|
|
||||||
margin-bottom: 40px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.entry {
|
.entry {
|
||||||
border-radius: 0 10px 30px 0;
|
border-radius: 0 10px 30px 0;
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
@ -190,15 +186,6 @@ h3 {
|
|||||||
padding-bottom: 0;
|
padding-bottom: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.blogarchive h1:first-child {
|
|
||||||
padding-bottom: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.blogarchive h2:first-child {
|
|
||||||
padding-top: 0;
|
|
||||||
padding-bottom: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.entry ul {
|
.entry ul {
|
||||||
padding-left: 20;
|
padding-left: 20;
|
||||||
}
|
}
|
||||||
@ -229,28 +216,3 @@ code {
|
|||||||
white-space: pre;
|
white-space: pre;
|
||||||
display: inline-block
|
display: inline-block
|
||||||
}
|
}
|
||||||
|
|
||||||
form.search input[type=text] {
|
|
||||||
padding: 10px;
|
|
||||||
font-size: 17px;
|
|
||||||
border: 1px solid grey;
|
|
||||||
float: left;
|
|
||||||
width: 50%;
|
|
||||||
background: #f1f1f1;
|
|
||||||
}
|
|
||||||
|
|
||||||
form.search button[type=submit] {
|
|
||||||
float: left;
|
|
||||||
width: 5%;
|
|
||||||
padding: 12px;
|
|
||||||
font-size: 17px;
|
|
||||||
border: 1px solid grey;
|
|
||||||
border-left: none; /* Prevent double borders */
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
form.search::after {
|
|
||||||
content: "";
|
|
||||||
clear: both;
|
|
||||||
display: table;
|
|
||||||
}
|
|
||||||
|
@ -1,19 +0,0 @@
|
|||||||
{% extends "template.html" %}
|
|
||||||
{% block content %}
|
|
||||||
|
|
||||||
<div class="container">
|
|
||||||
<div class="search">
|
|
||||||
<h1>{% if language=="de-de" %}Suche{% else %}Search{% endif %}</h1><br>
|
|
||||||
<form class="search" action="{{ url_for('search') }}" method=post>
|
|
||||||
{{ form.hidden_tag() }}
|
|
||||||
{{ form.query_str }}
|
|
||||||
<!-- {{ form.submit }} -->
|
|
||||||
<button id="submit" name="submit" type="submit" value="Search">{{ font_awesome.render_icon("fas fa-search")
|
|
||||||
}}</button>
|
|
||||||
</form>
|
|
||||||
{% autoescape off %}
|
|
||||||
{{ content }}
|
|
||||||
{% endautoescape %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endblock %}
|
|
@ -1,14 +1,11 @@
|
|||||||
<html>
|
<html>
|
||||||
|
|
||||||
<head>
|
<head>
|
||||||
<title>{{ title }}</title>
|
<title>{{ title }}</title>
|
||||||
<link href="{{ url_for('static', filename='css/' + style + '.css') }}" rel="stylesheet" type="text/css">
|
<link href="{{ url_for('static', filename='css/' + style + '.css') }}" rel="stylesheet" type="text/css">
|
||||||
<link rel="icon" type="image/x-icon" href="{{ url_for('static', filename='graphics/logo.png') }}">
|
<link rel="icon" type="image/x-icon" href="{{ url_for('static', filename='graphics/logo.png') }}">
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta name="viewport" content="width=device-width" initial-scale=1.0>
|
<meta name="viewport" content="width=device-width" initial-scale=1.0>
|
||||||
{{ font_awesome.load_js() }}
|
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<!-- Menu -->
|
<!-- Menu -->
|
||||||
<div class="main-menu-dropdown">
|
<div class="main-menu-dropdown">
|
||||||
@ -21,7 +18,6 @@
|
|||||||
<div class="main-menu">
|
<div class="main-menu">
|
||||||
<a href="{{ url_for('index') }}">Blog</a>
|
<a href="{{ url_for('index') }}">Blog</a>
|
||||||
<a href="{{ url_for('archive') }}">{% if language=="de-de" %}Archiv{% else %}Archive{% endif %}</a>
|
<a href="{{ url_for('archive') }}">{% if language=="de-de" %}Archiv{% else %}Archive{% endif %}</a>
|
||||||
<a href="{{ url_for('search') }}">{% if language=="de-de" %}Suche{% else %}Search{% endif %}</a>
|
|
||||||
<label for="main-menu-check" class="hide-menu">X</label>
|
<label for="main-menu-check" class="hide-menu">X</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -39,5 +35,4 @@
|
|||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user