diff --git a/cutprogress.py b/cutprogress.py
index a187fb7136d53ad7ede2cfd2b07dee83b19280a7..3e94a8d7bf8e4080150977a464ec621cf91cd515 100644
--- a/cutprogress.py
+++ b/cutprogress.py
@@ -1,14 +1,10 @@
 from server import *
 from datetime import time
 
-@register_navbar('Schnittfortschritt User', icon='spinner', iconlib='fa', userendpoint=True)
-@app.route('/internal/user/<int:user>/cutprogress')
-@mod_required
-def cutprogress_user(user):
-	return cutprogress(user)
-
+@register_navbar('Schnittfortschritt User', icon='spinner', iconlib='fa', userendpoint=True, endpoint='cutprogress_user')
 @register_navbar('Schnittfortschritt', icon='spinner', iconlib='fa')
 @app.route('/internal/cutprogress', endpoint="cutprogress")
+@app.route('/internal/user/<int:user>/cutprogress', endpoint='cutprogress_user')
 @mod_required
 def cutprogress(user=None):
 	allsemester = query('SELECT DISTINCT semester from courses ORDER BY semester DESC');
diff --git a/edit.py b/edit.py
index 0943c475ee72bf22ee055477fb899bab115ba268..33df20b8d7a0fd5e588d3eda375b24f5e57aa674 100644
--- a/edit.py
+++ b/edit.py
@@ -192,7 +192,7 @@ def create(table):
 	return str(id), 200
 
 @app.route('/internal/changelog')
-@register_navbar('Changelog', icon='book')
+@register_navbar('Changelog', icon='book', group='weitere')
 @mod_required
 def changelog():
 	if 'page' in request.args:
diff --git a/jobs.py b/jobs.py
index 1168e3331f49842c3f19a956232b70c0567ec138..a410e4a152d06d0e5d3514c4d32a5f8fd01e8130 100644
--- a/jobs.py
+++ b/jobs.py
@@ -33,7 +33,7 @@ def restart_job(job_id, canceled=False):
 		modify('UPDATE jobs SET state = "ready" WHERE id = ? AND state = "failed" AND NOT canceled', job_id)
 
 @app.route('/internal/jobs/overview')
-@register_navbar('Jobs', iconlib='fa', icon='suitcase')
+@register_navbar('Jobs', iconlib='fa', icon='suitcase', group='weitere')
 @mod_required
 def jobs_overview():
 	if 'page' in request.args:
diff --git a/server.py b/server.py
index 282c75ebdb970290d3e68320645d41cd71612808..e484976657a9d9980653c821692dc946f3498202 100644
--- a/server.py
+++ b/server.py
@@ -75,26 +75,14 @@ def evalperm(perms):
 		return cperms
 	return [{'type': 'public'}]
 
-app.jinja_env.globals['navbar'] = []
-# iconlib can be 'bootstrap'
-# ( see: http://getbootstrap.com/components/#glyphicons )
-# or 'fa'
-# ( see: http://fontawesome.io/icons/ )
-def register_navbar(name, iconlib='bootstrap', icon=None, userendpoint=False):
-	def wrapper(func):
-		endpoint = func.__name__
-		app.jinja_env.globals['navbar'].append((endpoint, name, iconlib, icon, not endpoint in mod_endpoints, userendpoint))
-		return func
-	return wrapper
 
 from db import query, modify, show, searchquery
+from template_helper import *
 from mail import notify_mods, notify_admins
 from ldap import ldapauth
 from legacy import legacy_index
 from scheduler import sched_func
 
-from template_helper import *
-
 def render_endpoint(endpoint, flashtext=None, **kargs):
 	if flashtext:
 		flash(flashtext)
@@ -458,7 +446,7 @@ def sitemap():
 	return Response(render_template('sitemap.xml', pages=pages), 200, {'Content-Type': 'application/atom+xml'} )
 
 @app.route('/internal/dbstatus')
-@register_navbar('DB-Status', icon='ok')
+@register_navbar('DB-Status', icon='ok', group='weitere')
 @mod_required
 def dbstatus():
 	hosts = set()
diff --git a/sorter.py b/sorter.py
index ffac660fe0f6b9c42a434f29bd96ab0d0993df0b..d52016c84f956ed32ddfc9e519f7cd2971d90b8f 100644
--- a/sorter.py
+++ b/sorter.py
@@ -3,7 +3,7 @@ import traceback
 import os.path
 
 @app.route('/internal/sort/log')
-@register_navbar('Sortierlog', icon='sort-by-attributes-alt')
+@register_navbar('Sortierlog', icon='sort-by-attributes-alt', group='weitere')
 @mod_required
 def sort_log():
 	return render_template('sortlog.html',sortlog=query('''
diff --git a/template_helper.py b/template_helper.py
index cfe252be18a56cf0a1f11b47dbc6825912f6d18e..26be0e7215fc5b849558187d656f4ce4a3fd78e8 100644
--- a/template_helper.py
+++ b/template_helper.py
@@ -20,8 +20,29 @@ app.jinja_env.globals['gitversion'] = {'hash': output[1], 'longhash': output[0],
 def ismod(*args):
 	return ('user' in session)
 
-csrf_endpoints = []
+app.jinja_env.globals['navbar'] = []
+# iconlib can be 'bootstrap'
+# ( see: http://getbootstrap.com/components/#glyphicons )
+# or 'fa'
+# ( see: http://fontawesome.io/icons/ )
+def register_navbar(name, iconlib='bootstrap', icon=None, userendpoint=False, group=None, endpoint=None):
+	def wrapper(func):
+		urlendpoint = endpoint
+		if not endpoint:
+			urlendpoint = func.__name__
+		item = {}
+		item['iconlib'] = iconlib
+		item['icon'] = icon
+		item['userendpoint'] = userendpoint
+		item['group'] = group
+		item['endpoint'] = urlendpoint
+		item['visible'] = not urlendpoint in mod_endpoints
+		item['name'] = name
+		app.jinja_env.globals['navbar'].append(item)
+		return func
+	return wrapper
 
+csrf_endpoints = []
 def csrf_protect(func):
 	csrf_endpoints.append(func.__name__)
 	@wraps(func)
diff --git a/templates/base.html b/templates/base.html
index 7a72d98ad3d763a1104b327d45093ad8ec9f2dc7..8e2e8c8abe95552175ead90c9ff7fb5c6086c0c6 100644
--- a/templates/base.html
+++ b/templates/base.html
@@ -8,7 +8,7 @@
 <html lang="de">
 	<head>
 		{% block header %}
-		<title>Video AG {% block title %}{% for endpoint, caption, iconlib, gly, visible, userendpoint in navbar if ((visible or ismod()) and (endpoint == request.endpoint)) %}- {{ caption }}{% else%}{% endfor %}{% endblock %}</title>
+		<title>Video AG {% block title %}{% for n in navbar if ((n.visible or ismod()) and (endpoint == request.endpoint)) %}- {{ n.name }}{% endfor %}{% endblock %}</title>
 		<meta http-equiv="X-UA-Compatible" content="IE=edge">
 		<link rel="icon" type="image/png" href="{{url_for('static', filename='favicon.png')}}">
 		<meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
@@ -43,6 +43,20 @@
 	</head>
 	<body>
 	{% block navbar %}
+		{% macro navbaricon(data, user=none) -%}
+			<li{% if data.endpoint == request.endpoint %} class="active"{% endif %}>
+				<a href="{{ url_for(data.endpoint, user=user) }}" style="padding: 10px 6px;">
+					{% if data.gly != '' %}
+						{% if data.iconlib == 'bootstrap' %}
+							<span aria-hidden="true" class="glyphicon glyphicon-{{ data.icon }}"></span>
+						{% elif data.iconlib == 'fa' %}
+							<span aria-hidden="true" class="fa fa-{{ data.icon }}"></span>
+						{% endif %}
+					{{ data.name }}
+					{% endif %}
+				</a>
+			</li>
+		{%- endmacro %}
 		<nav class="hidden-print navbar navbar-default navbar-static-top" {% if config.DEBUG %} style="background-color: red" {% endif %} >
 			<div class="container-fluid">
 				<div class="navbar-header">
@@ -56,39 +70,27 @@
 						<img alt="Brand" src="{{url_for('static', filename='logo.png')}}" style="height: 44px; width: 44px" >
 					</a>
 					<ul class="nav nav-pills" style="margin-top: 5px; padding-left: 40px;">
-						{% for endpoint, caption, iconlib, gly, visible, userendpoint in navbar if visible and (not userendpoint) %}
-							<li{% if endpoint == request.endpoint %} class="active"{% endif %}>
-								<a href="{{ url_for(endpoint) }}" style="padding: 10px 6px;">
-									{% if gly != '' %}
-										{% if iconlib == 'bootstrap' %}
-											<span aria-hidden="true" class="glyphicon glyphicon-{{ gly }}"></span> 
-										{% elif iconlib == 'fa' %}
-											<span aria-hidden="true" class="fa fa-{{ gly }}"></span> 
-										{% endif %}
-									{{ caption }}
-									{% endif %}
-								</a>
-							</li>
+						{% for n in navbar if n.visible and (not n.userendpoint) %}
+							{{ navbaricon(n) }}
 						{% endfor %}
 					</ul>
 				</div>
 				<div class="collapse navbar-collapse" style="overflow-y: inherit">
 					<ul class="nav nav-pills" style="margin-top: 5px;">
-						{% for endpoint, caption, iconlib, gly, visible, userendpoint in navbar if (not visible) and ismod() and (not userendpoint) %}
-							<li{% if endpoint == request.endpoint %} class="active"{% endif %}>
-								<a href="{{ url_for(endpoint) }}" style="padding: 10px 6px;">
-									{% if gly != '' %}
-										{% if iconlib == 'bootstrap' %}
-											<span aria-hidden="true" class="glyphicon glyphicon-{{ gly }}"></span> 
-										{% elif iconlib == 'fa' %}
-											<span aria-hidden="true" class="fa fa-{{ gly }}"></span> 
-										{% endif %}
-									{{ caption }}
-									{% endif %}
-								</a>
-							</li>
+						{% for n in navbar if (not n.visible) and ismod() and (not n.userendpoint) and (not n.group) %}
+							{{ navbaricon(n) }}
 						{% endfor %}
 
+						{% for grouper, list in navbar|rejectattr("group", "none")|groupby("group") %}
+							<li{% if request.endpoint in list|map(attribute='endpoint') %} class="active dropdown"{% endif %}>
+								<a data-toggle="dropdown" data-boundary="viewport" class="dropdown-toggle" style="padding: 10px 6px;">{{ grouper }}<span class="caret"></span></a>
+								<ul class="dropdown-menu">
+									{% for n in list %}
+										{{ navbaricon(n) }}
+									{% endfor %}
+								</ul>
+							</li>
+						{% endfor %}
 						<li class="col-xs-9 col-sm-4 pull-right">
 							<form action="{{ url_for('search') }}" role="search">
 								<div class="input-group" style="margin-top: 3px">
@@ -116,19 +118,8 @@
 							{% else %}
 								<button data-toggle="dropdown" data-boundary="viewport" class="btn dropdown-toggle" style="padding: 10px 6px;">{{ session.user.givenName }} <span class="caret"></span></button>
 								<ul class="dropdown-menu">
-									{% for endpoint, caption, iconlib, gly, visible, userendpoint in navbar if userendpoint %}
-										<li{% if endpoint == request.endpoint %} class="active"{% endif %}>
-											<a href="{{ url_for(endpoint, user=session.user.dbid) }}">
-												{% if gly != '' %}
-													{% if iconlib == 'bootstrap' %}
-														<span aria-hidden="true" class="glyphicon glyphicon-{{ gly }}"></span> 
-													{% elif iconlib == 'fa' %}
-														<span aria-hidden="true" class="fa fa-{{ gly }}"></span> 
-													{% endif %}
-												{{ caption }}
-												{% endif %}
-											</a>
-										</li>
+									{% for n in navbar if n.userendpoint %}
+										{{ navbaricon(n, user=session.user.dbid) }}
 									{% endfor %}
 									<li class="divider"></li>
 									<li><a href="{{url_for('logout', ref=request.url)}}">Logout</a></li>
diff --git a/timetable.py b/timetable.py
index 2d93dca541ce5a883ed89738b4965e0ed8567124..73d94bcf435215afb7104125ee6810874c8efeb9 100644
--- a/timetable.py
+++ b/timetable.py
@@ -1,14 +1,10 @@
 from server import *
 from datetime import time
 
-@app.route('/internal/user/<int:user>/timetable')
-@register_navbar('personalisierter Drehplan', icon='calendar', userendpoint=True)
-@mod_required
-def timetable_user(user):
-	return timetable(user)
-
-@app.route('/internal/timetable')
+@register_navbar('personalisierter Drehplan', icon='calendar', userendpoint=True, endpoint='timetable_user')
 @register_navbar('Drehplan', icon='calendar')
+@app.route('/internal/timetable')
+@app.route('/internal/user/<int:user>/timetable', endpoint='timetable_user')
 @mod_required
 def timetable(user=None):
 	if 'kw' not in request.args: