diff --git a/config.py.example b/config.py.example
index 4d8d389a2785d2541fa4b2c5f41c524d65af04d7..66052cd5376e4ed5a2b69fd5cc556e791fc87b9f 100644
--- a/config.py.example
+++ b/config.py.example
@@ -1,18 +1,52 @@
 SQLALCHEMY_DATABASE_URI = "postgresql://proto3:@/proto3"
 SQLALCHEMY_TRACK_MODIFICATIONS = False
+
 SECRET_KEY = "abc"
+
 DEBUG = False
+
 MAIL_ACTIVE = True
 MAIL_FROM = "protokolle@example.com"
 MAIL_HOST = "mail.example.com:465"
 MAIL_USER = "user"
 MAIL_PASSWORD = "password"
 MAIL_PREFIX = "protokolle"
+
 CELERY_BROKER_URL = "redis://localhost:6379/0"
 CELERY_TASK_SERIALIZER = "pickle"
 CELERY_ACCEPT_CONTENT = ["pickle"]
+
 URL_ROOT = "protokolle.example.com"
 URL_PROTO = "https"
 URL_PATH = "/"
 URL_PARAMS = ""
+
 ERROR_CONTEXT_LINES = 3
+
+# choose something nice from fc-list
+FONTS = {
+    "main": {
+        "regular": "Nimbus Sans",
+        "bold": "NimbusSans",
+        "italic": "NimbusSans",
+        "bolditalic": "NimbusSans"
+    },
+    "roman": {
+        "regular": "Nimbus Roman",
+        "bold": "Nimbus Roman",
+        "italic": "Nimbus Roman",
+        "bolditalic": "Nimbus Roman"
+    },
+    "sans": {
+        "regular": "Nimbus Sans",
+        "bold": "NimbusSans",
+        "italic": "NimbusSans",
+        "bolditalic": "NimbusSans"
+    },
+    "mono": {
+        "regular": "Nimbus Mono PS",
+        "bold": "Nimbus Mono PS",
+        "italic": "Nimbus Mono PS",
+        "bolditalic": "Nimbus Mono PS"
+    }
+}
diff --git a/migrations/versions/b114754024fb_.py b/migrations/versions/b114754024fb_.py
new file mode 100644
index 0000000000000000000000000000000000000000..a381a071d6d2a82d07d7c70925e71ad03673de2c
--- /dev/null
+++ b/migrations/versions/b114754024fb_.py
@@ -0,0 +1,28 @@
+"""empty message
+
+Revision ID: b114754024fb
+Revises: 97cf1913e60d
+Create Date: 2017-02-23 20:33:56.446729
+
+"""
+from alembic import op
+import sqlalchemy as sa
+
+
+# revision identifiers, used by Alembic.
+revision = 'b114754024fb'
+down_revision = '97cf1913e60d'
+branch_labels = None
+depends_on = None
+
+
+def upgrade():
+    # ### commands auto generated by Alembic - please adjust! ###
+    op.add_column('documents', sa.Column('is_private', sa.Boolean(), nullable=True))
+    # ### end Alembic commands ###
+
+
+def downgrade():
+    # ### commands auto generated by Alembic - please adjust! ###
+    op.drop_column('documents', 'is_private')
+    # ### end Alembic commands ###
diff --git a/models/database.py b/models/database.py
index 87b9f1d20eb7a998073ae8c591f0494c088636fe..3bac9b1ed9a7e986eb28d04f89aa4de30f361bbe 100644
--- a/models/database.py
+++ b/models/database.py
@@ -6,9 +6,10 @@ import math
 from shared import db
 from utils import random_string, url_manager
 
-#from models.tables import TexResponsiblesTable, TexSupportersTable
+import os
 
-from sqlalchemy.orm import relationship, backref
+from sqlalchemy import event
+from sqlalchemy.orm import relationship, backref, sessionmaker
 
 import config
 
@@ -171,16 +172,28 @@ class Document(db.Model):
     name = db.Column(db.String)
     filename = db.Column(db.String, unique=True)
     is_compiled = db.Column(db.Boolean)
+    is_private = db.Column(db.Boolean)
 
-    def __init__(self, protocol_id, name, filename, is_compiled):
+    def __init__(self, protocol_id, name, filename, is_compiled, is_private):
         self.protocol_id = protocol_id
         self.name = name
         self.filename = filename
         self.is_compiled = is_compiled
+        self.is_private = is_private
 
     def __repr__(self):
-        return "<Document(id={}, protocol_id={}, name={}, filename={}, is_compiled={})>".format(
-            self.id, self.protocol_id, self.name, self.filename, self.is_compiled)
+        return "<Document(id={}, protocol_id={}, name={}, filename={}, is_compiled={}, is_private={})>".format(
+            self.id, self.protocol_id, self.name, self.filename, self.is_compiled, self.is_private)
+
+    def get_filename(self):
+        return os.path.join(config.DOCUMENTS_PATH, self.filename)
+
+@event.listens_for(Document, "before_delete")
+def on_delete(mapper, connection, document):
+    if document.filename is not None:
+        document_path = os.path.join(config.DOCUMENTS_PATH, document.filename)
+        if os.path.isfile(document_path):
+            os.remove(document_path)
 
 class Todo(db.Model):
     __tablename__ = "todos"
diff --git a/server.py b/server.py
index 030978ceffa89c8453c51731f2d5ef08c6682e52..5ca957dcc36b0e32238a20714c64ed6d9a1034cf 100755
--- a/server.py
+++ b/server.py
@@ -3,6 +3,7 @@ import locale
 locale.setlocale(locale.LC_TIME, "de_DE.utf8")
 
 from flask import Flask, g, current_app, request, session, flash, redirect, url_for, abort, render_template, Response, send_file
+from werkzeug.utils import secure_filename
 from flask_script import Manager, prompt
 from flask_migrate import Migrate, MigrateCommand
 #from flask_socketio import SocketIO
@@ -10,13 +11,14 @@ from celery import Celery
 from functools import wraps
 import requests
 from io import StringIO, BytesIO
+import os
 
 import config
 from shared import db, date_filter, datetime_filter, date_filter_long, time_filter, ldap_manager, security_manager
 from utils import is_past, mail_manager, url_manager
 from models.database import ProtocolType, Protocol, DefaultTOP, TOP, Document, Todo, Decision, MeetingReminder, Error
-from views.forms import LoginForm, ProtocolTypeForm, DefaultTopForm, MeetingReminderForm, NewProtocolForm
-from views.tables import ProtocolsTable, ProtocolTypesTable, ProtocolTypeTable, DefaultTOPsTable, MeetingRemindersTable, ErrorsTable, TodosTable
+from views.forms import LoginForm, ProtocolTypeForm, DefaultTopForm, MeetingReminderForm, NewProtocolForm, DocumentUploadForm
+from views.tables import ProtocolsTable, ProtocolTypesTable, ProtocolTypeTable, DefaultTOPsTable, MeetingRemindersTable, ErrorsTable, TodosTable, DocumentsTable
 
 app = Flask(__name__)
 app.config.from_object(config)
@@ -335,7 +337,9 @@ def show_protocol(protocol_id):
         flash("Invalides Protokoll.", "alert-error")
         return redirect(request.args.get("next") or url_for("index"))
     errors_table = ErrorsTable(protocol.errors)
-    return render_template("protocol-show.html", protocol=protocol, errors_table=errors_table)
+    documents_table = DocumentsTable(protocol.documents)
+    document_upload_form = DocumentUploadForm()
+    return render_template("protocol-show.html", protocol=protocol, errors_table=errors_table, documents_table=documents_table, document_upload_form=document_upload_form)
 
 @app.route("/protocol/etherpull/<int:protocol_id>")
 def etherpull_protocol(protocol_id):
@@ -345,10 +349,7 @@ def etherpull_protocol(protocol_id):
         flash("Invalides Protokoll oder keine Berechtigung.", "alert-error")
         return redirect(request.args.get("next") or url_for("index"))
     source_req = requests.get(protocol.get_etherpad_source_link())
-    #source = source_req.content.decode("utf-8")
     source = source_req.text
-    print(source.split("\r"))
-    #print(source.split("\n"))
     protocol.source = source
     db.session.commit()
     tasks.parse_protocol(protocol)
@@ -386,6 +387,49 @@ def list_todos():
     todos_table = TodosTable(todos)
     return render_template("todos-list.html", todos=todos, todos_table=todos_table)
 
+@app.route("/document/download/<int:document_id>")
+def download_document(document_id):
+    user = current_user()
+    document = Document.query.filter_by(id=document_id).first()
+    if document is None:
+        flash("Invalides Dokument.", "alert-error")
+        return redirect(request.args.get("next") or url_for("index"))
+    if ((document.is_private
+            and not document.protocol.protocoltype.has_private_view_right(user))
+        or (not document.is_private
+            and not document.protocol.protocoltype.has_public_view_right(user))):
+        flash("Keine Berechtigung.", "alert-error")
+        return redirect(request.args.get("next") or url_for("index"))
+    with open(document.get_filename(), "rb") as file:
+        file_like = BytesIO(file.read())
+        return send_file(file_like, cache_timeout=1, as_attachment=True, attachment_filename=document.name)
+
+@app.route("/document/upload/<int:protocol_id>", methods=["POST"])
+@login_required
+def upload_document(protocol_id):
+    user = current_user()
+    protocol = Protocol.query.filter_by(id=protocol_id).first()
+    if protocol is None or not protocol.protocoltype.has_modify_right(user):
+        flash("Insufficient permissions.", "alert-error")
+        return redirect(request.args.get("next") or url_for("index"))
+    form = DocumentUploadForm()
+    print(form, form.document.data, form.private.data)
+    print(request.files)
+    if form.document.data is None:
+        flash("No file has been selected.", "alert-error")
+        return redirect(request.args.get("next") or url_for("index"))
+    file = form.document.data
+    if file.filename == "":
+        flash("No file has been selected.", "alert-error")
+        return redirect(request.args.get("next") or url_for("index"))
+    if file:
+        filename = secure_filename(file.filename)
+        internal_filename = "{}-{}".format(protocol.id, filename)
+        file.save(os.path.join(config.DOCUMENTS_PATH, internal_filename))
+        document = Document(protocol.id, filename, internal_filename, False, form.private.data)
+        db.session.add(document)
+        db.session.commit()
+    return redirect(request.args.get("next") or url_for("show_protocol", protocol_id=protocol.id))
 
 
 @app.route("/login", methods=["GET", "POST"])
diff --git a/shared.py b/shared.py
index a54521eee1ae8eca6e2cce0effb93acb096db4aa..aa22cf5fd695ba07facecfbab504fbb9022f36cd 100644
--- a/shared.py
+++ b/shared.py
@@ -69,6 +69,8 @@ def datetime_filter(date):
     return date.strftime("%d. %B %Y, %H:%M")
 def date_filter_long(date):
     return date.strftime("%A, %d.%m.%Y, Kalenderwoche %W")
+def date_filter_short(date):
+    return date.strftime("%d.%m.%Y")
 def time_filter(time):
     return time.strftime("%H:%m")
 
diff --git a/tasks.py b/tasks.py
index eb30dba5839e4079ae367327562d57fc54991fd8..96aa4b138e7a82965c761fdd13d432853f90e001 100644
--- a/tasks.py
+++ b/tasks.py
@@ -7,7 +7,7 @@ import tempfile
 
 from models.database import Document, Protocol, Error, Todo, Decision, TOP, DefaultTOP
 from server import celery, app
-from shared import db, escape_tex, unhyphen, date_filter, datetime_filter, date_filter_long, time_filter, class_filter
+from shared import db, escape_tex, unhyphen, date_filter, datetime_filter, date_filter_long, date_filter_short, time_filter, class_filter
 from utils import mail_manager, url_manager, encode_kwargs, decode_kwargs
 from parser import parse, ParserException, Element, Content, Text, Tag, Remark, Fork
 
@@ -27,6 +27,7 @@ texenv.lstrip_blocks = True
 texenv.filters["url_complete"] = url_manager.complete
 texenv.filters["datify"] = date_filter
 texenv.filters["datify_long"] = date_filter_long
+texenv.filters["datify_short"] = date_filter_short
 texenv.filters["datetimify"] = datetime_filter
 texenv.filters["timify"] = time_filter
 texenv.filters["class"] = class_filter
@@ -182,7 +183,9 @@ def compile_async(content, protocol_id):
             log_filename = "protocol.log"
             with open(os.path.join(compile_dir, protocol_source_filename), "w") as source_file:
                 source_file.write(content)
-            shutil.copy("static/tex/protokoll2.cls", compile_dir)
+            protocol2_class_source = texenv.get_template("protokoll2.cls").render(fonts=config.FONTS)
+            with open(os.path.join(compile_dir, "protokoll2.cls"), "w") as protocol2_class_file:
+                protocol2_class_file.write(protocol2_class_source)
             os.chdir(compile_dir)
             command = [
                 "/usr/bin/xelatex",
@@ -196,12 +199,12 @@ def compile_async(content, protocol_id):
             for old_document in [document for document in protocol.documents if document.is_compiled]:
                 protocol.documents.remove(old_document)
             db.session.commit()
-            document = Document(protocol.id, name="protokoll_{}_{}.pdf".format(protocol.protocoltype.short_name, date_filter(protocol.date)), filename="", is_compiled=True)
+            document = Document(protocol.id, name="protokoll_{}_{}.pdf".format(protocol.protocoltype.short_name, date_filter_short(protocol.date)), filename="", is_compiled=True, is_private=False)
             db.session.add(document)
             db.session.commit()
             target_filename = "compiled-{}.pdf".format(document.id)
             document.filename = target_filename
-            shutil.copy(os.path.join(compile_dir, protocol_target_filename), os.path.join("documents", target_filename))
+            shutil.copy(os.path.join(compile_dir, protocol_target_filename), os.path.join(config.DOCUMENTS_PATH, target_filename))
             db.session.commit()
             shutil.copy(os.path.join(compile_dir, log_filename), "/tmp")
         except subprocess.SubprocessError:
diff --git a/templates/macros.html b/templates/macros.html
index 86a89896936fd7bbde88232d6c3a0a83bad4d219..11d3f2c76a8ed34da13f0366cfdb6f6bfa996a21 100644
--- a/templates/macros.html
+++ b/templates/macros.html
@@ -87,9 +87,9 @@ to not render a label for the CRSFTokenField -->
         action_text - text of submit button
         class_ - sets a class for form
     #}
-{% macro render_form(form, action_url='', action_text='Submit', class_='', btn_class='btn btn-default') -%}
+{% macro render_form(form, action_url='', action_text='Submit', class_='', btn_class='btn btn-default', enctype=None) -%}
 
-    <form method="POST" action="{{ action_url }}" role="form" class="{{ class_ }}">
+    <form method="POST" action="{{ action_url }}" role="form" class="{{ class_ }}"{% if enctype is not none %}enctype="{{enctype}}"{% endif %}>
         {{ form.hidden_tag() if form.hidden_tag }}
         {% if caller %}
             {{ caller() }}
diff --git a/templates/protocol-show.html b/templates/protocol-show.html
index 55f9311ca29c469a806a52c59f70bf9b61eefd63..98aa0f0192c4921255b351a4e93ccea14766fdc1 100644
--- a/templates/protocol-show.html
+++ b/templates/protocol-show.html
@@ -1,5 +1,5 @@
 {% extends "layout.html" %}
-{% from "macros.html" import render_table %}
+{% from "macros.html" import render_table, render_form %}
 {% block title %}Protokoll{% endblock %}
 
 {% block content %}
@@ -71,8 +71,10 @@
                 {{render_table(errors_table)}}
             {% endif %}
             {% if protocol.documents|length > 0 %}
-                <h3>Anhang</h3>
-                {# TODO: render documents table here #}
+                {{render_table(documents_table)}}
+            {% endif %}
+            {% if protocol.is_done() %}
+                {{render_form(document_upload_form, action_url=url_for("upload_document", protocol_id=protocol.id, next=url_for("show_protocol", protocol_id=protocol.id)), action_text="Hochladen", enctype="multipart/form-data")}}
             {% endif %}
         </div>
     </div>
diff --git a/static/tex/protokoll2.cls b/templates/protokoll2.cls
similarity index 85%
rename from static/tex/protokoll2.cls
rename to templates/protokoll2.cls
index d5f32ea6c16a33f5489f374f84696ea77bb30260..be443f0764032592e11c6c3b585020b1c35d6814 100644
--- a/static/tex/protokoll2.cls
+++ b/templates/protokoll2.cls
@@ -14,37 +14,41 @@
 \LoadClass[a4paper]{article}
 
 % so ein paar sachen die wir eh immer brauchen
-%\RequirePackage[T1]{fontenc}
-%\RequirePackage{ngerman}
 \RequirePackage[ngerman]{babel}
-%\RequirePackage[utf8]{inputenc}
 \RequirePackage[vmargin=1.5cm,hmargin={1.5cm,1.2cm},bindingoffset=8mm]{geometry}
 %\RequirePackage{lineno}
 \RequirePackage{longtable}
 \RequirePackage{framed}
 \RequirePackage{eurosym}
 \RequirePackage[babel]{csquotes}
-%\RequirePackage{fontspec}
 \RequirePackage{polyglossia}
 
 \setmainlanguage[babelshorthands=true]{german}
 
 \RequirePackage{fontspec}
-%\setromanfont[Scale=0.925]{DejaVu Serif}
-%\setsansfont[Scale=0.925]{DejaVu Sans}
-%\setmonofont[Scale=0.925]{DejaVu Sans Mono}
-%\setmainfont[Scale=0.925]{DejaVu Sans}
-\setromanfont{Nimbus Roman}
-\setsansfont{Nimbus Sans}
-\setmonofont{Nimbus Mono PS}
+\setromanfont[
+    BoldFont={\VAR{fonts.roman.bold}},
+    ItalicFont={\VAR{fonts.roman.italic}},
+    BoldItalicFont={\VAR{fonts.roman.bolditalic}}
+]{\VAR{fonts.roman.regular}}
+\setsansfont[
+    BoldFont={\VAR{fonts.sans.bold}},
+    ItalicFont={\VAR{fonts.sans.italic}},
+    BoldItalicFont={\VAR{fonts.sans.bolditalic}}
+]{\VAR{fonts.sans.regular}}
+\setmonofont[
+    BoldFont={\VAR{fonts.mono.bold}},
+    ItalicFont={\VAR{fonts.mono.italic}},
+    BoldItalicFont={\VAR{fonts.mono.bolditalic}}
+]{\VAR{fonts.mono.regular}}
 \setmainfont[
-    BoldFont={NimbusSans},
-    ItalicFont={NimbusSans},
-    BoldItalicFont={NimbusSans}
-]{Nimbus Sans} % TODO: make configurable
+    BoldFont={\VAR{fonts.main.bold}},
+    ItalicFont={\VAR{fonts.main.italic}},
+    BoldItalicFont={\VAR{fonts.main.bolditalic}}
+]{\VAR{fonts.main.regular}}
 
 
-% nicht einr�cken und benutzerinnendefinierte kopfzeile
+% nicht einrücken und benutzerinnendefinierte kopfzeile
 \setlength{\parindent}{0cm}
 \setlength{\parskip}{1ex}
 \pagestyle{myheadings}
diff --git a/views/forms.py b/views/forms.py
index 6f6bb5840ed37cc5bf9674808d2a9f95e0f17a3c..48e4fa8ee53c60f2561dfab6299070f765f7ad10 100644
--- a/views/forms.py
+++ b/views/forms.py
@@ -1,5 +1,5 @@
 from flask_wtf import FlaskForm
-from wtforms import StringField, PasswordField, BooleanField, DateField, HiddenField, IntegerField, SelectField
+from wtforms import StringField, PasswordField, BooleanField, DateField, HiddenField, IntegerField, SelectField, FileField
 from wtforms.validators import InputRequired
 
 class LoginForm(FlaskForm):
@@ -32,3 +32,7 @@ class NewProtocolForm(FlaskForm):
     def __init__(self, protocoltypes, **kwargs):
         super().__init__(**kwargs)
         self.protocoltype.choices = [(protocoltype.id, protocoltype.short_name) for protocoltype in protocoltypes]
+
+class DocumentUploadForm(FlaskForm):
+    document = FileField("Datei", validators=[InputRequired("Du musst eine Datei angeben.")])
+    private = BooleanField("Intern")
diff --git a/views/tables.py b/views/tables.py
index 1fcc0a991ea226839f45a4a0dce1798d1ff2006d..8f81e0b7e974333866a62446f0620b2f08812745 100644
--- a/views/tables.py
+++ b/views/tables.py
@@ -1,7 +1,7 @@
 # coding: utf-8
 from flask import Markup, url_for, request
 from models.database import Protocol, ProtocolType, DefaultTOP, TOP, Todo, Decision
-from shared import date_filter, datetime_filter
+from shared import date_filter, datetime_filter, date_filter_short
 
 class Table:
     def __init__(self, title, values, newlink=None, newtext=None):
@@ -174,3 +174,17 @@ class TodosTable(Table):
             todo.who,
             todo.description
         ]
+
+class DocumentsTable(Table):
+    def __init__(self, documents):
+        super().__init__("Anhang", documents)
+
+    def headers(self):
+        return ["ID", "Name", ""]
+
+    def row(self, document):
+        return [
+            document.id,
+            Table.link(url_for("download_document", document_id=document.id), document.name),
+            ""
+        ]