diff --git a/config.py.example b/config.py.example
index 62997b3c13d11c0164cb145131baa9e40c6070a4..27254fd575acc9c1fa6df3b8cd010a416665f876 100644
--- a/config.py.example
+++ b/config.py.example
@@ -1,5 +1,5 @@
 # Defaults for development ,do not use in production!
-DEBUG = True
+DEBUG = False
 VIDEOPREFIX = 'https://videoag.fsmpi.rwth-aachen.de'
 VIDEOMOUNT = 'files/'
 #SECRET_KEY = 'something random'
@@ -16,6 +16,6 @@ DB_DATA = 'db_example.sql'
 DB_ENGINE = 'sqlite'
 SQLITE_DB = 'db.sqlite'
 SQLITE_INIT_SCHEMA = True
-SQLITE_INIT_DATA = True
+SQLITE_INIT_DATA = False
 
 #LDAP_HOST = 'ldaps://rumo.fsmpi.rwth-aachen.de'
diff --git a/db_schema.sql b/db_schema.sql
index 58e486bda352a9c232257e501bd73a9a65c7c5df..3b39f48cbaa602699270d623d356b1e36985cfb4 100644
--- a/db_schema.sql
+++ b/db_schema.sql
@@ -42,26 +42,26 @@ CREATE TABLE IF NOT EXISTS `chapters` (
 );
 CREATE TABLE IF NOT EXISTS `courses_data` (
 `id` INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
-  `visible` INTEGER NOT NULL,
+  `visible` INTEGER NOT NULL DEFAULT '0',
   `listed` INTEGER NOT NULL DEFAULT '1',
   `deleted` INTEGER NOT NULL DEFAULT '0',
-  `title` text NOT NULL,
-  `short` varchar(32) NOT NULL,
-  `handle` varchar(32) NOT NULL,
-  `organizer` text NOT NULL,
-  `subject` varchar(32) NOT NULL,
+  `title` text NOT NULL DEFAULT '',
+  `short` varchar(32) NOT NULL DEFAULT '',
+  `handle` varchar(32) NOT NULL DEFAULT '',
+  `organizer` text NOT NULL DEFAULT '',
+  `subject` varchar(32) NOT NULL DEFAULT '',
   `credits` INTEGER NOT NULL DEFAULT '0',
   `created_by` INTEGER DEFAULT NULL,
   `time_created` datetime NOT NULL,
   `time_updated` datetime NOT NULL,
-  `semester` char(6) NOT NULL,
-  `settings` text NOT NULL,
+  `semester` char(6) NOT NULL DEFAULT '',
+  `settings` text NOT NULL DEFAULT '',
   `downloadable` INTEGER NOT NULL DEFAULT '1',
   `embedinvisible` INTEGER NOT NULL DEFAULT '0',
-  `description` text NOT NULL,
-  `internal` text NOT NULL,
-  `responsible` text NOT NULL,
-  `feed_url` text NOT NULL
+  `description` text NOT NULL DEFAULT '',
+  `internal` text NOT NULL DEFAULT '',
+  `responsible` text NOT NULL DEFAULT '',
+  `feed_url` text NOT NULL DEFAULT ''
 );
 CREATE TABLE IF NOT EXISTS `filesizes` (
   `path` varchar(255) NOT NULL PRIMARY KEY,
@@ -185,18 +185,19 @@ CREATE TABLE IF NOT EXISTS `videos_data` (
 );
 CREATE TABLE IF NOT EXISTS `announcements` (
 `id` INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
-	`text` text NOT NULL,
-	`internal` text NOT NULL,
+	`text` text NOT NULL DEFAULT '',
 	`level` INTEGER NOT NULL DEFAULT 0,
 	`visible` INTEGER NOT NULL DEFAULT 0,
 	`deleted` INTEGER NOT NULL DEFAULT 0,
+  `time_publish` datetime DEFAULT "",
+  `time_expire` datetime DEFAULT "",
   `time_created` datetime NOT NULL,
   `time_updated` datetime NOT NULL,
   `created_by` INTEGER NOT NULL
 );
 CREATE TABLE IF NOT EXISTS `featured` (
 `id` INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
-	`title` text NOT NULL DEFAULT "Neuer Artikel",
+	`title` text NOT NULL DEFAULT '',
 	`text` text NOT NULL DEFAULT "",
 	`internal` text NOT NULL DEFAULT "",
 	`visible` INTEGER NOT NULL DEFAULT 0,
diff --git a/importer.py b/importer.py
old mode 100755
new mode 100644
diff --git a/schedule.py b/schedule.py
old mode 100755
new mode 100644
diff --git a/server.py b/server.py
old mode 100755
new mode 100644
index 089177a50d3f7be963f63a5d2e09a6684d7e15b2..9edddf0ac8134e14e156fee238273177999050b5
--- a/server.py
+++ b/server.py
@@ -12,6 +12,9 @@ app = Flask(__name__)
 
 app.jinja_env.trim_blocks = True
 app.jinja_env.lstrip_blocks = True
+app.add_template_global(random.randint, name='randint')
+app.add_template_global(datetime, name='datetime')
+app.add_template_global(timedelta, name='timedelta')
 
 def timer_func():
 	with app.test_request_context():
@@ -25,9 +28,9 @@ timer.start()
 
 config = app.config
 config.from_pyfile('config.py.example', silent=True)
-if not sys.argv[0].endswith('run.py'): 
-	config['SQLITE_INIT_DATA'] = False
-	config['DEBUG'] = False
+if sys.argv[0].endswith('run.py'): 
+	config['SQLITE_INIT_DATA'] = True
+	config['DEBUG'] = True
 config.from_pyfile('config.py', silent=True)
 if config['DEBUG']:
 	app.jinja_env.auto_reload = True
@@ -120,7 +123,10 @@ def rfc3339(d):
 
 @app.template_global()
 def get_announcements(minlevel=0):
-	return query('SELECT * FROM announcements WHERE NOT deleted AND (? OR visible) AND level >= ? ORDER BY level DESC', ismod(), minlevel)
+	offset = timedelta()
+	if ismod():
+		offset = timedelta(hours=24)
+	return query('SELECT * FROM announcements WHERE NOT deleted AND (time_expire ISNULL OR time_expire > ?) AND (? OR (visible AND time_publish < ?)) AND level >= ? ORDER BY level DESC', datetime.now()-offset, ismod(), datetime.now(), minlevel)
 
 @app.template_filter()
 def fixnl(s):
@@ -248,26 +254,32 @@ def logout():
 	session.pop('user')
 	return redirect(request.values.get('ref', url_for('index')))
 
+tabs = {
+	'courses': ('courses_data', 'id', ['visible', 'listed', 'title', 'short',
+			'handle', 'organizer', 'subject', 'semester', 'downloadable',
+			'internal', 'responsible','deleted']),
+	'lectures': ('lectures_data', 'id', ['visible', 'title', 'comment',
+			'internal', 'speaker', 'place', 'time', 'duration', 'jumplist','deleted']),
+	'videos': ('videos_data', 'id', ['visible','deleted']),
+	'chapters': ('chapters', 'id', ['time', 'text', 'visible', 'deleted']),
+	'announcements': ('announcements', 'id', ['text', 'level', 'visible', 'deleted', 'time_publish', 'time_expire']),
+	'featured': ('featured', 'id', ['title', 'text', 'internal', 'visible', 'deleted'])
+}
+
 @app.route('/edit', methods=['GET', 'POST'])
 @mod_required
-def edit(prefix="", ignore=[]):
+def edit(prefix='', ignore=[]):
 	# All editable tables are expected to have a 'time_updated' field
-	tabs = {
-		'courses': ('courses_data', 'id', ['visible', 'listed', 'title', 'short',
-				'handle', 'organizer', 'subject', 'semester', 'downloadable',
-				'internal', 'responsible','deleted']),
-		'lectures': ('lectures_data', 'id', ['visible', 'title', 'comment',
-				'internal', 'speaker', 'place', 'time', 'duration', 'jumplist','deleted']),
-		'videos': ('videos_data', 'id', ['visible','deleted']),
-		'chapters': ('chapters', 'id', ['time', 'text', 'visible', 'deleted']),
-		'announcements': ('announcements', 'id', ['text', 'internal', 'level', 'visible', 'deleted']),
-		'featured': ('featured', 'id', ['text', 'title', 'visible', 'deleted'])
-	}
+	ignore.append('ref')
+	ignore.append('prefix')
+	if not prefix and 'prefix' in request.args:
+		prefix = request.args['prefix']
 	modify('BEGIN')
 	if request.is_json:
 		changes = request.get_json().items()
 	else:
 		changes = request.args.items()
+	created = {}
 	for key, val in changes:
 		if key in ignore:
 			continue
@@ -275,22 +287,21 @@ 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]),table,id,tabs[table][1],column,val,id,datetime.now(),session['user']['givenName'])
+		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')
+	if 'ref' in request.values:
+		return redirect(request.values['ref'])
 	return "OK", 200
 
-@app.route('/newcourse', methods=['GET', 'POST'])
+@app.route('/new/<table>', methods=['GET', 'POST'])
 @mod_required
-def new_course():
-	id = modify('''
-		INSERT INTO courses_data
-			(visible, title, short, handle, organizer, subject, created_by, time_created,
-			 time_updated, semester, settings, description, internal, responsible, feed_url)
-			VALUES (0, "Neue Veranstaltung", "Neu", ?, "", "", ?, ?, ?, "", "", "", "", ?, "")
-		''', 'new'+str(random.randint(0,1000)), session['user']['dbid'], datetime.now(), datetime.now(),
-		session['user']['givenName'])
-	edit(prefix='courses.'+str(id)+'.', ignore=['ref'])
+def create(table):
+	assert table in tabs
+	id = modify('INSERT INTO %s (created_by, time_created, time_updated) VALUES (?, ?, ?)'%tabs[table][0],
+			session['user']['dbid'], datetime.now(), datetime.now())
+	edit(prefix=table+'.'+str(id)+'.')
 	if 'ref' in request.values:
 		return redirect(request.values['ref'])
 	return str(id), 200
@@ -361,7 +372,9 @@ def stats():
 @register_navbar('Changelog', icon='book')
 @mod_required
 def changelog():
-	changelog = query('SELECT *, ( "table" || "." || id_value || "." ||field) as path FROM changelog LEFT JOIN users ON (changelog.who = users.id) ORDER BY `when` DESC LIMIT 50')
+	changelog = query('SELECT * FROM changelog LEFT JOIN users ON (changelog.who = users.id) ORDER BY `when` DESC LIMIT 50')
+	for entry in changelog:
+		entry['path'] = '.'.join([entry['table'], entry['id_value'], entry['field']])
 	return render_template('changelog.html', changelog=changelog)
 
 @app.route('/files/<filename>')
@@ -383,24 +396,6 @@ def suggest_chapter(lectureid):
 		return redirect(request.values['ref'])
 	return 'OK',  200
 
-@app.route('/newpsa', methods=['POST', 'GET'])
-@mod_required
-def new_announcement():
-	id = modify('INSERT INTO announcements (text, internal, time_created, time_updated, created_by) VALUES ("Neue Ankündigung", "", ?, ?, ?)',
-			datetime.now(), datetime.now(), session.get('user', {'dbid':None})['dbid'])
-	if 'ref' in request.values:
-		return redirect(request.values['ref'])
-	return id,  200
-
-@app.route('/newfeatured', methods=['POST', 'GET'])
-@mod_required
-def new_featured():
-	id = modify('INSERT INTO featured (time_created, time_updated, created_by) VALUES (?, ?, ?)',
-			datetime.now(), datetime.now(), session.get('user', {'dbid':None})['dbid'])
-	if 'ref' in request.values:
-		return redirect(request.values['ref'])
-	return id,  200
-
 @app.route('/sitemap.xml')
 def sitemap():
 	pages=[]
diff --git a/templates/base.html b/templates/base.html
index 79ef4d8d9705b4d799ea8efbf265d8120646f6aa..2f7cb790e75dca5c98fb25d62e37b5d29cb09792 100644
--- a/templates/base.html
+++ b/templates/base.html
@@ -1,6 +1,6 @@
 {% set page_border = page_border|default(1) %}
 {% set min_announcement_level = min_announcement_level|default(1) %}
-{% set announcement_levels = {0: 'info', 1: 'info', 2: 'warning', 3: 'danger'} %}
+{% set levels = {0: ('info', 'Nur auf Hauptseite sichtbar'), 1: ('info', 'Überall sichtbar (Hinweis)'), 2: ('warning', 'Überall sichtbar (Warnung)'), 3: ('danger', 'Überall sichtbar (Wichtig)')} %}
 {% from 'macros.html' import valueeditor, valuecheckbox, valuedeletebtn %}
 
 <!DOCTYPE html>
@@ -100,20 +100,10 @@
 					{% for msg in get_flashed_messages() %}
 					<div class="alert alert-danger" role="alert">{{ msg }}</div>
 					{% endfor %}
-					{% for msg in get_announcements(min_announcement_level) if (not request.cookies['alert-info-'+msg.id|string]) or ismod() %}
-					<div class="alert alert-{{announcement_levels.get(msg.level, 'info')}}" role="alert">
-						{% if not ismod() %}
-							<a href="#" class="close" data-dismiss="alert" aria-label="close" onclick="Cookies.set('alert-info-{{msg.id}}', '1');">&times;</a>
-						{% endif %}
-						<ul class="list-unstyled">
-							<li>{{ valueeditor(('announcements',msg.id,'text'), msg.text|safe) }}</li>
-							<li class="pull-right">{{ valuedeletebtn(('announcements',msg.id,'deleted')) }}</li>
-						{% if ismod() %}
-							<li>{{ valueeditor(('announcements',msg.id,'internal'), msg.internal) }}</li>
-							<li class="pull-right">Sichtbar: {{ valuecheckbox(('announcements',msg.id,'visible'),msg.visible) }}</li>
-							<li>Level: {{ valueeditor(('announcements',msg.id,'level'), msg.level) }}</li>
-						{% endif %}
-						</ul>
+					{% for msg in get_announcements(min_announcement_level) if (not request.cookies['alert-info-'+msg.id|string]) %}
+					<div class="alert alert-{{levels.get(msg.level, ('info', ''))[0]}}" role="alert">
+						<a href="#" class="close" data-dismiss="alert" aria-label="close" onclick="Cookies.set('alert-info-{{msg.id}}', '1');">&times;</a>
+						{{ msg.text|safe }}
 					</div>
 					{% endfor %}
 					{% block content %}
diff --git a/templates/courses.html b/templates/courses.html
index f8f07bf0b99a085af995c81da5bb273f67733179..7a1cf9175730c0c4fb79443e2c15ccfe27b46659 100644
--- a/templates/courses.html
+++ b/templates/courses.html
@@ -9,7 +9,7 @@
 			</li>
 			{% if ismod() %} 
 			<li>
-				<a class="btn btn-default" href="{{ url_for('new_course', ref=request.url) }}">Neue Veranstaltung</a>
+				<a class="btn btn-default" href="{{ url_for('create', table='courses', handle='new'+(randint(0,1000)|string), title='Neue Veranstaltung', responsible=session.user.givenName, ref=request.url) }}">Neue Veranstaltung</a>
 			</li>
 			{% endif %}
 			<li class="dropdown" style="padding-right: 0px">
diff --git a/templates/index.html b/templates/index.html
index 9d3ef58ff28a58dd87eccf851e756c2f31f58d5b..f2100078abefaf535134dd61cd32c80dde9781c6 100644
--- a/templates/index.html
+++ b/templates/index.html
@@ -1,19 +1,59 @@
 {% from 'macros.html' import preview %}
 {% extends "base.html" %}
 {% set page_border = 0 %}
-{% set min_announcement_level = 0 %}
+{% if ismod() %}
+	{# Little hack to not show annoucements twice #}
+	{% set min_announcement_level = 999 %}
+{% else %}
+	{% set min_announcement_level = 0 %}
+{% endif %}
 {% block content %}
+{% if ismod() %}
 <div class="row">
 	<div class="col-xs-12">
-		<ul class="list-inline pull-right">
-			{% if ismod() %}
-				<li style="padding-right: 0px;">
-					<a class="btn btn-default" href="{{ url_for('new_announcement', ref=request.url) }}">Neue Ankündigung</a>
+		{% for msg in get_announcements() %}
+		<div class="alert alert-{{levels.get(msg.level, ('info', ''))[0]}}" role="alert">
+			<ul class="list-unstyled">
+				<li>{{ valueeditor(('announcements',msg.id,'text'), msg.text|safe) }}</li>
+				<li class="pull-right">{{ valuedeletebtn(('announcements',msg.id,'deleted')) }}</li>
+				<li class="pull-right" style="padding-right: 5px;">
+					{% if not msg.visible %}
+						<a href="{{ url_for('edit', prefix='announcements.'+str(msg.id|string)+'.', ref=request.url, visible=1) }}" class="btn btn-primary">Veröffentlichen</a>
+					{% elif msg.time_expire and msg.time_expire < datetime.now() %}
+						<a href="#" class="btn btn-danger disabled">Abgelaufen</a>
+					{% elif msg.time_publish and msg.time_publish > datetime.now() %}
+						<a href="#" class="btn btn-default disabled">Geplant</a>
+					{% else %}
+						<a href="#" class="btn btn-success disabled">Öffentlich</a>
+					{% endif %}
 				</li>
-			{% endif %}
+				<li class="dropdown pull-right" style="padding-right: 5px;">
+					<span class="btn btn-default dropdown-toggle" type="button" data-toggle="dropdown">{{levels.get(msg.level, ('', 'Unbekannt'))[1]}} <span class="caret"></span></span>
+					<ul class="dropdown-menu">
+						{% for level, descr in levels.items() %}
+							<li><a href="{{ url_for('edit', prefix='announcements.'+(msg.id|string)+'.', ref=request.url, level=level) }}">{{ descr[1] }}</a></li>
+						{% endfor %}
+					</ul>
+				</li>
+				<li>
+					Aktiv von {{ valueeditor(('announcements',msg.id,'time_publish'), msg.time_publish) }}
+					bis {{ valueeditor(('announcements',msg.id,'time_expire'), msg.time_expire) }}
+				</li>
+			</ul>
+		</div>
+		{% endfor %}
+	</div>
+</div>
+<div class="row">
+	<div class="col-xs-12">
+		<ul class="list-inline pull-right">
+			<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>
 		</ul>
 	</div>
 </div>
+{% endif %}
 <div class="row">
 	<div class="col-md-6 panel-group">
 		<div class="panel panel-default">
@@ -35,7 +75,7 @@
 				<div class="panel-heading">
 					<h1 class="panel-title">Featured
 						{% if ismod() %}
-							<a class="btn btn-default" href="{{ url_for('new_featured', ref=request.url) }}">Neue Empfehlung</a>
+							<a class="btn btn-default" href="{{ url_for('create', table='featured', title='Neuer Artikel', ref=request.url) }}">Neue Empfehlung</a>
 						{% endif %}
 					</h1>
 				</div>