diff --git a/db.py b/db.py
new file mode 100644
index 0000000000000000000000000000000000000000..9cc7b5d273db4a589cc3eef117aa391be9733576
--- /dev/null
+++ b/db.py
@@ -0,0 +1,102 @@
+from server import *
+import sqlite3
+import re
+
+if config['DB_ENGINE'] == 'sqlite':
+	created = not os.path.exists(config['SQLITE_DB'])
+	db = sqlite3.connect(config['SQLITE_DB'])
+	cur = db.cursor()
+	if config['SQLITE_INIT_SCHEMA']:
+		cur.executescript(open(config['DB_SCHEMA']).read())
+	if config['SQLITE_INIT_DATA'] and created:
+		cur.executescript(open(config['DB_DATA']).read())
+	db.commit()
+	db.close()
+
+# Row wrapper for sqlite
+def dict_factory(cursor, row):
+	d = {}
+	for idx, col in enumerate(cursor.description):
+		if type(row[idx]) == str:
+			d[col[0].split('.')[-1]] = row[idx].replace('\\n','\n').replace('\\r','\r')
+		else:
+			d[col[0].split('.')[-1]] = row[idx]
+	return d
+
+def query(operation, *params):
+	if config['DB_ENGINE'] == 'mysql':
+		import mysql.connector
+		if 'db' not in g or not g.db.is_connected():
+			g.db = mysql.connector.connect(user=config['MYSQL_USER'], password=config['MYSQL_PASSWD'], host=config['MYSQL_HOST'], database=config['MYSQL_DB'])
+		if not hasattr(request, 'db'):
+			request.db = g.db.cursor(dictionary=True)
+		request.db.execute(operation.replace('?', '%s'), params)
+	elif config['DB_ENGINE'] == 'sqlite':
+		if 'db' not in g:
+			g.db = sqlite3.connect(config['SQLITE_DB'])
+			g.db.row_factory = dict_factory
+			g.db.isolation_level = None
+		if not hasattr(request, 'db'):
+			request.db = g.db.cursor()
+		request.db.execute(operation, params)
+	else:
+		return []
+	return request.db.fetchall()
+
+@app.teardown_request
+def commit_db(*args):
+	if hasattr(request, 'db'):
+		request.db.close()
+		g.db.commit()
+
+def searchquery(text, columns, match, tables, suffix, *suffixparams):
+	params = []
+	subexprs = []
+	words = text.split(' ')
+	prio = len(words)+1
+	for word in words:
+		if word == '' or word.isspace():
+			continue
+		matchexpr = ' OR '.join(['%s LIKE ?'%column for column in match])
+		subexprs.append('SELECT %s, %s AS _prio FROM %s WHERE %s'%(columns, str(prio), tables, matchexpr))
+		params += ['%'+word+'%']*len(match)
+		prio -= 1
+	if subexprs == []:
+		return []
+	expr = 'SELECT *,SUM(_prio) AS _score FROM (%s) AS _tmp %s'%(' UNION '.join(subexprs), suffix)
+	return query(expr, *params, *suffixparams)
+
+LDAP_USERRE = re.compile(r'[^a-z0-9]')
+notldap = {
+	'videoag':('videoag', ['users','videoag'], {'uid': 'videoag', 'givenName': 'Video', 'sn': 'Geier'}),
+	'gustav':('passwort', ['users'], {'uid': 'gustav', 'givenName': 'Gustav', 'sn': 'Geier'})
+}
+
+def ldapauth(user, password):
+	user = LDAP_USERRE.sub(r'', user.lower())
+	if 'LDAP_HOST' in config:
+		import ldap3
+		try:
+			conn = ldap3.Connection(config['LDAP_HOST'], 'uid=%s,ou=users,dc=fsmpi,dc=rwth-aachen,dc=de'%user, password, auto_bind=True)
+			if conn.search("ou=groups,dc=fsmpi,dc=rwth-aachen,dc=de", "(&(cn=*)(memberUid=%s))"%user, attributes=['cn']):
+				groups = [e.cn.value for e in conn.entries]
+			conn.unbind()
+			return user, groups
+		except ldap3.core.exceptions.LDAPBindError:
+			pass
+	elif config.get('DEBUG') and user in notldap and password == notldap[user][0]:
+		return user, notldap[user][1]
+	return None, []
+
+def ldapget(user):
+	user = LDAP_USERRE.sub(r'', user.lower())
+	if 'LDAP_HOST' in config:
+		import ldap3
+		conn = ldap3.Connection('ldaps://rumo.fsmpi.rwth-aachen.de', auto_bind=True)
+		conn.search("ou=users,dc=fsmpi,dc=rwth-aachen,dc=de", "(uid=%s)"%user,
+				attributes=ldap3.ALL_ATTRIBUTES)
+		e = conn.entries[0]
+		return {'uid': user, 'givenName': e.givenName.value, 'sn':e.sn.value}
+	else:
+		return notldap[user][2]
+
diff --git a/run.py b/run.py
new file mode 100644
index 0000000000000000000000000000000000000000..c49f8a2c367ffda696e4ac77f5de08146c68b7eb
--- /dev/null
+++ b/run.py
@@ -0,0 +1,4 @@
+from server import *
+
+if __name__ == '__main__':
+  app.run(threaded=True)
diff --git a/server.py b/server.py
index 8138ac56e92f9f45d055ce96e643d03cc991a591..5f8326cefd0701462f1487376f482f4a6c43dd2b 100755
--- a/server.py
+++ b/server.py
@@ -23,6 +23,7 @@ if __name__ == '__main__':
 	config['DEBUG'] = True
 config.from_pyfile('config.py', silent=True)
 app.jinja_env.globals['videoprefix'] = config['VIDEOPREFIX']
+mod_endpoints = []
 
 if config['DB_ENGINE'] == 'sqlite':
 	created = not os.path.exists(config['SQLITE_DB'])
@@ -128,6 +129,7 @@ def ismod(*args):
 app.jinja_env.globals['ismod'] = ismod
 
 def mod_required(func):
+	mod_endpoints.append(func.__name__)
 	@wraps(func)
 	def decorator(*args, **kwargs):
 		if not ismod():
@@ -135,9 +137,20 @@ def mod_required(func):
 			return redirect(url_for('login', ref=request.url))
 		else:
 			return func(*args, **kwargs)
+	print(decorator.__name__)
 	return decorator
 
+app.jinja_env.globals['navbar'] = []
+def register_navbar(name, icon=None):
+	def wrapper(func):
+		endpoint = func.__name__
+		app.jinja_env.globals['navbar'].append((endpoint, name, icon,
+					not endpoint in mod_endpoints))
+		return func
+	return wrapper
+
 @app.route('/')
+@register_navbar('Home', icon='home')
 def index():
 	return render_template('index.html', latestvideos=query('''
 				SELECT lectures.*, max(videos.time_updated) AS lastvidtime, courses.short, courses.downloadable, courses.title AS coursetitle
@@ -151,6 +164,7 @@ def index():
 			''', ismod()))
 
 @app.route('/videos')
+@register_navbar('Videos', icon='film')
 def videos():
 	courses = query('SELECT * FROM courses WHERE (? OR (visible AND listed))', ismod())
 	for course in courses:
@@ -162,6 +176,7 @@ def videos():
 	return render_template('videos.html', courses=courses, groupedby=groupedby)
 
 @app.route('/faq')
+@register_navbar('FAQ', icon='question-sign')
 def faq():
 	return render_template('faq.html')
 
@@ -288,6 +303,8 @@ def auth(): # For use with nginx auth_request
 
 
 @app.route('/schedule')
+@register_navbar('Drehplan', 'calendar')
+@mod_required
 def schedule():
 	start = date.today() - timedelta(days=date.today().weekday()+7*20)
 	days = [{'date': start, 'lectures': [], 'atonce':0 }]
diff --git a/templates/base.html b/templates/base.html
index 54bc736f0e18fc9523b94771c9ad48428736aa36..7a3abd5c36ebf59a57d4eef3f66786e202372e17 100644
--- a/templates/base.html
+++ b/templates/base.html
@@ -1,11 +1,3 @@
-{% set navigation_bar = [
-('/', 'index', 'Home', 'home', false),
-('/videos', 'videos', 'Videos', 'film', false),
-('/faq', 'faq', 'FAQ', 'question-sign', false),
-('/schedule', 'schedule', 'Drehplan', 'calendar', true)
-] -%}
-{% set active_page = active_page|default('none') -%}
-
 {% set page_border = page_border|default(1) -%}
 
 <!DOCTYPE html>
@@ -54,11 +46,11 @@
 						<div class="row">
 							<div class="col-xs-12 col-sm-8">
 								<ul class="nav nav-pills">
-									{% for href, id, caption, gly, modonly in navigation_bar %}
-									{% if (not modonly) or (modonly and ismod()) %}
-										 <li{% if id == active_page %} class="active"{% endif %}>
-											 <a href="{{ href|e }}">{% if gly != '' %}<span class="glyphicon glyphicon-{{ gly }}"></span> {% endif %}{{ caption }}</a>
-										</li>
+									{% for endpoint, caption, gly, visible in navbar %}
+									{% if visible or ismod() %}
+									 <li{% if endpoint == request.endpoint %} class="active"{% endif %}>
+										 <a href="{{ url_for(endpoint)|e }}">{% if gly != '' %}<span class="glyphicon glyphicon-{{ gly }}"></span> {% endif %}{{ caption }}</a>
+									</li>
 									{% endif %}
 									{% endfor %}
 									<li class="navbar-right">
diff --git a/templates/faq.html b/templates/faq.html
index 737585aead498893a878a5be8e17060290258425..29519ca87fa48d5038d48af279539c32a38ee4bd 100644
--- a/templates/faq.html
+++ b/templates/faq.html
@@ -1,6 +1,5 @@
 {% from 'macros.html' import preview %}
 {% extends "base.html" %}
-{% set active_page = "faq" %}
 {% block content %}
 <div class="alert alert-warning alert-dismissible" role="alert" id="kontakt">
 	Unter <a href="mailto:video@fsmpi.rwth-aachen.de">video@fsmpi.rwth-aachen.de</a> stehen wir für alle Fragen bereit.
diff --git a/templates/index.html b/templates/index.html
index 32134c5b18e6380c8dbd0ab5c05d3c9c04a382ce..6ad3b970f053ecfb6b719257d8f2cdf56c109eec 100644
--- a/templates/index.html
+++ b/templates/index.html
@@ -1,6 +1,5 @@
 {% from 'macros.html' import preview %}
 {% extends "base.html" %}
-{% set active_page = "index" %}
 {% set page_border = 0 %}
 {% block content %}
 <div class="row">
diff --git a/templates/videos.html b/templates/videos.html
index 2c03083c7cc9ef96df3ba03483a3725babdfe288..5be235c5c7c368eec17d0c4d39f53285c1905218 100644
--- a/templates/videos.html
+++ b/templates/videos.html
@@ -1,6 +1,4 @@
 {% from 'macros.html' import course_list_item %}
-{% set active_page = 'videos' -%}
-
 {% extends "base.html" %}
 {% block content %}
 <div class="row">