diff --git a/aleksis/apps/untis/forms.py b/aleksis/apps/untis/forms.py
deleted file mode 100644
index 66a2f9d05fc92e1877634c867a695431cd6059e2..0000000000000000000000000000000000000000
--- a/aleksis/apps/untis/forms.py
+++ /dev/null
@@ -1,6 +0,0 @@
-from django import forms
-from django.utils.translation import gettext_lazy as _
-
-
-class UntisUploadForm(forms.Form):
-    untis_xml = forms.FileField(label=_("Untis XML export"))
diff --git a/aleksis/apps/untis/management/commands/untis_import_xml.py b/aleksis/apps/untis/management/commands/untis_import_xml.py
deleted file mode 100644
index efbaa675f26561d2961e856c0251086cb2956263..0000000000000000000000000000000000000000
--- a/aleksis/apps/untis/management/commands/untis_import_xml.py
+++ /dev/null
@@ -1,14 +0,0 @@
-from django.core.management.base import BaseCommand
-from django.utils.translation import ugettext as _
-
-from ...util.xml.xml import untis_import_xml
-
-
-class Command(BaseCommand):
-    def add_arguments(self, parser):
-        parser.add_argument("untis_xml_path", help=_("Path to Untis XML export file"))
-
-    def handle(self, *args, **options):
-        untis_xml = open(options["untis_xml_path"], "rb")
-
-        untis_import_xml(None, untis_xml)
diff --git a/aleksis/apps/untis/menus.py b/aleksis/apps/untis/menus.py
index f05d52f7451d3eb627a57f6f960c8da49de3921e..d9f723ff1ef69c8eb1eca61be17110d12da5cce8 100644
--- a/aleksis/apps/untis/menus.py
+++ b/aleksis/apps/untis/menus.py
@@ -2,13 +2,6 @@ from django.utils.translation import gettext_lazy as _
 
 MENUS = {
     "DATA_MANAGEMENT_MENU": [
-        {
-            "name": _("Untis XML import"),
-            "url": "untis_xml_import",
-            "validators": [
-                ("aleksis.core.util.predicates.permission_validator", "untis.do_xml_import_rule",),
-            ],
-        },
         {
             "name": _("Link subjects to groups (for UNTIS MySQL import)"),
             "url": "untis_groups_subjects",
diff --git a/aleksis/apps/untis/migrations/0001_initial.py b/aleksis/apps/untis/migrations/0001_initial.py
index dcdd6f43da756851d1ac4f3449132d1634c4349f..befeb5e61c5ef098ec02af3a268ce73f01756b89 100644
--- a/aleksis/apps/untis/migrations/0001_initial.py
+++ b/aleksis/apps/untis/migrations/0001_initial.py
@@ -2272,7 +2272,6 @@ class Migration(migrations.Migration):
             ],
             options={
                 "permissions": (
-                    ("do_xml_import", "Can do XML import"),
                     ("assign_subjects_to_groups", "Can assign subjects to groups"),
                 ),
                 "managed": False,
diff --git a/aleksis/apps/untis/migrations/0002_auto_20200820_1542.py b/aleksis/apps/untis/migrations/0002_auto_20200820_1542.py
index a3d79487be9872fa30e8e36f9ebf1ed2eb6ca310..eef0e28210a53a88217d3825bdb5c72238313d65 100644
--- a/aleksis/apps/untis/migrations/0002_auto_20200820_1542.py
+++ b/aleksis/apps/untis/migrations/0002_auto_20200820_1542.py
@@ -12,6 +12,6 @@ class Migration(migrations.Migration):
     operations = [
         migrations.AlterModelOptions(
             name='globalpermissions',
-            options={'managed': False, 'permissions': (('do_xml_import', 'Kann XML-Import durchführen'), ('assign_subjects_to_groups', 'Kann Fächer zu Gruppen zuzuordnen'))},
+            options={'managed': False, 'permissions': (('assign_subjects_to_groups', 'Kann Fächer zu Gruppen zuzuordnen'),)},
         ),
     ]
diff --git a/aleksis/apps/untis/models.py b/aleksis/apps/untis/models.py
index d14197965dc3839254117b55d2a2ac4bf12bf16f..4d1d23ac62b252e6549f857cb6187452fb1584df 100644
--- a/aleksis/apps/untis/models.py
+++ b/aleksis/apps/untis/models.py
@@ -4218,7 +4218,4 @@ class Views(models.Model, PureDjangoModel):
 class GlobalPermissions(models.Model, PureDjangoModel):
     class Meta:
         managed = False
-        permissions = (
-            ("do_xml_import", _("Can do XML import")),
-            ("assign_subjects_to_groups", _("Can assign subjects to groups")),
-        )
+        permissions = (("assign_subjects_to_groups", _("Can assign subjects to groups")),)
diff --git a/aleksis/apps/untis/rules.py b/aleksis/apps/untis/rules.py
index fc420c375e468c9043993ec1be6a52914c965c9e..cf7b59ca6d5caa9402291a83331925cad96e5372 100644
--- a/aleksis/apps/untis/rules.py
+++ b/aleksis/apps/untis/rules.py
@@ -2,12 +2,6 @@ from rules import add_perm
 
 from aleksis.core.util.predicates import has_global_perm, has_person
 
-# Do XML import
-do_xml_import_predicate = has_person & has_global_perm("untis.do_xml_import")
-add_perm("untis.do_xml_import_rule", do_xml_import_predicate)
-
-
-# Do XML import
 assign_subjects_to_groups_predicate = has_person & has_global_perm(
     "untis.assign_subjects_to_groups"
 )
diff --git a/aleksis/apps/untis/templates/untis/xml_import.html b/aleksis/apps/untis/templates/untis/xml_import.html
deleted file mode 100644
index 48af24b2606d5f7b00c60a4e655257d16c8e4547..0000000000000000000000000000000000000000
--- a/aleksis/apps/untis/templates/untis/xml_import.html
+++ /dev/null
@@ -1,43 +0,0 @@
-{# -*- engine:django -*- #}
-
-{% extends "core/base.html" %}
-
-{% load material_form i18n %}
-
-{% block browser_title %}{% blocktrans %}Import Untis data via XML{% endblocktrans %}{% endblock %}
-{% block page_title %}{% blocktrans %}Import Untis data via XML{% endblocktrans %}{% endblock %}
-
-{% block content %}
-
-  <p class="flow-text">
-    {% blocktrans %}
-      Untis provides a function for exporting all data as an XML file.
-    {% endblocktrans %}
-  </p>
-  <div class="alert warning">
-    <p>
-      <i class="material-icons left">warning</i>
-      {% blocktrans %}
-        Newly imported data will be valid as of tomorrow.
-      {% endblocktrans %}
-      {% blocktrans %}
-        The effective dates of all existing lessons will be set to end
-        today.
-      {% endblocktrans %}
-      {% blocktrans %}
-        The effective dates of all newly imported lessons will be set to
-        start tomorrow.
-      {% endblocktrans %}
-      {% blocktrans %}
-        Teachers, rooms, subjects and classes and periods will be updated in place.
-      {% endblocktrans %}
-    </p>
-  </div>
-
-  <form method="post" enctype="multipart/form-data">
-    {% csrf_token %}
-    {% form form=upload_form %}{% endform %}
-    {% include "core/partials/save_button.html" with icon="import_export" caption=_("Import data") %}
-  </form>
-
-{% endblock %}
diff --git a/aleksis/apps/untis/urls.py b/aleksis/apps/untis/urls.py
deleted file mode 100644
index 1a5119ea3c6195ab915588b2e3fd684891294b0b..0000000000000000000000000000000000000000
--- a/aleksis/apps/untis/urls.py
+++ /dev/null
@@ -1,7 +0,0 @@
-from django.urls import path
-
-from . import views
-
-urlpatterns = [
-    path("import/xml/", views.xml_import, name="untis_xml_import"),
-]
diff --git a/aleksis/apps/untis/util/xml/xml.py b/aleksis/apps/untis/util/xml/xml.py
deleted file mode 100644
index 076719b812966962fdf7ffe32a4b2bc6abfda194..0000000000000000000000000000000000000000
--- a/aleksis/apps/untis/util/xml/xml.py
+++ /dev/null
@@ -1,173 +0,0 @@
-from datetime import date, time, timedelta
-from typing import BinaryIO, Optional, Union
-from xml.dom import Node
-
-from django.http import HttpRequest
-from django.utils.translation import ugettext as _
-
-import defusedxml
-
-from aleksis.apps.chronos.models import Lesson, Room, Subject, TimePeriod
-from aleksis.core.models import Group, Person
-from aleksis.core.util import messages
-
-
-def get_child_node_text(node: Node, tag: str) -> Optional[str]:
-    tag_nodes = node.getElementsByTagName(tag)
-
-    if len(tag_nodes) == 1:
-        return tag_nodes[0].firstChild.nodeValue
-    else:
-        return None
-
-
-def get_child_node_id(node: Node, tag: str) -> Optional[str]:
-    tag_nodes = node.getElementsByTagName(tag)
-
-    if len(tag_nodes) == 1:
-        return tag_nodes[0].attributes["id"].value
-    else:
-        return None
-
-
-def untis_import_xml(request: HttpRequest, untis_xml: Union[BinaryIO, str]) -> None:
-    dom = defusedxml.parse(untis_xml)
-
-    subjects = dom.getElementsByTagName("subject")
-    for subject_node in subjects:
-        short_name = subject_node.attributes["id"].value[3:]
-        name = get_child_node_text(subject_node, "longname")
-        colour_fg = get_child_node_text(subject_node, "forecolor")
-        colour_bg = get_child_node_text(subject_node, "backcolor")
-
-        Subject.objects.select_related(None).prefetch_related(None).update_or_create(
-            short_name=short_name,
-            defaults={"name": name, "colour_fg": colour_fg, "colour_bg": colour_bg},
-        )
-
-    periods = dom.getElementsByTagName("timeperiod")
-    for period_node in periods:
-        weekday = int(get_child_node_text(period_node, "day"))
-        period = int(get_child_node_text(period_node, "period"))
-        starttime = get_child_node_text(period_node, "starttime")
-        endtime = get_child_node_text(period_node, "endtime")
-
-        time_start = time(int(starttime[:2]), int(starttime[2:]))
-        time_end = time(int(endtime[:2]), int(endtime[2:]))
-
-        TimePeriod.objects.select_related(None).prefetch_related(None).update_or_create(
-            weekday=weekday,
-            period=period,
-            defaults={"time_start": time_start, "time_end": time_end},
-        )
-
-    rooms = dom.getElementsByTagName("room")
-    for room_node in rooms:
-        short_name = room_node.attributes["id"].value[3:]
-        name = get_child_node_text(room_node, "longname")
-
-        Room.objects.select_related(None).prefetch_related(None).update_or_create(
-            short_name=short_name, defaults={"name": name}
-        )
-
-    classes = dom.getElementsByTagName("class")
-    for class_node in classes:
-        short_name = class_node.attributes["id"].value[3:]
-        name = _("Class %s") % short_name
-        class_teacher_short_name = get_child_node_id(class_node, "class_teacher")[3:]
-
-        class_, created = (
-            Group.objects.select_related(None)
-            .prefetch_related(None)
-            .update_or_create(short_name=short_name, defaults={"name": name})
-        )
-
-        try:
-            # Teachers need to come from another source, e.g. SchILD-NRW
-            class_.owners.set([Person.objects.get(short_name=class_teacher_short_name)])
-            class_.save()
-        except Person.DoesNotExist:
-            messages.warning(
-                request,
-                _("Could not set class teacher of %(class)s to %(teacher)s.")
-                % {"class": short_name, "teacher": class_teacher_short_name},
-            )
-
-    # Set all existing lessons that overlap to end today
-    today = date.today()
-    Lesson.objects.filter(date_end__gt=today).update(date_end=today)
-
-    lessons = dom.getElementsByTagName("lesson")
-    for lesson_node in lessons:
-        subject_short_name = get_child_node_id(lesson_node, "lesson_subject")[3:]
-        teacher_short_name = get_child_node_id(lesson_node, "lesson_teacher")[3:]
-        group_short_names = [
-            v.strip()
-            for v in get_child_node_id(lesson_node, "lesson_classes").split("CL_")
-            if v.strip()
-        ]
-        effectivebegindate = get_child_node_text(lesson_node, "effectivebegindate")
-        effectiveenddate = get_child_node_text(lesson_node, "effectiveenddate")
-
-        times = lesson_node.getElementsByTagName("time")
-        time_periods = []
-        for time_node in times:
-            day = int(get_child_node_text(time_node, "assigned_day"))
-            period = int(get_child_node_text(time_node, "assigned_period"))
-
-            room_id = get_child_node_id(time_node, "assigned_room")
-            room = room_id[3:] if room_id else None
-
-            time_periods.append((day, period, room))
-
-        subject = Subject.objects.get(short_name=subject_short_name)
-        periods = [
-            (
-                TimePeriod.objects.get(weekday=v[0], period=v[1]),
-                Room.objects.get(short_name=v[2]) if v[2] else None,
-            )
-            for v in time_periods
-        ]
-        date_start = (
-            date(
-                int(effectivebegindate[:4]),
-                int(effectivebegindate[4:6]),
-                int(effectivebegindate[6:]),
-            )
-            if effectivebegindate
-            else None
-        )
-        date_end = (
-            date(int(effectiveenddate[:4]), int(effectiveenddate[4:6]), int(effectiveenddate[6:]),)
-            if effectiveenddate
-            else None
-        )
-
-        # Coerce effective start date to not be before tomorrow
-        if date_start and date_start <= today:
-            date_start = today + timedelta(days=1)
-
-        try:
-            groups = [Group.objects.get(short_name=v) for v in group_short_names]
-        except Group.DoesNotExist:
-            messages.error(request, _("Invalid list of classes: %s") % ", ".join(group_short_names))
-            continue
-
-        try:
-            teachers = [Person.objects.get(short_name=teacher_short_name)]
-        except Person.DoesNotExist:
-            messages.error(
-                request,
-                _("Failed to import lesson: Teacher %s does not exist.") % teacher_short_name,
-            )
-            continue
-
-        lesson = Lesson.objects.create(subject=subject, date_start=date_start, date_end=date_end)
-
-        lesson.groups.set(groups)
-        lesson.teachers.set(teachers)
-
-        for period in periods:
-            lesson.periods.add(period[0], through_defaults={"room": period[1]})
-
-        lesson.save()
diff --git a/aleksis/apps/untis/views.py b/aleksis/apps/untis/views.py
deleted file mode 100644
index 84b2a2d8b2505ef04d4a528b627b05e8eddbd3c2..0000000000000000000000000000000000000000
--- a/aleksis/apps/untis/views.py
+++ /dev/null
@@ -1,24 +0,0 @@
-from django.http import HttpRequest, HttpResponse
-from django.shortcuts import render
-
-from rules.contrib.views import permission_required
-
-from .forms import UntisUploadForm
-from .util.xml.xml import untis_import_xml
-
-
-@permission_required("untis.do_xml_import_rule")
-def xml_import(request: HttpRequest) -> HttpResponse:
-    context = {}
-
-    upload_form = UntisUploadForm()
-
-    if request.method == "POST":
-        upload_form = UntisUploadForm(request.POST, request.FILES)
-
-        if upload_form.is_valid():
-            untis_import_xml(request, request.FILES["untis_xml"])
-
-    context["upload_form"] = upload_form
-
-    return render(request, "untis/xml_import.html", context)