mirror of
https://github.com/FAUSheppy/flask-json-dream-website
synced 2025-12-06 00:01:36 +01:00
refactor
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,4 +1,5 @@
|
||||
js/
|
||||
__pycache__/
|
||||
css/
|
||||
*.swp
|
||||
*.jpg
|
||||
|
||||
42
README.md
42
README.md
@@ -1,50 +1,14 @@
|
||||
# Requirements
|
||||
This Softwares runs python3-flask with markdown, json and caldav.
|
||||
|
||||
python3 -m pip install flask, json, caldav, markdown2
|
||||
python3 -m pip install flask, caldav, markdown2
|
||||
|
||||
This Software requires bootstrap > 4.13 which can be downloaded [here](https://getbootstrap.com/docs/4.3/getting-started/download/). It must be unpacked into the *static/*-directory into *js* and *css* respectively.
|
||||
|
||||
Additionally bootstrap depends on [jquery](https://code.jquery.com) which you have to download into a file called *jquery.min.js* in *static/js/*.
|
||||
|
||||
# Usage
|
||||
|
||||
./server.py -h
|
||||
usage: server.py [-h] [-i INTERFACE] [-p PORT] --cal-info CAL_INFO
|
||||
[--no-update-on-start]
|
||||
|
||||
optional arguments:
|
||||
-h, --help show this help message and exit
|
||||
-i INTERFACE Interface to listen on (default: 0.0.0.0)
|
||||
-p PORT, --port PORT Port to listen on (default: 5000)
|
||||
--cal-info CAL_INFO File Containing a public calendar link (default: None)
|
||||
--no-update-on-start Don't update the calendar on start (default: False)
|
||||
|
||||
|
||||
# Configuration
|
||||
The page and most of it's content is configured via json. To use the CalDav-events section, you need to add a comma seperated file with the following format format/information:
|
||||
|
||||
URL,USER,PASSWORD
|
||||
|
||||
## Main Config
|
||||
The main Config ``config.json`` which must be placed in the project-root must contain the following values:
|
||||
|
||||
{
|
||||
"siteTitle" : "the default site title",
|
||||
"siteDescription" : "a description for this site",
|
||||
"siteLogo" : "url to logo",
|
||||
"siteURL": "the url of this site"
|
||||
}
|
||||
|
||||
Additionally it may contain the following information:
|
||||
|
||||
"teamspeak-server" : "TS_SERVER",
|
||||
"discord-server" : "DISCORD_LINK",
|
||||
"facebook" : "FACEBOOK_LINK",
|
||||
"instagram" : "INSTAGRAM_LINK",
|
||||
"twitter" : "TWITTER_LINK",
|
||||
"twitch-channel" : "TWITCH_CHANNEL_NAME",
|
||||
"twitch-placeholder-img" : "PLACEHOLDER_IMG"
|
||||
The page and most of it's content is configured via json, basic configuration is done in *config.py*.
|
||||
|
||||
## Startpage Sections
|
||||
### Events
|
||||
@@ -108,4 +72,4 @@ New subpages must be added as a new location in the *server.py* like this:
|
||||
def subpage():
|
||||
return flask.render_template("subpage.html", conf=mainConfig)
|
||||
|
||||
See the example subpage-templates in *templates/*.
|
||||
See the example *subpage\_example.html* in *templates/*.
|
||||
|
||||
4
app.py
Normal file
4
app.py
Normal file
@@ -0,0 +1,4 @@
|
||||
import server as moduleContainingApp
|
||||
|
||||
def createApp(envivorment=None, start_response=None):
|
||||
return moduleContainingApp.app
|
||||
31
config.py
Normal file
31
config.py
Normal file
@@ -0,0 +1,31 @@
|
||||
# calendar configuration #
|
||||
SHOW_CALENDAR=False
|
||||
CALENDAR_URL=""
|
||||
CALENDAR_USERNAME=""
|
||||
CALENDAR_PASSWORD=""
|
||||
|
||||
# content directory #
|
||||
CONTENT_DIR="content.example"
|
||||
|
||||
# reload calendar on start #
|
||||
RELOAD_CALENDAR_ON_START=True
|
||||
|
||||
# other
|
||||
NEWS_MAX_AGE=90
|
||||
SITEMAP_IGNORE = ["icon", "siteMap", "invalidate", "news"]
|
||||
|
||||
# site parameters
|
||||
SITE_TITLE = "Site Title"
|
||||
SITE_DESCRIPTION = "Site Description"
|
||||
SITE_AUTHOR = "Site Author"
|
||||
SITE_LOGO_URL = "Site Logo URL"
|
||||
SITE_BASE_URL = "Site Base URL"
|
||||
|
||||
TEAMSPEAK_SERVER = "teamspeak.com"
|
||||
DISCORD_SERVER = "https://discord.gg/",
|
||||
FACEBOOK = "https://www.facebook.com/",
|
||||
INSTAGRAM = "https://www.instagram.com/",
|
||||
TWITTER = "https://twitter.com/its_a_sheppy",
|
||||
|
||||
TWITCH_CHANNEL = "esports_erlangen",
|
||||
TWITCH_PLACEHOLDER_IMG = "placeholder.png"
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"picture" : "/static/pictures/placeholder.png",
|
||||
"picture" : "/pictures/placeholder.png",
|
||||
"title" : "Section Title",
|
||||
"text" : "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.",
|
||||
"moreInfoButtonText" : "Mehr..",
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"picture" : "/static/pictures/placeholder.png",
|
||||
"picture" : "/pictures/placeholder.png",
|
||||
"title" : "Section Title",
|
||||
"text" : "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"picture" : "/static/pictures/placeholder.png",
|
||||
"picture" : "/pictures/placeholder.png",
|
||||
"title" : "Section Title",
|
||||
"text" : "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.",
|
||||
"moreInfoButtonText" : "Mehr..",
|
||||
225
server.py
225
server.py
@@ -8,49 +8,64 @@ import caldav
|
||||
import datetime as dt
|
||||
import markdown2
|
||||
|
||||
# sitemap utilities #
|
||||
# sitemap utilities
|
||||
from werkzeug.routing import BuildError
|
||||
import xml.etree.ElementTree as et
|
||||
|
||||
VEREIN_SECTIONS_DIR = "sections/"
|
||||
MAIN_LINKS_DIR = "main-links/"
|
||||
NEWS_DIR = "news/"
|
||||
# paths
|
||||
SECTIONS_DIR = "sections/"
|
||||
NEWS_DIR = "news/"
|
||||
PICTURES_DIR = "pictures/"
|
||||
|
||||
app = flask.Flask("athq-landing-page", static_folder=None)
|
||||
mainConfig = dict()
|
||||
with open("config.json") as f:
|
||||
mainConfig = json.load(f)
|
||||
# json config keys
|
||||
TIMEOUT_RELATIVE = "timeout-relative-weeks"
|
||||
TIMEOUT_FIXED = "timeout-fixed"
|
||||
PARSED_TIME = "parsed-time"
|
||||
ACTIVE = "active"
|
||||
DATE = "date"
|
||||
UID = "uid"
|
||||
|
||||
caldavUrl = None
|
||||
caldavPassword = None
|
||||
caldavUsername = None
|
||||
MARKDOWN_FILE_KEY = "markdown-file"
|
||||
MARKDOWN_CONTENT_KEY = "markdown-content"
|
||||
|
||||
# sitemap
|
||||
PRIORITY_PRIMARY = 1.0
|
||||
PRIORITY_SECONDARY = 0.8
|
||||
|
||||
# other
|
||||
HTTP_NOT_FOUND = 404
|
||||
EMPTY_STRING = ""
|
||||
CACHE_FILE = "cache.json"
|
||||
READ = "r"
|
||||
WRITE = "w"
|
||||
|
||||
app = flask.Flask("FLASK_JSON_DREAM_WEBSITE", static_folder=None)
|
||||
app.config.from_object("config")
|
||||
|
||||
def updateEventsFromCalDav():
|
||||
'''Load event from a remote calendar'''
|
||||
|
||||
|
||||
if app.config["USE_CALENDAR"]:
|
||||
if app.config["SHOW_CALENDAR"]:
|
||||
client = caldav.DAVClient(url=caldavUrl, username=caldavUsername, password=caldavPassword)
|
||||
authenticatedClient = client.principal()
|
||||
defaultCal = authenticatedClient.calendars()[0]
|
||||
|
||||
start = dt.datetime.now()
|
||||
start = start - dt.timedelta(seconds=start.timestamp() % dt.timedelta(days=1).total_seconds())
|
||||
end = start + dt.timedelta(days=90)
|
||||
|
||||
# TODO remove this
|
||||
# start = start - dt.timedelta(days=90)
|
||||
start -= dt.timedelta(seconds=start.timestamp() % dt.timedelta(days=1).total_seconds())
|
||||
end = start + dt.timedelta(days=app.config["NEWS_MAX_AGE"])
|
||||
|
||||
events = sorted(defaultCal.date_search(start, end),
|
||||
key=lambda e: e.vobject_instance.vevent.dtstart.value)
|
||||
|
||||
eventsDictList = []
|
||||
for e in events:
|
||||
date = e.vobject_instance.vevent.dtstart.value
|
||||
date += dt.timedelta(hours=2)
|
||||
newEventDict = { "description" : e.vobject_instance.vevent.summary.value,
|
||||
"time" : date.strftime("%H:%M"),
|
||||
"day" : date.strftime("%d"),
|
||||
"month" : date.strftime("%b"),
|
||||
"year" : date.strftime("%Y") }
|
||||
"time" : date.strftime("%H:%M"),
|
||||
"day" : date.strftime("%d"),
|
||||
"month" : date.strftime("%b"),
|
||||
"year" : date.strftime("%Y")}
|
||||
try:
|
||||
newEventDict.update({ "location" : e.vobject_instance.vevent.location.value })
|
||||
except AttributeError:
|
||||
@@ -59,17 +74,20 @@ def updateEventsFromCalDav():
|
||||
else:
|
||||
eventsDictList = []
|
||||
|
||||
with open("cache.json", "w") as f:
|
||||
# dump to cache file #
|
||||
with open(CACHE_FILE, WRITE) as f:
|
||||
json.dump(eventsDictList, f)
|
||||
|
||||
|
||||
def getEventsCache():
|
||||
with open("cache.json", "r") as f:
|
||||
'''Return the cached events'''
|
||||
|
||||
with open(CACHE_FILE, READ) as f:
|
||||
return json.load(f)
|
||||
|
||||
def readJsonDir(basedir):
|
||||
'''Read a directory containing json information'''
|
||||
|
||||
# load json files from projects/ dir #
|
||||
jsonDictList =[]
|
||||
for root, dirs, files in os.walk(basedir):
|
||||
for filename in sorted(files):
|
||||
@@ -80,14 +98,10 @@ def readJsonDir(basedir):
|
||||
return jsonDictList
|
||||
|
||||
def parseNewsDirWithTimeout():
|
||||
'''Parse a directory containing news-json structs and filter out
|
||||
entries that have exceeded the max age'''
|
||||
|
||||
TIMEOUT_RELATIVE = "timeout-relative-weeks"
|
||||
TIMEOUT_FIXED = "timeout-fixed"
|
||||
PARSED_TIME = "parsed-time"
|
||||
ACTIVE = "active"
|
||||
DATE = "date"
|
||||
|
||||
news = readJsonDir(NEWS_DIR)
|
||||
news = readJsonDir(app.config["NEWS_DIR"])
|
||||
now = dt.datetime.now()
|
||||
for n in news:
|
||||
n.update( { PARSED_TIME : dt.datetime.fromtimestamp(n[DATE]) } )
|
||||
@@ -104,96 +118,107 @@ def parseNewsDirWithTimeout():
|
||||
|
||||
return sorted(news, key=lambda n: n[PARSED_TIME], reverse=True)
|
||||
|
||||
|
||||
@app.route("/invalidate")
|
||||
def invalidateEventCache():
|
||||
'''Reload the calendar events'''
|
||||
|
||||
updateEventsFromCalDav();
|
||||
return ("", 204)
|
||||
return (EMPTY_STRING, 204)
|
||||
|
||||
@app.route("/")
|
||||
def root():
|
||||
announcements = parseNewsDirWithTimeout()
|
||||
return flask.render_template("index.html", mainLinks=readJsonDir(MAIN_LINKS_DIR),
|
||||
siteTitle=mainConfig["siteTitle"],
|
||||
conf=mainConfig,
|
||||
events=getEventsCache(),
|
||||
moreEvents=len(getEventsCache())>3,
|
||||
vereinSections=readJsonDir(VEREIN_SECTIONS_DIR),
|
||||
announcements=announcements)
|
||||
return flask.render_template("index.html", conf=app.config,
|
||||
events=getEventsCache(),
|
||||
moreEvents=len(getEventsCache())>3,
|
||||
sections=readJsonDir(app.config["SECTIONS_DIR"]),
|
||||
announcements=parseNewsDirWithTimeout())
|
||||
|
||||
@app.route("/impressum")
|
||||
def impressum():
|
||||
return flask.render_template("impressum.html", conf=mainConfig)
|
||||
|
||||
@app.route("/verein")
|
||||
def verein():
|
||||
return flask.render_template("verein.html", conf=mainConfig)
|
||||
|
||||
@app.route("/stammtisch")
|
||||
def stammtisch():
|
||||
return flask.render_template("stammtisch.html", conf=mainConfig)
|
||||
return flask.render_template("impressum.html", conf=app.config)
|
||||
|
||||
@app.route("/people")
|
||||
def people():
|
||||
return flask.render_template("people.html", conf=mainConfig,
|
||||
return flask.render_template("people.html", conf=app.config,
|
||||
people=readJsonDir("people/"))
|
||||
|
||||
@app.route("/news")
|
||||
def news():
|
||||
'''Display news-articles based on a UID-parameter'''
|
||||
|
||||
uid = flask.request.args.get("uid")
|
||||
requestedId = flask.request.args.get(UID)
|
||||
|
||||
# load news and map UIDs #
|
||||
news = parseNewsDirWithTimeout()
|
||||
newsDict = dict()
|
||||
for n in news:
|
||||
newsDict.update( { n["uid"] : n } )
|
||||
newsDict.update( { n[UID] : n } )
|
||||
|
||||
if not uid:
|
||||
article = sorted(news, key=lambda n: n["parsed-time"])[-1]
|
||||
elif not newsDict[int(uid)]:
|
||||
return ("", 404)
|
||||
# set newest article config if there is not UID #
|
||||
# return 404 if the UID doesnt exist #
|
||||
# set article config of matching article otherwiese #
|
||||
if not requestedId:
|
||||
article = sorted(news, key=lambda n: n[PARSED_TIME])[-1]
|
||||
elif not newsDict[int(requestedId)]:
|
||||
return (EMPTY_STRING, HTTP_NOT_FOUND)
|
||||
else:
|
||||
article = newsDict[int(uid)]
|
||||
try:
|
||||
with open(article["markdown-file"]) as f:
|
||||
article.update( { "markdown-content" : markdown2.markdown(f.read()) } )
|
||||
except FileNotFoundError as e:
|
||||
return ("File not found Error ({})".format(e), 404)
|
||||
article = newsDict[int(requestedId)]
|
||||
|
||||
return flask.render_template("news.html", conf=mainConfig, article=article)
|
||||
# load article based on config #
|
||||
try:
|
||||
with open(article[MARKDOWN_FILE_KEY]) as f:
|
||||
article.update( { MARKDOWN_CONTENT_KEY : markdown2.markdown(f.read()) } )
|
||||
except FileNotFoundError as e:
|
||||
return ("File not found Error ({})".format(e), HTTP_NOT_FOUND)
|
||||
|
||||
return flask.render_template("news.html", conf=app.config, article=article)
|
||||
|
||||
@app.route("/static/<path:path>")
|
||||
def sendStatic(path):
|
||||
if "pictures" in path:
|
||||
cache_timeout = 2592000
|
||||
else:
|
||||
cache_timeout = None
|
||||
cache_timeout = None
|
||||
return flask.send_from_directory('static', path, cache_timeout=cache_timeout)
|
||||
|
||||
@app.route("/picture/<path:path>")
|
||||
def sendPicture(path):
|
||||
cache_timeout = 2592000
|
||||
return flask.send_from_directory(PICTURES_DIR, path, cache_timeout=cache_timeout)
|
||||
|
||||
@app.route('/defaultFavicon.ico')
|
||||
def icon():
|
||||
return flask.send_from_directory('static', 'defaultFavicon.ico')
|
||||
|
||||
@app.route("/sitemap.xml")
|
||||
def siteMap():
|
||||
urls = []
|
||||
for rule in app.url_map.iter_rules():
|
||||
skips = ["icon", "siteMap", "invalidate", "news"]
|
||||
if any([s in rule.endpoint for s in skips]):
|
||||
continue
|
||||
if "GET" in rule.methods:
|
||||
try:
|
||||
url = flask.url_for(rule.endpoint, **(rule.defaults or {}))
|
||||
priority = 0.8
|
||||
if rule.endpoint == "root":
|
||||
priority = 1.0
|
||||
urls += [(url, app.config["START_TIME"], priority)]
|
||||
except BuildError:
|
||||
pass
|
||||
'''Return an XML-sitemap for SEO'''
|
||||
|
||||
# search for urls to add to sitemap #
|
||||
urls = []
|
||||
|
||||
# iterate through all endpoints #
|
||||
for rule in app.url_map.iter_rules():
|
||||
|
||||
# skip all endpoints #
|
||||
if any([s in rule.endpoint for s in app.config["SITEMAP_IGNORE"]]):
|
||||
continue
|
||||
|
||||
# skip all non-GET endpoints #
|
||||
if not "GET" in rule.methods:
|
||||
continue
|
||||
|
||||
# get url for endpoint, get start time and set priority #
|
||||
try:
|
||||
url = flask.url_for(rule.endpoint, **(rule.defaults or {}))
|
||||
priority = PRIORITY_SECONDARY
|
||||
if rule.endpoint == "root":
|
||||
priority = PRIORITY_PRIMARY
|
||||
urls += [(url, app.config["START_TIME"], priority)]
|
||||
except BuildError:
|
||||
pass
|
||||
|
||||
# add news articles to sitemap #
|
||||
news = parseNewsDirWithTimeout()
|
||||
for n in filter(lambda x: x["active"], news):
|
||||
urls += [("/news?uid={}".format(n["uid"]), n["parsed-time"], 0.8)]
|
||||
urls += [("/news?uid={}".format(n[UID]), n[PARSED_TIME], PRIORITY_SECONDARY)]
|
||||
|
||||
hostname = flask.request.headers.get("X-REAL-HOSTNAME")
|
||||
if not hostname:
|
||||
@@ -215,32 +240,30 @@ def siteMap():
|
||||
xmlDump += et.tostring(top, encoding='UTF-8', method='xml').decode()
|
||||
return flask.Response(xmlDump, mimetype='application/xml')
|
||||
|
||||
@app.before_first_request
|
||||
def init():
|
||||
app.config["SECTIONS_DIR"] = os.path.join(app.config["CONTENT_DIR"], SECTIONS_DIR)
|
||||
app.config["NEWS_DIR"] = os.path.join(app.config["CONTENT_DIR"], NEWS_DIR)
|
||||
|
||||
if app.config["RELOAD_CALENDAR_ON_START"]:
|
||||
updateEventsFromCalDav()
|
||||
|
||||
app.config["START_TIME"] = dt.datetime.now()
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
parser = argparse.ArgumentParser(description='Projects Showcase',
|
||||
formatter_class=argparse.ArgumentDefaultsHelpFormatter)
|
||||
|
||||
# general parameters #
|
||||
parser.add_argument("-i", "--interface", default="0.0.0.0", help="Interface to listen on")
|
||||
parser.add_argument("-p", "--port", default="5000", help="Port to listen on")
|
||||
parser.add_argument("--cal-info", help="File Containing a public calendar link")
|
||||
parser.add_argument("-i", "--interface", default="127.0.0.1", help="Interface to listen on")
|
||||
parser.add_argument("-p", "--port", default="5000", help="Port to listen on")
|
||||
parser.add_argument("--auto-reload", action="store_const", default=False, const=True,
|
||||
help="Automaticly reload HTTP templates (impacts performance)")
|
||||
parser.add_argument("--no-update-on-start", action="store_const", const=True, default=False,
|
||||
help="Don't update the calendar on start")
|
||||
|
||||
# startup #
|
||||
app.config['TEMPLATES_AUTO_RELOAD'] = True
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.cal_info:
|
||||
app.config["USE_CALENDAR"] = True
|
||||
with open(args.cal_info) as f:
|
||||
caldavUrl, caldavUsername, caldavPassword = f.read().strip().split(",")
|
||||
else:
|
||||
app.config["USE_CALENDAR"] = False
|
||||
|
||||
if not args.no_update_on_start:
|
||||
updateEventsFromCalDav()
|
||||
|
||||
app.config["START_TIME"] = dt.datetime.now()
|
||||
|
||||
app.config['TEMPLATES_AUTO_RELOAD'] = args.auto_reload
|
||||
app.run(host=args.interface, port=args.port)
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 2.1 KiB |
@@ -1 +0,0 @@
|
||||
placeholder.png
|
||||
@@ -1,7 +1,10 @@
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||
<meta name="description" content="">
|
||||
<meta name="author" content="">
|
||||
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||
<meta name="description" content="{{ conf['SITE_DESCRIPTION'] }}">
|
||||
<meta name="author" content="{{ conf['SITE_AUTHOR'] }}">
|
||||
<meta name="title" content="{{ conf['SITE_TITLE'] }}">
|
||||
|
||||
<link rel="shortcut icon" href="/defaultFavicon.ico">
|
||||
|
||||
<!-- Bootstrap core CSS -->
|
||||
@@ -14,16 +17,16 @@
|
||||
<script src="/static/js/bootstrap.min.js"></script>
|
||||
|
||||
<meta property="og:type" content="website" />
|
||||
<meta property="og:title" content="{{ conf['siteTitle'] }}" />
|
||||
<meta property="og:description" content="{{ conf['siteDescription'] }}" />
|
||||
<meta property="og:title" content="{{ conf['SITE_TITLE'] }}" />
|
||||
<meta property="og:description" content="{{ conf['SITE_DESCRIPTION'] }}" />
|
||||
<meta property="og:url" content="{{ url_for(request.endpoint) }}" />
|
||||
<meta property="og:image" content="{{ conf['siteLogo'] }}">
|
||||
<meta property="og:image" content="{{ conf['SITE_LOG_URL'] }}">
|
||||
|
||||
<script type="application/ld+json">
|
||||
{
|
||||
"@context": "https://schema.org",
|
||||
"@type": "Organization",
|
||||
"url": "{{ conf['siteURL'] }}",
|
||||
"logo": "{{ conf['siteLogo'] }}"
|
||||
"url": "{{ conf['SITE_BASE_URL'] }}",
|
||||
"logo": "{{ conf['SITE_LOGO_URL'] }}"
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -4,8 +4,6 @@
|
||||
|
||||
{% include 'head.html' %}
|
||||
|
||||
<title>{{ conf["siteTitle"] }}</title>
|
||||
|
||||
</head>
|
||||
<body style="background-color: #eae9e9">
|
||||
|
||||
|
||||
@@ -5,8 +5,6 @@
|
||||
|
||||
{% include 'head.html' %}
|
||||
|
||||
<title>{{ conf["siteTitle"] }}</title>
|
||||
|
||||
<!-- Load the Twitch embed script -->
|
||||
<!-- <script src="https://embed.twitch.tv/embed/v1.js"></script> -->
|
||||
<script src="https://sslrelay.atlantishq.de/twitch"></script>
|
||||
@@ -55,7 +53,7 @@
|
||||
|
||||
<div id="twitch-consent-placeholder" class="card bg-dark text-white">
|
||||
<img style="min-width: 80%; min-height: 200px;"
|
||||
class="card-img" src="/static/pictures/{{ conf['twitch-placeholder-img'] }}" >
|
||||
class="card-img" src="/pictures/{{ conf['twitch-placeholder-img'] }}" >
|
||||
<div class="card-img-overlay">
|
||||
<label class="switch mt-3 mt-0-u440">
|
||||
<input id="toogle-twitch" class="custom-control-input"
|
||||
@@ -97,7 +95,7 @@
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% for section in vereinSections %}
|
||||
{% for section in sections %}
|
||||
<div class="{% if loop.index %2 == 1 %} bg-secondary {% else %} bg-dark {% endif %} pt-2 pb-2">
|
||||
<div class="container text-color-special">
|
||||
<div class="row" {% if loop.index %2 == 1 %} style="flex-direction: row-reverse;" {% endif %}>
|
||||
|
||||
@@ -15,15 +15,15 @@
|
||||
<a class="nav-link" href="/">Home</a>
|
||||
</li>
|
||||
|
||||
{% if conf["teamspeak-server"] %}
|
||||
{% if conf["TEAMSPEAK_SERVER"] %}
|
||||
<li class="nav-item right">
|
||||
<a class="nav-link" href="ts3server://{{ conf['teamspeak-server'] }}">Teamspeak</a>
|
||||
<a class="nav-link" href="ts3server://{{ conf['TEAMSPEAK_SERVER'] }}">Teamspeak</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
|
||||
{% if conf["discord-server"] %}
|
||||
{% if conf["DISCORD_SERVER"] %}
|
||||
<li class="nav-item right">
|
||||
<a class="nav-link" href="{{ conf['discord-server'] }}">Discord</a>
|
||||
<a class="nav-link" href="{{ conf['DISCORD_SERVER'] }}">Discord</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
|
||||
@@ -32,21 +32,21 @@
|
||||
<!-- right side -->
|
||||
<ul class="navbar-nav">
|
||||
|
||||
{% if conf["instagram"] %}
|
||||
{% if conf["INSTAGRAM"] %}
|
||||
<li class="nav-item right">
|
||||
<a class="nav-link" href="{{ conf['instagram'] }}">Instagram</a>
|
||||
<a class="nav-link" href="{{ conf['INSTAGRAM'] }}">Instagram</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
|
||||
{% if conf["facebook"] %}
|
||||
{% if conf["FACEBOOK"] %}
|
||||
<li class="nav-item right">
|
||||
<a class="nav-link" href="{{ conf['facebook'] }}">Facebook</a>
|
||||
<a class="nav-link" href="{{ conf['FACEBOOK'] }}">Facebook</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
|
||||
{% if conf["twitter"] %}
|
||||
{% if conf["TWITTER"] %}
|
||||
<li>
|
||||
<a class="nav-link" href="{{ conf['twitter'] }}">Twitter</a>
|
||||
<a class="nav-link" href="{{ conf['TWITTER'] }}">Twitter</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
</div>
|
||||
<div class="col image-min-dimensions">
|
||||
<img class="img-responsive w-100 image-max-dimensions"
|
||||
src="/static/pictures/{{ p['image'] }}"></img>
|
||||
src="/pictures/{{ p['image'] }}"></img>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
|
||||
Reference in New Issue
Block a user