diff --git a/db_example.sql b/db_example.sql
index 3c6754b52c22cf065ff3648f41b334a77557b49a..659dd748e9d609e8d28ce30a0debbf1002eeed1e 100644
--- a/db_example.sql
+++ b/db_example.sql
@@ -14642,10 +14642,12 @@ INSERT INTO "videos_data" VALUES(9680,7011,1,0,1,'','','','pub/16ss-dsal/16ss-ds
 INSERT INTO "videos_data" VALUES(9681,7012,1,0,1,'','','','pub/16ss-dsal/16ss-dsal-160715-1080p_1.mp4','2016-08-07 22:54:46','2016-08-07 21:02:37','2016-08-07 21:02:43',46,1402602183,4,'e036f7cbd51afd3ab7be10cf77747c00');
 INSERT INTO "videos_data" VALUES(9682,7012,1,0,1,'','','','pub/16ss-dsal/16ss-dsal-160715-360p_1.mp4','2016-08-07 22:45:34','2016-08-07 21:02:38','2016-08-07 21:02:45',46,368611109,10,'fae2bda2da55a3005aa6329a2d0227c3');
 INSERT INTO "videos_data" VALUES(9683,7012,1,0,1,'','','','pub/16ss-dsal/16ss-dsal-160715-720p_1.mp4','2016-08-07 22:46:00','2016-08-07 21:02:40','2016-08-07 21:02:44',46,721141077,5,'083c0b7693c82078c513707d1402096b');
+INSERT INTO "featured" VALUES(1,'Video AG','<p>Wir machen Vorlesungsvideos, damit du dir deine Vorlesungen angucken kannst, wann, wo und so oft <strong>du</strong> willst ;)</p><p><strong>Probleme?</strong><a href="/faq"> Hier gehts zur FAQ</a></p><p>Wenn du die Videos nützlich fandest, schreib doch bitte den Dozenten eine kurze E-Mail. Waren die Videos grauenhaft? Kritik an uns.</p><p>Wenn du mitmachen willst, Fragen oder Anregungen hast, oder nur mal schauen möchtest, komm zu unserem AG-Treffen oder schreib uns eine E-Mail. Insbesondere freuen wir uns über Studis der Mathematik und Physik, die ihre Vorlesungen filmen wollen.</p>','','plain','','',0,1,0,'2017-04-09 19:00:00','2017-04-09 19:00:00',0);
 INSERT INTO "sqlite_sequence" VALUES('changelog',17859);
 INSERT INTO "sqlite_sequence" VALUES('courses_data',303);
 INSERT INTO "sqlite_sequence" VALUES('formats',30);
 INSERT INTO "sqlite_sequence" VALUES('lectures_data',7362);
 INSERT INTO "sqlite_sequence" VALUES('users',77);
 INSERT INTO "sqlite_sequence" VALUES('videos_data',9683);
+INSERT INTO "sqlite_sequence" VALUES('featured',1);
 COMMIT;
diff --git a/db_schema.sql b/db_schema.sql
index 2f2a6eac0f64fe289ae8563e815166492afbb19b..2dc1f8c376208f0e0a152ab0f7d41be9f29699eb 100644
--- a/db_schema.sql
+++ b/db_schema.sql
@@ -126,7 +126,6 @@ CREATE TABLE IF NOT EXISTS `site_texts` (
   `modified_by` text NOT NULL
 );
 CREATE TABLE IF NOT EXISTS `log` (
-	CREATE TABLE IF NOT EXISTS `streamlog` (
 	`id` INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
 	`time` datetime NOT NULL,
 	`date` datetime NOT NULL,
@@ -221,6 +220,10 @@ CREATE TABLE IF NOT EXISTS `featured` (
 	`title` text NOT NULL DEFAULT '',
 	`text` text NOT NULL DEFAULT '',
 	`internal` text NOT NULL DEFAULT '',
+	`type` varchar(32) NOT NULL DEFAULT '',
+	`param` text NOT NULL DEFAULT '',
+	`param2` text NOT NULL DEFAULT '',
+	`order` INTEGER DEFAULT NULL,
 	`visible` INTEGER NOT NULL DEFAULT 0,
 	`deleted` INTEGER NOT NULL DEFAULT 0,
   `time_created` datetime NOT NULL,
@@ -269,4 +272,6 @@ CREATE VIEW IF NOT EXISTS `courses` AS select * from `courses_data` where (not(`
 CREATE VIEW IF NOT EXISTS `lectures` AS select `lectures_data`.* from `lectures_data` join `courses_data` on (`courses_data`.`id` = `course_id`) where (not(`lectures_data`.`deleted` or `courses_data`.`deleted`));
 CREATE VIEW IF NOT EXISTS `videos` AS select `videos_data`.* from `videos_data` join `lectures_data` on (`lectures_data`.`id` = `lecture_id`) join `courses_data` on (`courses_data`.`id` = `course_id`) where (not(`videos_data`.`deleted` or `lectures_data`.`deleted` or `courses_data`.`deleted`));
 CREATE VIEW IF NOT EXISTS `sorterrorlog` AS select * from `sorterrorlog_data` where (not(`sorterrorlog_data`.`deleted`));
+CREATE TRIGGER IF NOT EXISTS featured_unique_order AFTER INSERT ON featured FOR EACH ROW BEGIN UPDATE featured SET `order` = (SELECT MAX(`order`) FROM featured)+1 WHERE id = NEW.id; END;
+CREATE TRIGGER IF NOT EXISTS courses_unique_handle BEFORE UPDATE OF handle ON courses_data FOR EACH ROW WHEN NEW.handle IN (SELECT handle FROM courses WHERE NOT deleted) BEGIN SELECT RAISE(ROLLBACK, "Handle bereits in Verwendung"); END;
 COMMIT;
diff --git a/feeds.py b/feeds.py
index 7b7767d3cefc9f7bca631825a5b3b78662b58ffd..702c57553986b66cb46bcd64a5f8e07ea9cbbde5 100644
--- a/feeds.py
+++ b/feeds.py
@@ -9,7 +9,7 @@ def fixdate(d):
 	return d
 
 @app.route('/feed')
-@app.route('/course/<handle>/feed')
+@app.route('/<handle>/feed')
 @handle_errors(None, 'Diese Veranstaltung existiert nicht!', 400, IndexError)
 def feed(handle=None):
 	id = None
@@ -40,7 +40,7 @@ def feed(handle=None):
 	course['updated'] = updated
 	return Response(render_template('feed.atom', course=course, entries=entries), 200, {'Content-Type': 'application/atom+xml'})
 
-@app.route('/course/feed')
+@app.route('/courses/feed')
 def courses_feed():
 	courses = query('SELECT * FROM courses WHERE visible AND listed ORDER BY time_created DESC LIMIT 100')
 	atomid = gen_atomid('Video AG, courses')
diff --git a/importer.py b/importer.py
index ded1e9689dec21c06a3ee3b1520bc12643d2f67a..b3d3bd3ecb5004351055669ab3733e44adb3e2f0 100644
--- a/importer.py
+++ b/importer.py
@@ -1,6 +1,6 @@
 from server import *
 
-@app.route('/import/<int:id>', methods=['GET', 'POST'])
+@app.route('/internal/import/<int:id>', methods=['GET', 'POST'])
 @mod_required
 def list_import_sources(id):
 	courses = query('SELECT * FROM courses WHERE id = ?', id)[0]
@@ -25,7 +25,7 @@ def list_import_sources(id):
 
 	return render_template('import_campus.html', course=courses, import_campus=import_campus, events=[])
 
-@app.route('/import/<int:id>/now', methods=['GET', 'POST'])
+@app.route('/internal/import/<int:id>/now', methods=['GET', 'POST'])
 @mod_required
 def import_from(id):
 
diff --git a/jobs.py b/jobs.py
index 41d0c4651e8918c6847891ceb0a104ce56f603d9..38beea101d3bbcdbf15590327560161d51d1e2f5 100644
--- a/jobs.py
+++ b/jobs.py
@@ -3,7 +3,7 @@ import traceback
 import json
 import random
 
-@app.route('/jobs/overview')
+@app.route('/internal/jobs/overview')
 @register_navbar('Jobs', iconlib='fa',  icon='suitcase')
 @mod_required
 def jobs_overview():
@@ -47,13 +47,13 @@ def jobs_catch_broken():
 	except:
 		pass
 
-@app.route('/jobs/api/worker/<hostname>/ping', methods=['GET', 'POST'])
+@app.route('/internal/jobs/api/worker/<hostname>/ping', methods=['GET', 'POST'])
 @jobs_api_token_required
 def jobs_worker_ping(hostname):
 	query('INSERT OR REPLACE INTO worker (hostname, last_ping) values (?, ?)', hostname, datetime.now())
 	return 'OK',200
 
-@app.route('/jobs/api/job/<int:id>/ping', methods=['GET', 'POST'])
+@app.route('/internal/jobs/api/job/<int:id>/ping', methods=['GET', 'POST'])
 @jobs_api_token_required
 def jobs_ping(id):
 	hostname = request.values['host']
@@ -65,7 +65,7 @@ def jobs_ping(id):
 		query('UPDATE jobs SET worker = ?, last_ping = ?, status = ?, state = ? where id = ?', hostname, datetime.now(), status, state, id)
 	return 'OK',200
 
-@app.route('/jobs/api/worker/<hostname>/schedule', methods=['POST'])
+@app.route('/internal/jobs/api/worker/<hostname>/schedule', methods=['POST'])
 @jobs_api_token_required
 def jobs_schedule(hostname):
 	hostdata =  request.get_json()
diff --git a/l2pauth.py b/l2pauth.py
index d658ee431b2273258571c0c4a4ec5e0c9e7782e8..3c971e74733e175fbb170811af10685c954b85d0 100644
--- a/l2pauth.py
+++ b/l2pauth.py
@@ -14,7 +14,7 @@ def oauthget(endpoint, **args):
 	r = requests.request('POST', OAUTH_BASE+endpoint, data=args)
 	return r.json()
 
-@app.route('/l2pauth')
+@app.route('/internal/l2pauth')
 def start_l2pauth():
 	if 'L2P_APIKEY' not in config:
 		return render_template("500.html"), 500
@@ -23,7 +23,7 @@ def start_l2pauth():
 	session['oauthscope'] = 'l2p'
 	return redirect(code['verification_url']+'?q=verify&d='+code['user_code'])
 
-@app.route('/rwthauth')
+@app.route('/internal/rwthauth')
 def start_rwthauth():
 	if 'L2P_APIKEY' not in config:
 		return render_template("500.html"), 500
diff --git a/server.py b/server.py
index 7ecee0c681763853e949ac06129d33cc0216aceb..fcdf465d2910b3d24e15eab7cc0f5f271514435a 100644
--- a/server.py
+++ b/server.py
@@ -299,7 +299,10 @@ def index():
 		return redirect(url_for('course', handle=request.args['course']),code=302)
 	if 'view' in request.args:
 		if (request.args['view'] == 'player') and ('lectureid' in request.args) :
-			return redirect(url_for('lecture', id=request.args['lectureid']),code=302)
+			courses = query('SELECT courses.handle FROM courses JOIN lectures ON courses.id = lectures.course_id WHERE lectures.id = ?', request.args['lectureid'])
+			if not courses:
+				return "Not found", 404
+			return redirect(url_for('lecture', course=courses[0]['handle'], id=request.args['lectureid']),code=302)
 
 	start = date.today() - timedelta(days=1)
 	end = start + timedelta(days=7)
@@ -326,10 +329,16 @@ def index():
 		JOIN courses ON courses.id = lectures.course_id
 		WHERE streams.active AND (? OR (streams.visible AND courses.visible AND courses.listed AND lectures.visible))
 		''', ismod())
-	featured = query('SELECT * FROM featured WHERE NOT deleted AND (? OR visible)', ismod())
+	featured = query('SELECT * FROM featured WHERE (? OR visible) ORDER BY `order`', ismod())
+	featured = list(filter(lambda x: not x['deleted'], featured))
+	for item in featured:
+		if item['type'] == 'courses':
+			if item['param'] not in ['title', 'semester', 'organizer', 'subject']:
+				continue
+			item['courses'] = query('SELECT * FROM courses WHERE (visible AND listed) AND `%s` = ? ORDER BY `%s`'%(item['param'], item['param']), item['param2'])
 	return render_template('index.html', latestvideos=livestreams+latestvideos, upcomming=upcomming, featured=featured)
 
-@app.route('/course')
+@app.route('/courses')
 @register_navbar('Videos', icon='film')
 def courses():
 	courses = query('SELECT * FROM courses WHERE (? OR (visible AND listed)) ORDER BY title', ismod())
@@ -349,8 +358,8 @@ def genlive(streams):
 		stream['file_size'] = 0
 	return streams
 
-@app.route('/course/<handle>')
-@app.route('/course/<int:id>')
+@app.route('/<handle>')
+@app.route('/<int:id>')
 @handle_errors('courses', 'Diese Veranstaltung existiert nicht!', 404, IndexError)
 def course(id=None, handle=None):
 	if id:
@@ -390,10 +399,12 @@ def course(id=None, handle=None):
 def faq():
 	return render_template('faq.html')
 
-@app.route('/play/<int:id>')
-@app.route('/embed/<int:id>', endpoint='embed')
+@app.route('/<course>/<int:id>')
+@app.route('/<int:courseid>/<int:id>')
+@app.route('/<course>/<int:id>/embed', endpoint='embed')
+@app.route('/<int:courseid>/<int:id>/embed', endpoint='embed')
 @handle_errors('course', 'Diese Vorlesung existiert nicht!', 404, IndexError)
-def lecture(id):
+def lecture(id, course=None, courseid=None):
 	lecture = query('SELECT * FROM lectures WHERE id = ? AND (? OR visible)', id, ismod())[0]
 	videos = query('''
 			SELECT videos.*, (videos.downloadable AND courses.downloadable) as downloadable, formats.description AS format_description, formats.player_prio, formats.prio, formats.mimetype
@@ -421,9 +432,12 @@ def lecture(id):
 	if not checkperm(perms):
 		mode, text = permdescr(perms)
 		if mode == 'rwth':
-			flash(text+'. <a target="_blank" href="'+url_for('start_rwthauth')+'">Hier authorisieren</a>.')
+			flash(text+'. <a target="_blank" class="reloadonclose" href="'+url_for('start_rwthauth')+'">Hier authorisieren</a>.')
 		elif mode == 'l2p':
-			flash(text+'. <a target="_blank" href="'+url_for('start_l2pauth')+'">Hier authorisieren</a>.')
+			if 'l2p_courses' in session:
+				flash(text+'. Du bist kein Teilnehmer des L2P-Kurses! <a target="_blank" class="reloadonclose" href="'+url_for('start_l2pauth')+'">Kurse aktualisieren</a>.')
+			else:
+				flash(text+'. <a target="_blank" class="reloadonclose" href="'+url_for('start_l2pauth')+'">Hier authorisieren</a>.')
 		else:
 			flash(text+'.')
 	return render_template('embed.html' if request.endpoint == 'embed' else 'lecture.html', course=courses[0], lecture=lecture, videos=videos, chapters=chapters)
@@ -454,7 +468,7 @@ def search():
 def check_mod(user, groups):
 	return user and 'users' in groups
 
-@app.route('/login', methods=['GET', 'POST'])
+@app.route('/internal/login', methods=['GET', 'POST'])
 def login():
 	if request.method == 'GET':
 		return render_template('login.html')
@@ -471,7 +485,7 @@ def login():
 	session['_csrf_token'] = ''.join(random.SystemRandom().choice(string.ascii_letters + string.digits) for _ in range(128))
 	return redirect(request.values.get('ref', url_for('index')))
 
-@app.route('/logout', methods=['GET', 'POST'])
+@app.route('/internal/logout', methods=['GET', 'POST'])
 def logout():
 	session.pop('user')
 	return redirect(request.values.get('ref', url_for('index')))
@@ -492,15 +506,15 @@ tabs = {
 	'announcements': ('announcements', 'id', ['text', 'level', 'visible',
 			'deleted', 'time_publish', 'time_expire'],
 			['created_by', 'time_created', 'time_updated']),
-	'featured': ('featured', 'id', ['title', 'text', 'internal', 'visible', 'deleted'],
-			['created_by', 'time_created', 'time_updated']),
+	'featured': ('featured', 'id', ['title', 'text', 'internal', 'visible', 'deleted', 'param', 'param2', 'order'],
+			['created_by', 'time_created', 'time_updated', 'type']),
 	'perm': ('perm', 'id', ['type', 'param1', 'param2', 'deleted'],
 			['course_id', 'lecture_id', 'video_id', 'created_by', 'time_created', 'time_updated']),
 	'sorterrorlog': ('sorterrorlog_data', 'id', ['deleted'],
 			['time_created', 'time_updated'])
 }
 
-@app.route('/edit', methods=['GET', 'POST'])
+@app.route('/internal/edit', methods=['GET', 'POST'])
 @mod_required
 @csrf_protect
 def edit(prefix='', ignore=[]):
@@ -510,7 +524,6 @@ def edit(prefix='', ignore=[]):
 	ignore.append('_csrf_token')
 	if not prefix and 'prefix' in request.args:
 		prefix = request.args['prefix']
-	modify('BEGIN')
 	changes = request.values.items()
 	if (request.method == 'POST') and (request.get_json()):
 		changes = request.get_json().items()
@@ -521,15 +534,14 @@ def edit(prefix='', ignore=[]):
 		table, id, column = key.split('.', 2)
 		assert table in tabs
 		assert column in tabs[table][2]
-		modify('INSERT INTO changelog (`table`,id_value, id_key, field, value_new, value_old, `when`, who, executed) VALUES (?,?,?,?,?,(SELECT %s FROM %s WHERE %s = ?),?,?,1)'%(column, tabs[table][0], tabs[table][1]),
+		modify('INSERT INTO changelog (`table`,id_value, id_key, field, value_new, value_old, `when`, who, executed) VALUES (?,?,?,?,?,(SELECT `%s` FROM %s WHERE %s = ?),?,?,1)'%(column, tabs[table][0], tabs[table][1]),
 				table, id, tabs[table][1], column, val, id, datetime.now(), session['user']['dbid'])
-		modify('UPDATE %s SET %s = ?, time_updated = ? WHERE %s = ?'%(tabs[table][0], column, tabs[table][1]), val, datetime.now(), id)
-	modify('COMMIT')
+		modify('UPDATE %s SET `%s` = ?, time_updated = ? WHERE `%s` = ?'%(tabs[table][0], column, tabs[table][1]), val, datetime.now(), id)
 	if 'ref' in request.values:
 		return redirect(request.values['ref'])
 	return "OK", 200
 
-@app.route('/new/<table>', methods=['GET', 'POST'])
+@app.route('/internal/new/<table>', methods=['GET', 'POST'])
 @mod_required
 @csrf_protect
 def create(table):
@@ -549,7 +561,7 @@ def create(table):
 			continue
 		assert column in tabs[table][2]+tabs[table][3]
 		assert column not in defaults
-		columns.append(column)
+		columns.append('`'+column+'`')
 		values.append(val)
 	id = modify('INSERT INTO %s (%s) VALUES (%s)'%(tabs[table][0],
 				','.join(columns), ','.join(['?']*len(values))), *values)
@@ -557,7 +569,7 @@ def create(table):
 		return redirect(request.values['ref'])
 	return str(id), 200
 
-@app.route('/auth')
+@app.route('/internal/auth')
 def auth(): # For use with nginx auth_request
 	if 'X-Original-Uri' not in request.headers:
 		return 'Internal Server Error', 500
@@ -618,7 +630,7 @@ def auth(): # For use with nginx auth_request
 		return Response("Login required", 401, {'WWW-Authenticate': 'Basic realm="Login Required"'})
 	return "Not allowed", 403
 
-@app.route('/changelog')
+@app.route('/internal/changelog')
 @register_navbar('Changelog', icon='book')
 @mod_required
 def changelog():
@@ -631,7 +643,7 @@ def changelog():
 def files(filename):
 	return redirect(config['VIDEOPREFIX']+'/'+filename)
 
-@app.route('/newchapter/<int:lectureid>', methods=['POST', 'GET'])
+@app.route('/internal/newchapter/<int:lectureid>', methods=['POST', 'GET'])
 def suggest_chapter(lectureid):
 	time = request.values['time']
 	text = request.values['text']
@@ -652,7 +664,7 @@ def suggest_chapter(lectureid):
 		return redirect(request.values['ref'])
 	return 'OK',  200
 
-@app.route('/chapters/<int:lectureid>')
+@app.route('/internal/chapters/<int:lectureid>')
 def chapters(lectureid):
 	chapters = query("SELECT * FROM chapters WHERE lecture_id = ? AND NOT deleted AND (visible OR ?) ORDER BY time DESC", lectureid, ismod())
 	if not chapters:
@@ -675,7 +687,7 @@ def sitemap():
 	for i in query('select * from courses where visible and listed'):
 		pages.append([url_for('course',handle=i['handle'])])
 		for j in query('select * from lectures where (course_id = ? and visible)',i['id']):
-			pages.append([url_for('lecture',id=j['id'])])
+			pages.append([url_for('lecture',course=i['handle'],id=j['id'])])
 
 
 	return Response(render_template('sitemap.xml', pages=pages), 200, {'Content-Type': 'application/atom+xml'} )
@@ -685,7 +697,10 @@ def sitemap():
 @app.route('/site/<string:phpfile>')
 def legacy(phpfile=None):
 	if phpfile=='embed.php' and ('lecture' in request.args):
-		return redirect(url_for('embed', id=request.args['lecture']),code=302)
+		courses = query('SELECT courses.handle FROM courses JOIN lectures ON courses.id = lectures.course_id WHERE lectures.id = ?', request.args['lecture'])
+		if not courses:
+			return render_endpoint('index', 'Diese Seite existiert nicht!'), 404
+		return redirect(url_for('embed', course=courses[0]['handle'], id=request.args['lecture']),code=302)
 	if phpfile=='feed.php' and ('all' in request.args):
 		return redirect(url_for('feed'),code=302)
 	if phpfile=='feed.php' and ('newcourses' in request.args):
diff --git a/sorter.py b/sorter.py
index 72c338e884e24a9634181b95bba648ef98f3567d..1e0f80be35c3046dace946cb0703c91b1716850e 100644
--- a/sorter.py
+++ b/sorter.py
@@ -1,7 +1,7 @@
 from server import *
 import traceback
 
-@app.route('/sort/log')
+@app.route('/internal/sort/log')
 @register_navbar('Sortierlog', icon='sort-by-attributes-alt')
 @mod_required
 def sort_log():
@@ -53,7 +53,7 @@ def schedule_thumbnail(lectureid):
 	data = '{"lectureid": "'+str(lectureid)+'", "path": "'+path+'"}'
 	query('INSERT INTO jobs (type, data, time_created) VALUES ("thumbnail", ?, ?)', data, datetime.now());
 
-@app.route('/sort/now')
+@app.route('/internal/sort/now')
 @mod_required
 @sched_func(600)
 def sort_now():
diff --git a/static/moderator.js b/static/moderator.js
index 858b5f57d4708a44272801546d04f6aa35c01b25..ca809f1220ccbe4883b891572af6f50b67df6a9d 100644
--- a/static/moderator.js
+++ b/static/moderator.js
@@ -13,7 +13,7 @@ var moderator = {
 			dict['_csrf_token'] = moderator.api.csrf_token;
 			$.ajax({
 				method: "POST",
-				url: "/edit",
+				url: "/internal/edit",
 				dataType: "text",
 				contentType: "application/json",
 				data: JSON.stringify(dict),
@@ -29,7 +29,7 @@ var moderator = {
 			value['_csrf_token'] = moderator.api.csrf_token;
 			$.ajax({
 				method: "POST",
-				url: "/new/"+type,
+				url: "/internal/new/"+type,
 				dataType: "text",
 				contentType: "application/json",
 				data: JSON.stringify(value),
diff --git a/stats.py b/stats.py
index b0be43cfef0fbdc5d07994ba0f0439018eafa3d7..8ef31660c07b1e13c41454b9ee5a716a23fbfa1c 100644
--- a/stats.py
+++ b/stats.py
@@ -3,7 +3,7 @@ import json
 from jobs import date_json_handler
 from hashlib import md5
 
-@app.route('/stats')
+@app.route('/internal/stats')
 @register_navbar('Statistiken', icon='stats')
 @mod_required
 def stats():
@@ -22,8 +22,8 @@ statsqueries['live_views'] = "SELECT hlslog.segment AS x, COUNT(DISTINCT hlslog.
 def plotly_date_handler(obj):
 	return obj.strftime("%Y-%m-%d %H:%M:%S")
 
-@app.route('/stats/generic/<req>')
-@app.route('/stats/generic/<req>/<param>')
+@app.route('/internal/stats/generic/<req>')
+@app.route('/internal/stats/generic/<req>/<param>')
 @mod_required
 def stats_generic(req, param=None):
 	if req not in statsqueries:
@@ -37,8 +37,8 @@ def stats_generic(req, param=None):
 			res[key].append(val)
 	return Response(json.dumps([res], default=plotly_date_handler),  mimetype='application/json')
 
-@app.route('/stats/viewsperday/<req>')
-@app.route('/stats/viewsperday/<req>/<param>')
+@app.route('/internal/stats/viewsperday/<req>')
+@app.route('/internal/stats/viewsperday/<req>/<param>')
 @mod_required
 def stats_viewsperday(req, param=""):
 	update_expr = 'INSERT INTO logcache (req, param, trace, date, value) SELECT "%s", ?, trace, date, y FROM (%s) AS cachetmp WHERE date < ?'
diff --git a/templates/500.html b/templates/500.html
index 0bbc3b2c648b23784d9522d35834dde8eaa81b91..e311fa800c9bf377babf7f6cce2480cf6f6200a5 100644
--- a/templates/500.html
+++ b/templates/500.html
@@ -6,10 +6,9 @@
 	</div>
 	<div class="row panel-body">
 		<div class="col-xs-12">
-			Es ist ein interner Fehler aufgetreten.
-			Eventuell tritt dieser nur vorübergehend auf, versuche es doch einfach in ein paar Minuten noch einmal.
-			Sollte das Problem länger bestehen, schreib uns bitte eine Mail an <a href="mailto:video@fsmpi.rwth-aachen.de">video@fsmpi.rwth-aachen.de</a>.
-			Wir werden uns dann so schnellst möglich darum kümmern.
+			<p>Es ist ein interner Fehler aufgetreten. Eventuell betrifft dieser nur einen Teil der Seite oder er tritt nur vorübergehend auf. Versuche es doch einfach in ein paar Minuten noch einmal.</p>
+			<p>Falls das Problem länger bestehen sollte, schreib uns bitte eine Mail an <a href="mailto:video@fsmpi.rwth-aachen.de">video@fsmpi.rwth-aachen.de</a> in der du uns die <b>Uhrzeit</b> und <b>aufgerufene Seite</b> nennst und, dass der Fehler auf Server <b>{{ gethostname() }}</b> aufgetreten ist.
+			Wir werden uns dann schnellst möglich darum kümmern.</p>
 		</div>
 	</div>
 </div>
diff --git a/templates/embed.html b/templates/embed.html
index 2c5a2ee6f7d88194cace46307fe2da39fbe62bb1..0871eced2af0004a6edbfd65b2d707322e68c03b 100644
--- a/templates/embed.html
+++ b/templates/embed.html
@@ -1,6 +1,4 @@
 {% from 'macros.html' import player %}
-{% from 'macros.html' import video_download_btn %}
-{% from 'macros.html' import video_embed_btn %}
 {% set page_border = 0 -%}
 
 {% extends "base.html" %}
diff --git a/templates/feed.atom b/templates/feed.atom
index 0144dd00dfd3b104c564b96db86fd8ba6779082d..e24ad9404aacaf705429d9015a8c51c01d15fa4b 100644
--- a/templates/feed.atom
+++ b/templates/feed.atom
@@ -53,7 +53,7 @@ Veranstalter: {{ course.organizer }}<br>
 			<name>{{ entry.speaker }}</name>
 		</author>
 		{% endif %}
-		<link rel="alternate" href="{{ url_for('lecture', id=entry.id, _external=True) }}"/>
+		<link rel="alternate" href="{{ url_for('lecture', course=entry.course.handle, id=entry.id, _external=True) }}"/>
 		<link rel="enclosure" href="{{ url_for('files', filename=entry.video.path, _external=True)}}" length="{{ entry.video.file_size }}"/>
 		<id>{{ entry.atomid }}</id>
 		<updated>{{ entry.updated|rfc3339 }}</updated>
diff --git a/templates/index.html b/templates/index.html
index cdfbfbe0a36471a9ae5ee8031ae01d4b34c2c2af..b036828b4659cd5a8acbc47c82d5dc4f8ef96417 100644
--- a/templates/index.html
+++ b/templates/index.html
@@ -1,4 +1,4 @@
-{% from 'macros.html' import preview %}
+{% from 'macros.html' import preview, featured_content %}
 {% extends "base.html" %}
 {% set page_border = 0 %}
 {% if ismod() %}
@@ -53,6 +53,16 @@
 <div class="row">
 	<div class="col-xs-12">
 		<ul class="list-inline pull-right">
+			<li style="padding-right: 0px;">
+				<div class="btn-group">
+					<button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">Neues Panel <span class="caret"></span></button>
+					<ul class="dropdown-menu">
+						<li><a href="{{ url_for('create', table='featured', title='Neues Panel', type='plain', ref=request.url) }}">Nur Text</a></li>
+						<li><a href="{{ url_for('create', table='featured', title='Neues Panel', type='image', ref=request.url) }}">Text mit Bild</a></li>
+						<li><a href="{{ url_for('create', table='featured', title='Neues Panel', type='courses', param='semester', param2='', ref=request.url) }}">Veranstaltungsliste</a></li>
+					</ul>
+				</div>
+			</li>
 			<li style="padding-right: 0px;">
 				<a class="btn btn-default" href="{{ url_for('create', table='announcements', text='Neue Ankündigung', time_publish=datetime.now().replace(hour=0, minute=0, second=0, microsecond=0), time_expire=datetime.now().replace(hour=0, minute=0, second=0, microsecond=0)+timedelta(days=7), ref=request.url) }}">Neue Ankündigung</a>
 			</li>
@@ -61,66 +71,32 @@
 </div>
 {% endif %}
 <div class="row">
-	<div class="col-md-6 panel-group">
+	<div class="col-md-6">
+		{% for item in featured %}
 		<div class="panel panel-default">
 			<div class="panel-heading">
-				<h1 class="panel-title">Video AG</h1>
-			</div>
-			<div class="panel-body">
-				<p>Wir machen Vorlesungsvideos, damit du dir deine Vorlesungen angucken kannst, wann, wo und so oft <strong>du</strong> willst ;)</p>
-				<p><strong>Probleme?</strong><a href="{{url_for('faq')}}"> Hier gehts zur FAQ</a></p>
-				<p>Wenn du die Videos nützlich fandest, schreib doch bitte den Dozenten eine kurze E-Mail. Waren die Videos grauenhaft? Kritik an uns.</p>
-				<p>Wenn du mitmachen willst, Fragen oder Anregungen hast, oder nur mal schauen möchtest, komm zu unserem AG-Treffen oder schreib uns eine E-Mail.
-				Insbesondere freuen wir uns über Studis der Mathematik und Physik, die ihre Vorlesungen filmen wollen.</p>
-				</p>
-			</div>
-		</div>
-		{% if (featured|length > 0) or ismod() %}
-			<div class="panel panel-default">
-				<div class="panel-heading">
-					<h1 class="panel-title">Featured
-						{% if ismod() %}
-							<a class="btn btn-default" href="{{ url_for('create', table='featured', title='Neuer Artikel', ref=request.url) }}">Neue Empfehlung</a>
+				<h1 class="panel-title">
+					{{ moderator_editor(('featured',item.id,'title'), item.title) }} {{ moderator_checkbox(('featured',item.id,'visible'), item.visible) }}
+					{% if ismod() %}
+					<div class="btn-group pull-right" role="group">
+						{% if not loop.first %}
+							<a class="btn btn-default" href="{{ url_for('edit', **{'featured.'+item.id|string+'.order': featured[loop.index0-1].order, 'featured.'+featured[loop.index0-1].id|string+'.order': item.order, 'ref': request.url})}}"><span class="glyphicon glyphicon-arrow-up"></span></a>
+						{% else %}
+							<button class="btn btn-default disabled"><span class="glyphicon glyphicon-arrow-up"></span></button>
 						{% endif %}
-					</h1>
-				</div>
-				<div class="panel-body">
-					<div id="myCarousel" class="carousel slide" data-ride="carousel" style="background-color: #EEE; min-height: 400px;" {% if ismod() %}data-interval="false"{% endif %}>
-						<ol class="carousel-indicators">
-							{% for i in featured %}
-							<li data-target="#myCarousel" data-slide-to="{{ loop.index0 }}" {% if loop.first %} class="active" {% endif %}></li>
-							{% endfor %}
-						</ol>
-							<div class="carousel-inner" role="listbox">
-						{% for i in featured %}
-								<div class="item {% if loop.first %}active{% endif %}">
-									{% if ismod() %}
-										<div class="center-block">
-											{{ moderator_editor(('featured',i.id,'text'), i.text) }}
-											{{ moderator_editor(('featured',i.id,'title'), i.title) }}
-											{{ moderator_checkbox(('featured',i.id,'visible'),i.visible) }}
-										</div>
-									{% else %}
-										{{i.text|safe}}
-									{% endif %}
-									<div class="carousel-caption">{{ i.title }}</div>
-								</div>
-						{% endfor %}
-							</div>
-							{% if not ismod() %}
-								<a class="left carousel-control" href="#myCarousel" role="button" data-slide="prev">
-									<span class="glyphicon glyphicon-chevron-left" aria-hidden="true"></span>
-									<span class="sr-only">Previous</span>
-								</a>
-								<a class="right carousel-control" href="#myCarousel" role="button" data-slide="next">
-									<span class="glyphicon glyphicon-chevron-right" aria-hidden="true"></span>
-									<span class="sr-only">Next</span>
-								</a>
-							{% endif %}
+						{% if not loop.last %}
+							<a class="btn btn-default" href="{{ url_for('edit', **{'featured.'+item.id|string+'.order': featured[loop.index0+1].order, 'featured.'+featured[loop.index0+1].id|string+'.order': item.order, 'ref': request.url})}}"><span class="glyphicon glyphicon-arrow-down"></span></a>
+						{% else %}
+							<button class="btn btn-default disabled"><span class="glyphicon glyphicon-arrow-down"></span></button>
+						{% endif %}
+						{{ moderator_delete(['featured',item.id,'deleted']) }}
 					</div>
-				</div>
+					{% endif %}
+				</h1>
 			</div>
-		{% endif %}
+			{{ featured_content(item) }}
+		</div>
+		{% endfor %}
 	</div>
 	<div class="col-md-6">
 		<div class="panel panel-default">
diff --git a/templates/lecture.html b/templates/lecture.html
index 4007263b47de998985e68dad49d5811c18618784..71c4b1246b492aa4f2428b77810d66cc55df6841 100644
--- a/templates/lecture.html
+++ b/templates/lecture.html
@@ -27,7 +27,7 @@
 				<a href="{{url_for('course', handle=course.handle)}}#lecture-{{lecture.id}}" class="btn btn-default" >Zur Veranstaltungsseite</a>
 				<ul class="list-inline pull-right">
 					<li><button class="btn btn-default" id="hintnewchapter">Kapitelmarker vorschlagen</button></li>
-					<li>{{ video_embed_btn(lecture.id) }}</li>
+					<li>{{ video_embed_btn(lecture.id, course=course.handle) }}</li>
 					<li class="dropdown">{{ video_download_btn(videos) }}</li>
 				</ul>
 			</div>
@@ -98,5 +98,19 @@ $(function() {
 			})
 
 });
+$(document).ready(function() {
+	$("a.reloadonclose").click(function () {
+		var popup = window.open(this.href, this.target);
+		if (!popup)
+			return true;
+		var popup_check = setInterval(function() {
+			if (popup.closed) {
+				clearInterval(popup_check);
+				location.reload();
+			};
+		}, 500);
+		return false;
+	});
+});
 </script>
 {% endblock %}
diff --git a/templates/macros.html b/templates/macros.html
index 7f7c80b4cc76d18a6d1eed5753a13d08e505609a..9395650c02de2d2d6b1bebb617f30b91ff1ed312 100644
--- a/templates/macros.html
+++ b/templates/macros.html
@@ -1,6 +1,6 @@
 {% macro preview(lecture) %}
 <li class="list-group-item">
-	<a href="{{url_for('lecture', id=lecture['id'])}}" title="{{ lecture.course.title }}" style="color: #000">
+	<a href="{{url_for('lecture', course=lecture.course.handle, id=lecture['id'])}}" title="{{ lecture.course.title }}" style="color: #000">
 		<div class="hidden-xs">
 			<div class="row">
 				<img class="col-xs-4" style="max-height: 120px; height: auto; width:170px" src="{{ config.VIDEOPREFIX }}/{{ lecture['titlefile'] }}" alt="Vorschaubild" onerror="this.src='{{url_for('static',filename='no-thumbnail.png')}}'; this.onerror=''; ">
@@ -144,12 +144,12 @@ $(function() {
 {% endif %}
 {% endmacro %}
 
-{%macro video_embed_btn(lectureid) %}
+{%macro video_embed_btn(lectureid, course=None) %}
 <a class="btn btn-default" id="embedcodebtn" data-container="body" data-toggle="popover" data-placement="bottom">
 	<span>Einbetten</span>
 </a>
 <script>
-{% set embedcode = '<iframe width="700" height="394" src="'+url_for('embed', id=lectureid, _external=True)+'" frameborder="0" allowfullscreen="true"></iframe>' %}
+{% set embedcode = '<iframe width="700" height="394" src="'+url_for('embed', course=course, id=lectureid, _external=True)+'" frameborder="0" allowfullscreen="true"></iframe>' %}
 $('#embedcodebtn').popover(
 		{
 			html:true,
@@ -165,7 +165,7 @@ $('#embedcodebtn').popover(
 	<div class="row">
 		<div style="background-image: url('{% if not lecture.titlefile %}{{url_for('static',filename='no-thumbnail.png')}}{% else %}{{ config.VIDEOPREFIX }}/{{lecture.titlefile}}'){% endif %}" class="col-sm-2 col-xs-12 thumbnailimg">
 		{% if not videos|length is equalto 0 %}
-			<a href="{{url_for('lecture', id=lecture.id)}}">
+			<a href="{{url_for('lecture', course=lecture.course.handle, id=lecture.id)}}">
 				<span class="glyphicon glyphicon-play-circle playpreviewbtn"></span>
 			</a>
 		{% endif %}
@@ -214,7 +214,7 @@ $('#embedcodebtn').popover(
 		<a class="moderator_editor_sign btn btn-default" title="{{path|join('.')}}" data-toggle="tooltip" tabindex="0" style="padding: 3px; margin-right: 5px;">
 			<span class="glyphicon glyphicon-pencil"></span>
 		</a>
-		<span class="moderator_editor_value">{{ value|safe }}</span>
+		<span class="moderator_editor_value">{{ value|fixnl|safe }}</span>
 	</span>
 	{% else %}
 		{{value|fixnl|safe}}
@@ -306,3 +306,46 @@ $('#embedcodebtn').popover(
 	});
 </script>
 {% endmacro %}
+
+{% macro featured_content(item) %} {# This macro is used in a panel div after the panel header #}
+	{% if item.type == 'image' %}
+		<!-- Putting image tag here makes it borderless. This should be replaced by a bootstrap-compatible solution. -->
+		<img src="{{item.param}}" style="width: 100%;"/>
+		{% if ismod() %}
+		<p>{{moderator_editor(('featured',item.id,'param'), item.param)}}</p>
+		{% endif %}
+		{% if item.text or ismod() %}
+			<div class="panel-body">{{ moderator_editor(('featured',item.id,'text'), item.text) }}</div>
+		{% endif %}
+	{% elif item.type == 'courses' %}
+		{% if item.text or ismod() %}
+			<div class="panel-body">{{ moderator_editor(('featured',item.id,'text'), item.text) }}</div>
+		{% endif %}
+		<ul class="courses-list list-group">
+		{% for i in item.courses %}
+			{{ course_list_item(i) }}
+		{% endfor %}
+		</ul>
+		<div class="panel-footer">
+		{% if ismod() %}
+			<ul class="list-inline">	
+			<li><div class="dropdown">
+				{% set params = {'semester': 'Semester', 'title': 'Veranstaltung', 'organizer': 'Dozent', 'subject': 'Kategorie'} %}
+				<button class="btn btn-default dropdown-toggle" type="button" data-toggle="dropdown">{{params[item.param]}}: <span class="caret"></span></button>
+				<ul class="dropdown-menu">
+					{% for key, name in params.items() %}
+					<li><a href="{{url_for('edit', **{'featured.'+item.id|string+'.param': key, 'ref': request.url})}}">{{name}}</a></li>
+					{% endfor %}
+				</ul>
+			</div></li>
+			<li>{{ moderator_editor(('featured',item.id,'param2'), item.param2) }}</li>
+			</ul>
+		{% endif %}
+		Die vollständige Liste findest du <a href="{{ url_for('courses') }}">hier</a>.
+		</div>
+	{% else %}
+		{% if item.text or ismod() %}
+			<div class="panel-body">{{ moderator_editor(('featured',item.id,'text'), item.text) }}</div>
+		{% endif %}
+	{% endif %}
+{% endmacro %}
diff --git a/timetable.py b/timetable.py
index a8e18be29a810c85b67f578167cdfb3c96db08b3..2ccb548870b4a16c87876ed5b1e0b04eec5511e1 100644
--- a/timetable.py
+++ b/timetable.py
@@ -1,6 +1,6 @@
 from server import *
 
-@app.route('/timetable')
+@app.route('/internal/timetable')
 @register_navbar('Drehplan', icon='calendar')
 @mod_required
 def timetable():