diff --git a/aleksis/apps/alsijil/actions.py b/aleksis/apps/alsijil/actions.py
new file mode 100644
index 0000000000000000000000000000000000000000..8c672da4f5c8ae70ced965e45d11672d34c688a8
--- /dev/null
+++ b/aleksis/apps/alsijil/actions.py
@@ -0,0 +1,54 @@
+from typing import Sequence
+
+from django.contrib import messages
+from django.contrib.humanize.templatetags.humanize import apnumber
+from django.http import HttpRequest
+from django.template.loader import get_template
+from django.urls import reverse
+from django.utils.translation import gettext_lazy as _
+
+from aleksis.core.models import Notification
+
+
+def send_request_to_check_entry(modeladmin, request: HttpRequest, selected_items: Sequence[dict]):
+    """Send notifications to the teachers of the selected register objects.
+
+    Action for use with ``RegisterObjectTable`` and ``RegisterObjectActionForm``.
+    """
+    # Group class register entries by teachers so each teacher gets just one notification
+    grouped_by_teachers = {}
+    for entry in selected_items:
+        teachers = entry["register_object"].get_teachers().all()
+        for teacher in teachers:
+            grouped_by_teachers.setdefault(teacher, [])
+            grouped_by_teachers[teacher].append(entry)
+
+    template = get_template("alsijil/notifications/check.html")
+    for teacher, items in grouped_by_teachers.items():
+        msg = template.render({"items": items})
+
+        title = _("{} wants you to check some class register entries.").format(
+            request.user.person.addressing_name
+        )
+
+        n = Notification(
+            title=title,
+            description=msg,
+            sender=request.user.person.addressing_name,
+            recipient=teacher,
+            link=request.build_absolute_uri(reverse("overview_me")),
+        )
+        n.save()
+
+    count_teachers = len(grouped_by_teachers.keys())
+    count_items = len(selected_items)
+    messages.success(
+        request,
+        _(
+            "We have successfully sent notifications to "
+            "{count_teachers} persons for {count_items} lessons."
+        ).format(count_teachers=apnumber(count_teachers), count_items=apnumber(count_items)),
+    )
+
+
+send_request_to_check_entry.short_description = _("Notify teacher to check data")
diff --git a/aleksis/apps/alsijil/forms.py b/aleksis/apps/alsijil/forms.py
index 228e825bae4e1170a3327629b6c8a973c192f7c0..72d3d20275c9c9cfec0909a418940862ce9d6491 100644
--- a/aleksis/apps/alsijil/forms.py
+++ b/aleksis/apps/alsijil/forms.py
@@ -1,8 +1,11 @@
-from datetime import datetime
+from datetime import datetime, timedelta
+from typing import Optional, Sequence
 
 from django import forms
 from django.core.exceptions import ValidationError
 from django.db.models import Count, Q
+from django.http import HttpRequest
+from django.utils import timezone
 from django.utils.translation import gettext_lazy as _
 
 from django_select2.forms import ModelSelect2MultipleWidget, ModelSelect2Widget, Select2Widget
@@ -10,11 +13,13 @@ from guardian.shortcuts import get_objects_for_user
 from material import Fieldset, Layout, Row
 
 from aleksis.apps.chronos.managers import TimetableType
-from aleksis.apps.chronos.models import TimePeriod
-from aleksis.core.models import Group, Person
+from aleksis.apps.chronos.models import Subject, TimePeriod
+from aleksis.core.forms import ListActionForm
+from aleksis.core.models import Group, Person, SchoolTerm
 from aleksis.core.util.core_helpers import get_site_preferences
 from aleksis.core.util.predicates import check_global_permission
 
+from .actions import send_request_to_check_entry
 from .models import (
     ExcuseType,
     ExtraMark,
@@ -254,3 +259,66 @@ class GroupRoleAssignmentEditForm(forms.ModelForm):
     class Meta:
         model = GroupRoleAssignment
         fields = ["date_start", "date_end"]
+
+
+class FilterRegisterObjectForm(forms.Form):
+    """Form for filtering register objects in ``RegisterObjectTable``."""
+
+    layout = Layout(
+        Row("school_term", "date_start", "date_end"), Row("has_documentation", "group", "subject")
+    )
+    school_term = forms.ModelChoiceField(queryset=None, label=_("School term"))
+    has_documentation = forms.NullBooleanField(label=_("Has lesson documentation"))
+    group = forms.ModelChoiceField(queryset=None, label=_("Group"), required=False)
+    subject = forms.ModelChoiceField(queryset=None, label=_("Subject"), required=False)
+    date_start = forms.DateField(label=_("Start date"))
+    date_end = forms.DateField(label=_("End date"))
+
+    @classmethod
+    def get_initial(cls):
+        date_end = timezone.now().date()
+        date_start = date_end - timedelta(days=30)
+        return {
+            "school_term": SchoolTerm.current,
+            "date_start": date_start,
+            "date_end": date_end,
+        }
+
+    def __init__(
+        self,
+        request: HttpRequest,
+        *args,
+        for_person: bool = True,
+        groups: Optional[Sequence[Group]] = None,
+        **kwargs
+    ):
+        self.request = request
+        person = self.request.user.person
+
+        kwargs["initial"] = self.get_initial()
+        super().__init__(*args, **kwargs)
+
+        self.fields["school_term"].queryset = SchoolTerm.objects.all()
+
+        if not groups and for_person:
+            groups = Group.objects.filter(
+                Q(lessons__teachers=person)
+                | Q(lessons__lesson_periods__substitutions__teachers=person)
+                | Q(events__teachers=person)
+                | Q(extra_lessons__teachers=person)
+            )
+        elif not for_person:
+            groups = Group.objects.all()
+        self.fields["group"].queryset = groups
+
+        # Filter subjects by selectable groups
+        subject_qs = Subject.objects.filter(
+            Q(lessons__groups__in=groups) | Q(extra_lessons__groups__in=groups)
+        ).distinct()
+        self.fields["subject"].queryset = subject_qs
+
+
+class RegisterObjectActionForm(ListActionForm):
+    """Action form for managing register objects for use with ``RegisterObjectTable``."""
+
+    actions = [send_request_to_check_entry]
diff --git a/aleksis/apps/alsijil/menus.py b/aleksis/apps/alsijil/menus.py
index a7e6c8407ba49cf537bc728ffe2794a2a9bb01db..fe720477001be6e5992a71ac62b66fad00186780 100644
--- a/aleksis/apps/alsijil/menus.py
+++ b/aleksis/apps/alsijil/menus.py
@@ -78,6 +78,17 @@ MENUS = {
                         ),
                     ],
                 },
+                {
+                    "name": _("All lessons"),
+                    "url": "all_register_objects",
+                    "icon": "list",
+                    "validators": [
+                        (
+                            "aleksis.core.util.predicates.permission_validator",
+                            "alsijil.view_register_objects_list",
+                        ),
+                    ],
+                },
                 {
                     "name": _("Excuse types"),
                     "url": "excuse_types",
diff --git a/aleksis/apps/alsijil/preferences.py b/aleksis/apps/alsijil/preferences.py
index 6ebcb2ae2ecae7f796c67a00c36dd6e0aafcceba..3ad751865f0534a156fd8ea7f9bfc0b558a467f8 100644
--- a/aleksis/apps/alsijil/preferences.py
+++ b/aleksis/apps/alsijil/preferences.py
@@ -1,7 +1,8 @@
+from django.core.exceptions import ValidationError
 from django.utils.translation import gettext as _
 
 from dynamic_preferences.preferences import Section
-from dynamic_preferences.types import BooleanPreference
+from dynamic_preferences.types import BooleanPreference, IntegerPreference
 
 from aleksis.core.registries import person_preferences_registry, site_preferences_registry
 
@@ -109,3 +110,17 @@ class ShowGroupRolesInLessonView(BooleanPreference):
     name = "group_roles_in_lesson_view"
     default = True
     verbose_name = _("Show assigned group roles in lesson view")
+
+
+@person_preferences_registry.register
+class RegisterObjectsTableItemsPerPage(IntegerPreference):
+    """Preference how many items are shown per page in ``RegisterObjectTable``."""
+
+    section = alsijil
+    name = "register_objects_table_items_per_page"
+    default = 100
+    verbose_name = _("Items per page in lessons table")
+
+    def validate(self, value):
+        if value < 1:
+            raise ValidationError(_("Each page must show at least one item."))
diff --git a/aleksis/apps/alsijil/rules.py b/aleksis/apps/alsijil/rules.py
index 72ddda20ce1eca248ff51030ae5d8cb44504d69d..1a8a06adc1033d690d0b20cfbbe92869621767bc 100644
--- a/aleksis/apps/alsijil/rules.py
+++ b/aleksis/apps/alsijil/rules.py
@@ -1,6 +1,8 @@
 from rules import add_perm
 
+from aleksis.core.models import Group
 from aleksis.core.util.predicates import (
+    has_any_object,
     has_global_perm,
     has_object_perm,
     has_person,
@@ -299,3 +301,9 @@ delete_group_role_assignment_predicate = (
     has_global_perm("alsjil.assign_grouprole") | is_group_role_assignment_group_owner
 )
 add_perm("alsijil.delete_grouproleassignment", delete_group_role_assignment_predicate)
+
+view_register_objects_list_predicate = has_person & (
+    has_any_object("core.view_full_register_group", Group)
+    | has_global_perm("core.view_full_register")
+)
+add_perm("alsijil.view_register_objects_list", view_register_objects_list_predicate)
diff --git a/aleksis/apps/alsijil/tables.py b/aleksis/apps/alsijil/tables.py
index c3835e973164f5a6cd3b50540fa7b9f5a3d4ea43..336ca5827932b81fab534ab277ee5e2dab0aad6d 100644
--- a/aleksis/apps/alsijil/tables.py
+++ b/aleksis/apps/alsijil/tables.py
@@ -4,6 +4,8 @@ from django.utils.translation import gettext_lazy as _
 import django_tables2 as tables
 from django_tables2.utils import A
 
+from aleksis.core.util.tables import SelectColumn
+
 
 class ExtraMarkTable(tables.Table):
     class Meta:
@@ -78,3 +80,51 @@ class GroupRoleTable(tables.Table):
             self.columns.hide("edit")
         if not request.user.has_perm("alsijil.delete_grouprole"):
             self.columns.hide("delete")
+
+
+def _get_link(value, record):
+    return record["register_object"].get_alsijil_url(record.get("week"))
+
+
+class RegisterObjectTable(tables.Table):
+    """Table to show all register objects in an overview.
+
+    .. warning::
+        Works only with ``generate_list_of_all_register_objects``.
+    """
+
+    class Meta:
+        attrs = {"class": "highlight responsive-table"}
+
+    status = tables.Column(accessor="register_object")
+    date = tables.Column(order_by="date_sort", linkify=_get_link)
+    period = tables.Column(order_by="period_sort", linkify=_get_link)
+    groups = tables.Column(linkify=_get_link)
+    teachers = tables.Column(linkify=_get_link)
+    subject = tables.Column(linkify=_get_link)
+    topic = tables.Column(linkify=_get_link)
+    homework = tables.Column(linkify=_get_link)
+    group_note = tables.Column(linkify=_get_link)
+
+    def render_status(self, value, record):
+        return render_to_string(
+            "alsijil/partials/lesson_status_icon.html",
+            dict(
+                week=record.get("week"),
+                has_documentation=record.get("has_documentation", False),
+                substitution=record.get("substitution"),
+                register_object=value,
+            ),
+        )
+
+
+class RegisterObjectSelectTable(RegisterObjectTable):
+    """Table to show all register objects with multi-select support.
+
+    More information at ``RegisterObjectTable``
+    """
+
+    selected = SelectColumn()
+
+    class Meta(RegisterObjectTable.Meta):
+        sequence = ("selected", "...")
diff --git a/aleksis/apps/alsijil/templates/alsijil/class_register/all_objects.html b/aleksis/apps/alsijil/templates/alsijil/class_register/all_objects.html
new file mode 100644
index 0000000000000000000000000000000000000000..b5e57e0ee276ab395ebd824327c7584a063dba63
--- /dev/null
+++ b/aleksis/apps/alsijil/templates/alsijil/class_register/all_objects.html
@@ -0,0 +1,19 @@
+{# -*- engine:django -*- #}
+{% extends "core/base.html" %}
+{% load i18n rules static django_tables2 material_form %}
+
+{% block browser_title %}{% blocktrans %}All lessons{% endblocktrans %}{% endblock %}
+
+{% block page_title %}
+  {% blocktrans %}All lessons{% endblocktrans %}
+{% endblock %}
+
+{% block extra_head %}
+  {{ block.super }}
+  <link rel="stylesheet" href="{% static 'css/alsijil/alsijil.css' %}"/>
+{% endblock %}
+
+{% block content %}
+  {% include "alsijil/partials/objects_table.html" %}
+  <script src="{% static "js/multi_select.js" %}"></script>
+{% endblock %}
diff --git a/aleksis/apps/alsijil/templates/alsijil/class_register/person.html b/aleksis/apps/alsijil/templates/alsijil/class_register/person.html
index 7254e0e24be0f979f9fa1f01a05a0b4c8f10e532..96729befb2851ba6194c29ce1b1280d04033d6bd 100644
--- a/aleksis/apps/alsijil/templates/alsijil/class_register/person.html
+++ b/aleksis/apps/alsijil/templates/alsijil/class_register/person.html
@@ -1,9 +1,6 @@
 {# -*- engine:django -*- #}
 {% extends "core/base.html" %}
-{% load rules %}
-{% load data_helpers %}
-{% load week_helpers %}
-{% load i18n %}
+{% load rules data_helpers week_helpers i18n material_form django_tables2 %}
 
 {% block browser_title %}{% blocktrans %}Class register: person{% endblocktrans %}{% endblock %}
 
@@ -12,7 +9,7 @@
   {% has_perm "alsijil.view_my_students" user as has_students %}
   {% if has_students %}
     <a href="{% url "my_students" %}"
-    class="btn-flat primary-color-text waves-light waves-effect">
+       class="btn-flat primary-color-text waves-light waves-effect">
       <i class="material-icons left">chevron_left</i> {% trans "Back" %}
     </a>
   {% endif %}
@@ -22,249 +19,271 @@
 {% endblock %}
 
 {% block content %}
-  {% has_perm "alsijil.edit_person_overview_personalnote" user person as can_mark_all_as_excused %}
-  {% has_perm "alsijil.register_absence" user person as can_register_absence %}
-  {% if can_register_absence %}
-    <a class="btn primary-color waves-effect waves-light" href="{% url "register_absence" person.pk %}">
-      <i class="material-icons left">rate_review</i>
-      {% trans "Register absence" %}
-    </a>
-  {% endif %}
-
   <div class="row">
-  <div class="col s12 m12 l6">
-    <h5>{% trans "Unexcused absences" %}</h5>
-
-    <ul class="collection">
-      {% for note in unexcused_absences %}
-        <li class="collection-item">
-          {% has_perm "alsijil.edit_personalnote" user note as can_edit_personal_note %}
-          {% if can_edit_personal_note %}
-            <form action="" method="post" class="right hide-on-small-only" style="margin-top: -7px;">
-              {% csrf_token %}
-              {% trans "Mark as" %}
-              <input type="hidden" value="{{ note.pk }}" name="personal_note">
-              {% include "alsijil/partials/mark_as_buttons.html" %}
-              <a class="btn-flat red-text" title="{% trans "Delete note" %}"
-                 href="{% url "delete_personal_note" note.pk %}">
-                <i class="material-icons center">cancel</i>
-              </a>
-            </form>
-          {% endif %}
-          <i class="material-icons left red-text">warning</i>
-          <p class="no-margin">
-            <a href="{{ note.get_absolute_url }}">{{ note.date }}, {{ note.lesson_period }}</a>
-          </p>
-          {% if note.remarks %}
-            <p class="no-margin"><em>{{ note.remarks }}</em></p>
-          {% endif %}
-          {% if can_edit_personal_note %}
-            <form action="" method="post" class="hide-on-med-and-up">
-              {% csrf_token %}
-              {% trans "Mark as" %}
-              <input type="hidden" value="{{ note.pk }}" name="personal_note">
-              {% include "alsijil/partials/mark_as_buttons.html" %}
-              <a class="btn-flat red-text" title="{% trans "Delete note" %}"
-                 href="{% url "delete_personal_note" note.pk %}">
-                <i class="material-icons center">cancel</i>
-              </a>
-            </form>
-          {% endif %}
-        </li>
-      {% empty %}
-        <li class="collection-item avatar valign-wrapper">
-          <i class="material-icons left materialize-circle green white-text">check</i>
-          <span class="title">{% trans "There are no unexcused lessons." %}</span>
+  <div class="col s12">
+    <ul class="tabs">
+      {% if register_object_table %}
+        <li class="tab">
+          <a href="#lesson-documentations">{% trans "Lesson documentations" %}</a>
         </li>
-      {% endfor %}
+      {% endif %}
+      <li class="tab">
+        <a href="#personal-notes">{% trans "Personal notes" %}</a>
+      </li>
     </ul>
-    {% if stats %}
-      <h5>{% trans "Statistics on absences, tardiness and remarks" %}</h5>
-      <ul class="collapsible">
-        {% for school_term, stat in stats %}
-          <li {% if forloop.first %}class="active"{% endif %}>
-            <div class="collapsible-header">
-              <i class="material-icons">date_range</i>{{ school_term }}</div>
-            <div class="collapsible-body">
-              <table>
-                <tr>
-                  <th colspan="2">{% trans 'Absences' %}</th>
-                  <td>{{ stat.absences_count }}</td>
-                </tr>
-                <tr>
-                  <td rowspan="{{ excuse_types.count|add:2 }}" class="hide-on-small-only">{% trans "thereof" %}</td>
-                  <td rowspan="{{ excuse_types.count|add:2 }}" class="hide-on-med-and-up"></td>
-                  <th class="truncate">{% trans 'Excused' %}</th>
-                  <td>{{ stat.excused }}</td>
-                </tr>
-                {% for excuse_type in excuse_types %}
-                  <th>{{ excuse_type.name }}</th>
-                  <td>{{ stat|get_dict:excuse_type.count_label }}</td>
-                {% endfor %}
-                <tr>
-                  <th>{% trans 'Unexcused' %}</th>
-                  <td>{{ stat.unexcused }}</td>
-                </tr>
-                <tr>
-                  <th colspan="2">{% trans 'Tardiness' %}</th>
-                  <td>{{ stat.tardiness }}'/{{ stat.tardiness_count }} &times;</td>
-                </tr>
-                {% for extra_mark in extra_marks %}
-                  <tr>
-                    <th colspan="2">{{ extra_mark.name }}</th>
-                    <td>{{ stat|get_dict:extra_mark.count_label }}</td>
-                  </tr>
-                {% endfor %}
-              </table>
-            </div>
-          </li>
-        {% endfor %}
-      </ul>
-    {% endif %}
   </div>
-  <div class="col s12 m12 l6">
-    <h5>{% trans "Relevant personal notes" %}</h5>
-    <ul class="collapsible">
-      <li>
-        <div>
-          <ul>
-            {% for note in personal_notes %}
-              {% ifchanged note.school_term %}</ul></div></li>
-                <li {% if forloop.first %}class="active"{% endif %}>
-                <div class="collapsible-header"><i
-                    class="material-icons">date_range</i>{{ note.school_term }}</div>
-                <div class="collapsible-body">
-                <ul class="collection">
-              {% endifchanged %}
-
-              {% ifchanged note.week %}
-                <li class="collection-item">
-                  <strong>{% blocktrans with week=note.calendar_week.week %}Week {{ week }}{% endblocktrans %}</strong>
-                </li>
-              {% endifchanged %}
-              {% ifchanged note.date %}
-                <li class="collection-item">
-                  {% if can_mark_all_as_excused and note.date %}
-                    <form action="" method="post" class="right hide-on-small-only" style="margin-top: -7px;">
-                      {% csrf_token %}
-                      {% trans "Mark all as" %}
-                      <input type="hidden" value="{{ note.date|date:"Y-m-d" }}" name="date">
-                      {% include "alsijil/partials/mark_as_buttons.html" %}
-                    </form>
-                  {% endif %}
-                  <i class="material-icons left">schedule</i>
+  {% if register_object_table %}
+    <div class="col s12" id="lesson-documentations">
+      {% include "alsijil/partials/objects_table.html" with table=register_object_table filter_form=filter_form %}
+    </div>
+  {% endif %}
+  <div class="col s12" id="personal-notes">
+    {% has_perm "alsijil.edit_person_overview_personalnote" user person as can_mark_all_as_excused %}
+    {% has_perm "alsijil.register_absence" user person as can_register_absence %}
+    {% if can_register_absence %}
+      <a class="btn primary-color waves-effect waves-light" href="{% url "register_absence" person.pk %}">
+        <i class="material-icons left">rate_review</i>
+        {% trans "Register absence" %}
+      </a>
+    {% endif %}
 
-                  {% if note.date %}
-                    {{ note.date }}
-                  {% else %}
-                    {{ note.register_object.date_start }}
-                    {{ note.register_object.period_from.period }}.–{{ note.register_object.date_end }}
-                    {{ note.register_object.period_to.period }}.
-                  {% endif %}
+    <div class="row">
+      <div class="col s12 m12 l6">
+        <h5>{% trans "Unexcused absences" %}</h5>
 
-                  {% if can_mark_all_as_excused and note.date %}
-                    <form action="" method="post" class="hide-on-med-and-up">
-                      {% csrf_token %}
-                      {% trans "Mark all as" %}
-                      <input type="hidden" value="{{ note.date|date:"Y-m-d" }}" name="date">
-                      {% include "alsijil/partials/mark_as_buttons.html" %}
-                    </form>
-                  {% endif %}
-                </li>
-              {% endifchanged %}
+        <ul class="collection">
+          {% for note in unexcused_absences %}
+            <li class="collection-item">
+              {% has_perm "alsijil.edit_personalnote" user note as can_edit_personal_note %}
+              {% if can_edit_personal_note %}
+                <form action="" method="post" class="right hide-on-small-only" style="margin-top: -7px;">
+                  {% csrf_token %}
+                  {% trans "Mark as" %}
+                  <input type="hidden" value="{{ note.pk }}" name="personal_note">
+                  {% include "alsijil/partials/mark_as_buttons.html" %}
+                  <a class="btn-flat red-text" title="{% trans "Delete note" %}"
+                     href="{% url "delete_personal_note" note.pk %}">
+                    <i class="material-icons center">cancel</i>
+                  </a>
+                </form>
+              {% endif %}
+              <i class="material-icons left red-text">warning</i>
+              <p class="no-margin">
+                <a href="{{ note.get_absolute_url }}">{{ note.date }}, {{ note.lesson_period }}</a>
+              </p>
+              {% if note.remarks %}
+                <p class="no-margin"><em>{{ note.remarks }}</em></p>
+              {% endif %}
+              {% if can_edit_personal_note %}
+                <form action="" method="post" class="hide-on-med-and-up">
+                  {% csrf_token %}
+                  {% trans "Mark as" %}
+                  <input type="hidden" value="{{ note.pk }}" name="personal_note">
+                  {% include "alsijil/partials/mark_as_buttons.html" %}
+                  <a class="btn-flat red-text" title="{% trans "Delete note" %}"
+                     href="{% url "delete_personal_note" note.pk %}">
+                    <i class="material-icons center">cancel</i>
+                  </a>
+                </form>
+              {% endif %}
+            </li>
+            {% empty %}
+            <li class="collection-item avatar valign-wrapper">
+              <i class="material-icons left materialize-circle green white-text">check</i>
+              <span class="title">{% trans "There are no unexcused lessons." %}</span>
+            </li>
+          {% endfor %}
+        </ul>
+        {% if stats %}
+          <h5>{% trans "Statistics on absences, tardiness and remarks" %}</h5>
+          <ul class="collapsible">
+            {% for school_term, stat in stats %}
+              <li {% if forloop.first %}class="active"{% endif %}>
+                <div class="collapsible-header">
+                  <i class="material-icons">date_range</i>{{ school_term }}</div>
+                <div class="collapsible-body">
+                  <table>
+                    <tr>
+                      <th colspan="2">{% trans 'Absences' %}</th>
+                      <td>{{ stat.absences_count }}</td>
+                    </tr>
+                    <tr>
+                      <td rowspan="{{ excuse_types.count|add:2 }}" class="hide-on-small-only">{% trans "thereof" %}</td>
+                      <td rowspan="{{ excuse_types.count|add:2 }}" class="hide-on-med-and-up"></td>
+                      <th class="truncate">{% trans 'Excused' %}</th>
+                      <td>{{ stat.excused }}</td>
+                    </tr>
+                    {% for excuse_type in excuse_types %}
+                      <th>{{ excuse_type.name }}</th>
+                      <td>{{ stat|get_dict:excuse_type.count_label }}</td>
+                    {% endfor %}
+                    <tr>
+                      <th>{% trans 'Unexcused' %}</th>
+                      <td>{{ stat.unexcused }}</td>
+                    </tr>
+                    <tr>
+                      <th colspan="2">{% trans 'Tardiness' %}</th>
+                      <td>{{ stat.tardiness }}'/{{ stat.tardiness_count }} &times;</td>
+                    </tr>
+                    {% for extra_mark in extra_marks %}
+                      <tr>
+                        <th colspan="2">{{ extra_mark.name }}</th>
+                        <td>{{ stat|get_dict:extra_mark.count_label }}</td>
+                      </tr>
+                    {% endfor %}
+                  </table>
+                </div>
+              </li>
+            {% endfor %}
+          </ul>
+        {% endif %}
+      </div>
+      <div class="col s12 m12 l6">
+        <h5>{% trans "Relevant personal notes" %}</h5>
+        <ul class="collapsible">
+          <li>
+            <div>
+              <ul>
+                {% for note in personal_notes %}
+                  {% ifchanged note.school_term %}</ul></div></li>
+                    <li {% if forloop.first %}class="active"{% endif %}>
+                    <div class="collapsible-header"><i
+                        class="material-icons">date_range</i>{{ note.school_term }}</div>
+                    <div class="collapsible-body">
+                    <ul class="collection">
+                  {% endifchanged %}
 
-              <li class="collection-item">
-                <div class="row no-margin">
-                  <div class="col s2 m1">
-                    {% if note.register_object.period %}
-                      {{ note.register_object.period.period }}.
-                    {% endif %}
-                  </div>
+                  {% ifchanged note.week %}
+                    <li class="collection-item">
+                      <strong>{% blocktrans with week=note.calendar_week.week %}Week
+                        {{ week }}{% endblocktrans %}</strong>
+                    </li>
+                  {% endifchanged %}
+                  {% ifchanged note.date %}
+                    <li class="collection-item">
+                      {% if can_mark_all_as_excused and note.date %}
+                        <form action="" method="post" class="right hide-on-small-only" style="margin-top: -7px;">
+                          {% csrf_token %}
+                          {% trans "Mark all as" %}
+                          <input type="hidden" value="{{ note.date|date:"Y-m-d" }}" name="date">
+                          {% include "alsijil/partials/mark_as_buttons.html" %}
+                        </form>
+                      {% endif %}
+                      <i class="material-icons left">schedule</i>
 
-                  <div class="col s10 m4">
-                    <i class="material-icons left">event_note</i>
-                    <a href="{{ note.get_absolute_url }}">
-                      {% if note.register_object.get_subject %}
-                        {{ note.register_object.get_subject.name }}
+                      {% if note.date %}
+                        {{ note.date }}
                       {% else %}
-                        {% trans "Event" %} ({{ note.register_object.title }})
-                      {% endif %}<br/>
-                      {{ note.register_object.teacher_names }}
-                    </a>
-                  </div>
+                        {{ note.register_object.date_start }}
+                        {{ note.register_object.period_from.period }}.–{{ note.register_object.date_end }}
+                        {{ note.register_object.period_to.period }}.
+                      {% endif %}
 
-                  <div class="col s12 m7 no-padding">
-                    {% has_perm "alsijil.edit_personalnote" user note as can_edit_personal_note %}
-                    {% if note.absent and not note.excused and can_edit_personal_note %}
-                      <form action="" method="post" class="right hide-on-small-only" style="margin-top: -7px;">
-                        {% csrf_token %}
-                        {% trans "Mark as" %}
-                        <input type="hidden" value="{{ note.pk }}" name="personal_note">
-                        {% include "alsijil/partials/mark_as_buttons.html" %}
-                        <a class="btn-flat red-text" title="{% trans "Delete note" %}"
-                           href="{% url "delete_personal_note" note.pk %}">
-                          <i class="material-icons center">cancel</i>
-                        </a>
-                      </form>
-                    {% elif can_edit_personal_note %}
-                      <a class="btn-flat red-text right hide-on-small-only" title="{% trans "Delete note" %}"
-                         href="{% url "delete_personal_note" note.pk %}">
-                        <i class="material-icons center">cancel</i>
-                      </a>
-                    {% endif %}
+                      {% if can_mark_all_as_excused and note.date %}
+                        <form action="" method="post" class="hide-on-med-and-up">
+                          {% csrf_token %}
+                          {% trans "Mark all as" %}
+                          <input type="hidden" value="{{ note.date|date:"Y-m-d" }}" name="date">
+                          {% include "alsijil/partials/mark_as_buttons.html" %}
+                        </form>
+                      {% endif %}
+                    </li>
+                  {% endifchanged %}
 
-                    {% if note.absent %}
-                      <div class="chip red white-text">
-                        {% trans 'Absent' %}
-                      </div>
-                    {% endif %}
-                    {% if note.excused %}
-                      <div class="chip green white-text">
-                        {% if note.excuse_type %}
-                          {{ note.excuse_type.name }}
-                        {% else %}
-                          {% trans 'Excused' %}
+                  <li class="collection-item">
+                    <div class="row no-margin">
+                      <div class="col s2 m1">
+                        {% if note.register_object.period %}
+                          {{ note.register_object.period.period }}.
                         {% endif %}
                       </div>
-                    {% endif %}
 
-                    {% if note.late %}
-                      <div class="chip orange white-text">
-                        {% blocktrans with late=note.late %}{{ late }}' late{% endblocktrans %}
+                      <div class="col s10 m4">
+                        <i class="material-icons left">event_note</i>
+                        <a href="{{ note.get_absolute_url }}">
+                          {% if note.register_object.get_subject %}
+                            {{ note.register_object.get_subject.name }}
+                          {% else %}
+                            {% trans "Event" %} ({{ note.register_object.title }})
+                          {% endif %}<br/>
+                          {{ note.register_object.teacher_names }}
+                        </a>
                       </div>
-                    {% endif %}
 
-                    {% for extra_mark in note.extra_marks.all %}
-                      <div class="chip">{{ extra_mark.name }}</div>
-                    {% endfor %}
+                      <div class="col s12 m7 no-padding">
+                        {% has_perm "alsijil.edit_personalnote" user note as can_edit_personal_note %}
+                        {% if note.absent and not note.excused and can_edit_personal_note %}
+                          <form action="" method="post" class="right hide-on-small-only" style="margin-top: -7px;">
+                            {% csrf_token %}
+                            {% trans "Mark as" %}
+                            <input type="hidden" value="{{ note.pk }}" name="personal_note">
+                            {% include "alsijil/partials/mark_as_buttons.html" %}
+                            <a class="btn-flat red-text" title="{% trans "Delete note" %}"
+                               href="{% url "delete_personal_note" note.pk %}">
+                              <i class="material-icons center">cancel</i>
+                            </a>
+                          </form>
+                          {% elif can_edit_personal_note %}
+                          <a class="btn-flat red-text right hide-on-small-only" title="{% trans "Delete note" %}"
+                             href="{% url "delete_personal_note" note.pk %}">
+                            <i class="material-icons center">cancel</i>
+                          </a>
+                        {% endif %}
 
-                    <em>{{ note.remarks }}</em>
+                        {% if note.absent %}
+                          <div class="chip red white-text">
+                            {% trans 'Absent' %}
+                          </div>
+                        {% endif %}
+                        {% if note.excused %}
+                          <div class="chip green white-text">
+                            {% if note.excuse_type %}
+                              {{ note.excuse_type.name }}
+                            {% else %}
+                              {% trans 'Excused' %}
+                            {% endif %}
+                          </div>
+                        {% endif %}
 
-                  </div>
-                  <div class="col s12 hide-on-med-and-up">
-                    {% if note.absent and not note.excused and can_edit_personal_note %}
-                      <form action="" method="post">
-                        {% csrf_token %}
-                        {% trans "Mark as" %}
-                        <input type="hidden" value="{{ note.pk }}" name="personal_note">
-                        {% include "alsijil/partials/mark_as_buttons.html" %}
-                        <a class="btn-flat red-text" title="{% trans "Delete note" %}"
-                           href="{% url "delete_personal_note" note.pk %}">
-                          <i class="material-icons center">cancel</i>
-                        </a>
-                      </form>
-                    {% elif can_edit_personal_note %}
-                      <a class="btn-flat red-text" title="{% trans "Delete note" %}"
-                         href="{% url "delete_personal_note" note.pk %}">
-                        <i class="material-icons left">cancel</i>
-                        {% trans "Delete" %}
-                      </a>
-                    {% endif %}
-                  </div>
-              </li>
-            {% endfor %}
-            </li>
-            </ul>
-            </div>
+                        {% if note.late %}
+                          <div class="chip orange white-text">
+                            {% blocktrans with late=note.late %}{{ late }}' late{% endblocktrans %}
+                          </div>
+                        {% endif %}
+
+                        {% for extra_mark in note.extra_marks.all %}
+                          <div class="chip">{{ extra_mark.name }}</div>
+                        {% endfor %}
+
+                        <em>{{ note.remarks }}</em>
+
+                      </div>
+                      <div class="col s12 hide-on-med-and-up">
+                        {% if note.absent and not note.excused and can_edit_personal_note %}
+                          <form action="" method="post">
+                            {% csrf_token %}
+                            {% trans "Mark as" %}
+                            <input type="hidden" value="{{ note.pk }}" name="personal_note">
+                            {% include "alsijil/partials/mark_as_buttons.html" %}
+                            <a class="btn-flat red-text" title="{% trans "Delete note" %}"
+                               href="{% url "delete_personal_note" note.pk %}">
+                              <i class="material-icons center">cancel</i>
+                            </a>
+                          </form>
+                          {% elif can_edit_personal_note %}
+                          <a class="btn-flat red-text" title="{% trans "Delete note" %}"
+                             href="{% url "delete_personal_note" note.pk %}">
+                            <i class="material-icons left">cancel</i>
+                            {% trans "Delete" %}
+                          </a>
+                        {% endif %}
+                      </div>
+                  </li>
+                {% endfor %}
+                </li>
+                </ul>
+                </div>
+      </div>
+    </div>
   </div>
 {% endblock %}
diff --git a/aleksis/apps/alsijil/templates/alsijil/notifications/check.html b/aleksis/apps/alsijil/templates/alsijil/notifications/check.html
new file mode 100644
index 0000000000000000000000000000000000000000..d76a1a0a5abfc6fb8b7722d5a4cf16ff927f069a
--- /dev/null
+++ b/aleksis/apps/alsijil/templates/alsijil/notifications/check.html
@@ -0,0 +1,4 @@
+{% load i18n %}{% trans "Please check if the following class register entries are complete and correct:" %}
+{% for entry in items %}
+- {{ entry.register_object }} ({{ entry.date }})
+{% endfor %}
\ No newline at end of file
diff --git a/aleksis/apps/alsijil/templates/alsijil/partials/lesson_status_icon.html b/aleksis/apps/alsijil/templates/alsijil/partials/lesson_status_icon.html
index ceff8c1a17ea0452b1f013d759dbee7edfd6c630..52f55e9723c3b9650bcd09f63725467a75f8993d 100644
--- a/aleksis/apps/alsijil/templates/alsijil/partials/lesson_status_icon.html
+++ b/aleksis/apps/alsijil/templates/alsijil/partials/lesson_status_icon.html
@@ -2,7 +2,7 @@
 
 {% now_datetime as now_dt %}
 
-{% if register_object.has_documentation %}
+{% if has_documentation or register_object.has_documentation %}
   <i class="material-icons green{% firstof color_suffix "-text"%} tooltipped {{ css_class }}" data-position="bottom" data-tooltip="{% trans "Data complete" %}" title="{% trans "Data complete" %}">check_circle</i>
 {% elif not register_object.period %}
   {% period_to_time_start week register_object.raw_period_from_on_day as time_start %}
@@ -19,13 +19,13 @@
   {% period_to_time_start week register_object.period as time_start %}
   {% period_to_time_end week register_object.period as time_end %}
 
-  {% if register_object.get_substitution.cancelled %}
+  {% if substitution.cancelled or register_object.get_substitution.cancelled %}
     <i class="material-icons red{% firstof color_suffix "-text"%} tooltipped {{ css_class }}"  data-position="bottom" data-tooltip="{% trans "Lesson cancelled" %}" title="{% trans "Lesson cancelled" %}">cancel</i>
   {% elif now_dt > time_end %}
     <i class="material-icons red{% firstof color_suffix "-text"%} tooltipped {{ css_class }}"  data-position="bottom" data-tooltip="{% trans "Missing data" %}" title="{% trans "Missing data" %}">history</i>
   {% elif now_dt > time_start and now_dt < time_end %}
     <i class="material-icons orange{% firstof color_suffix "-text"%} tooltipped {{ css_class }}"  data-position="bottom" data-tooltip="{% trans "Pending" %}" title="{% trans "Pending" %}">more_horiz</i>
-  {% elif register_object.get_substitution %}
+  {% elif substitution or register_object.get_substitution %}
     <i class="material-icons orange{% firstof color_suffix "-text"%} tooltipped {{ css_class }}"  data-position="bottom" data-tooltip="{% trans "Substitution" %}" title="{% trans "Substitution" %}">update</i>
   {% endif %}
 {% endif %}
diff --git a/aleksis/apps/alsijil/templates/alsijil/partials/objects_table.html b/aleksis/apps/alsijil/templates/alsijil/partials/objects_table.html
new file mode 100644
index 0000000000000000000000000000000000000000..f13e6043935b6fe5b1e91915158cc12d39a8f2e0
--- /dev/null
+++ b/aleksis/apps/alsijil/templates/alsijil/partials/objects_table.html
@@ -0,0 +1,43 @@
+{% load i18n material_form django_tables2 %}
+<div class="card">
+  <div class="card-content">
+    <div class="card-title">{% trans "Lesson filter" %}</div>
+    <form action="" method="get">
+      {% form form=filter_form %}{% endform %}
+      <button type="submit" class="btn waves-effect waves-light">
+        <i class="material-icons left">refresh</i>
+        {% trans "Update filters" %}
+      </button>
+    </form>
+  </div>
+</div>
+
+{% if table %}
+  <div class="card">
+    <div class="card-content">
+      <form action="" method="post">
+        {% csrf_token %}
+        <div class="row">
+          <div class="col s12 {% if action_form %}m4 l4 xl6{% endif %}">
+            <div class="card-title">{% trans "Lesson table" %}</div>
+          </div>
+          {% if action_form %}
+            <div class="col s12 m8 l8 xl6">
+              <div class="col s12 m8">
+                {% form form=action_form %}{% endform %}
+              </div>
+              <div class="col s12 m4">
+                <button type="submit" class="btn waves-effect waves-primary">
+                  {% trans "Execute" %}
+                  <i class="material-icons right">send</i>
+                </button>
+              </div>
+            </div>
+          {% endif %}
+        </div>
+        {% render_table table %}
+
+      </form>
+    </div>
+  </div>
+{% endif %}
diff --git a/aleksis/apps/alsijil/urls.py b/aleksis/apps/alsijil/urls.py
index 16ba0d0b9c5c4c976c26222a8542d3e96a93b1aa..8fb31b625ba068fc3c6ba731fe39155f9974d76e 100644
--- a/aleksis/apps/alsijil/urls.py
+++ b/aleksis/apps/alsijil/urls.py
@@ -100,4 +100,5 @@ urlpatterns = [
         views.AssignGroupRoleMultipleView.as_view(),
         name="assign_group_role_multiple",
     ),
+    path("all/", views.AllRegisterObjectsView.as_view(), name="all_register_objects"),
 ]
diff --git a/aleksis/apps/alsijil/util/alsijil_helpers.py b/aleksis/apps/alsijil/util/alsijil_helpers.py
index 9aedcb24ea2c8dd2c529d273b701b082281786ea..b5879b1f2ddcb79dbe336c9c4285ddccc30f23f6 100644
--- a/aleksis/apps/alsijil/util/alsijil_helpers.py
+++ b/aleksis/apps/alsijil/util/alsijil_helpers.py
@@ -1,14 +1,19 @@
-from typing import List, Optional, Union
+from datetime import date
+from operator import itemgetter
+from typing import Any, Dict, Iterable, List, Optional, Sequence, Union
 
 from django.db.models.expressions import Exists, OuterRef
 from django.db.models.query import Prefetch, QuerySet
 from django.db.models.query_utils import Q
 from django.http import HttpRequest
+from django.utils.formats import date_format
+from django.utils.translation import gettext as _
 
 from calendarweek import CalendarWeek
 
+from aleksis.apps.alsijil.forms import FilterRegisterObjectForm
 from aleksis.apps.alsijil.models import LessonDocumentation
-from aleksis.apps.chronos.models import Event, ExtraLesson, LessonPeriod
+from aleksis.apps.chronos.models import Event, ExtraLesson, Holiday, LessonPeriod
 from aleksis.apps.chronos.util.chronos_helpers import get_el_by_pk
 
 
@@ -99,3 +104,281 @@ def register_objects_sorter(register_object: Union[LessonPeriod, Event, ExtraLes
         return register_object.period_from_on_day
     else:
         return 0
+
+
+def _filter_register_objects_by_dict(
+    filter_dict: Dict[str, Any],
+    register_objects: QuerySet[Union[LessonPeriod, Event, ExtraLesson]],
+    label_: str,
+) -> QuerySet[Union[LessonPeriod, Event, ExtraLesson]]:
+    """Filter register objects by a dictionary generated through ``FilterRegisterObjectForm``."""
+    if label_ == LessonPeriod.label_:
+        register_objects = register_objects.filter(
+            lesson__validity__school_term=filter_dict.get("school_term")
+        )
+    else:
+        register_objects = register_objects.filter(school_term=filter_dict.get("school_term"))
+    register_objects = register_objects.distinct()
+
+    if (
+        filter_dict.get("date_start")
+        and filter_dict.get("date_end")
+        and label_ != LessonPeriod.label_
+    ):
+        register_objects = register_objects.within_dates(
+            filter_dict.get("date_start"), filter_dict.get("date_end")
+        )
+
+    if filter_dict.get("person"):
+        if label_ == LessonPeriod.label_:
+            register_objects = register_objects.filter(
+                Q(lesson__teachers=filter_dict.get("person"))
+                | Q(substitutions__teachers=filter_dict.get("person"))
+            )
+        else:
+            register_objects = register_objects.filter_teacher(filter_dict.get("person"))
+
+    if filter_dict.get("group"):
+        register_objects = register_objects.filter_group(filter_dict.get("group"))
+
+    if filter_dict.get("groups"):
+        register_objects = register_objects.filter_groups(filter_dict.get("groups"))
+
+    if filter_dict.get("subject"):
+        if label_ == LessonPeriod.label_:
+            register_objects = register_objects.filter(
+                Q(lesson__subject=filter_dict.get("subject"))
+                | Q(substitutions__subject=filter_dict.get("subject"))
+            )
+        elif label_ == Event.label_:
+            # As events have no subject, we exclude them at all
+            register_objects = register_objects.none()
+        else:
+            register_objects = register_objects.filter(subject=filter_dict.get("subject"))
+
+    return register_objects
+
+
+def _generate_dicts_for_lesson_periods(
+    filter_dict: Dict[str, Any],
+    lesson_periods: QuerySet[LessonPeriod],
+    documentations: Optional[Iterable[LessonDocumentation]] = None,
+    holiday_days: Optional[Sequence[date]] = None,
+) -> List[Dict[str, Any]]:
+    """Generate a list of dicts for use with ``RegisterObjectTable``."""
+    if not holiday_days:
+        holiday_days = []
+    date_start = lesson_periods.first().lesson.validity.date_start
+    date_end = lesson_periods.last().lesson.validity.date_end
+    if (
+        filter_dict["filter_date"]
+        and filter_dict.get("date_start") > date_start
+        and filter_dict.get("date_start") < date_end
+    ):
+        date_start = filter_dict.get("date_start")
+    if (
+        filter_dict["filter_date"]
+        and filter_dict.get("date_end") < date_end
+        and filter_dict.get("date_end") > date_start
+    ):
+        date_end = filter_dict.get("date_end")
+    weeks = CalendarWeek.weeks_within(date_start, date_end)
+
+    register_objects = []
+    for lesson_period in lesson_periods:
+        for week in weeks:
+            day = week[lesson_period.period.weekday]
+
+            # Skip all lesson periods in holidays
+            if day in holiday_days:
+                continue
+            # Ensure that the lesson period is in filter range and validity range
+            if (
+                lesson_period.lesson.validity.date_start
+                <= day
+                <= lesson_period.lesson.validity.date_end
+            ) and (
+                not filter_dict.get("filter_date")
+                or (filter_dict.get("date_start") <= day <= filter_dict.get("date_end"))
+            ):
+                sub = lesson_period.get_substitution()
+
+                # Skip lesson period if the person isn't a teacher
+                # or substitution teacher of this lesson period
+                if filter_dict.get("person") and (
+                    filter_dict.get("person") not in lesson_period.lesson.teachers.all() and not sub
+                ):
+                    continue
+
+                teachers = (
+                    sub.teacher_names
+                    if sub and sub.teachers.all()
+                    else lesson_period.lesson.teacher_names
+                )
+                if (
+                    filter_dict.get("subject")
+                    and filter_dict.get("subject") != lesson_period.get_subject()
+                ):
+                    continue
+
+                # Filter matching documentations and annotate if they exist
+                filtered_documentations = list(
+                    filter(
+                        lambda d: d.week == week.week
+                        and d.year == week.year
+                        and d.lesson_period_id == lesson_period.pk,
+                        documentations
+                        if documentations is not None
+                        else lesson_period.documentations.all(),
+                    )
+                )
+                has_documentation = bool(filtered_documentations)
+
+                if filter_dict.get(
+                    "has_documentation"
+                ) is not None and has_documentation != filter_dict.get("has_documentation"):
+                    continue
+
+                # Build table entry
+                entry = {
+                    "pk": f"lesson_period_{lesson_period.pk}_{week.year}_{week.week}",
+                    "week": week,
+                    "has_documentation": has_documentation,
+                    "substitution": sub,
+                    "register_object": lesson_period,
+                    "date": date_format(day),
+                    "date_sort": day,
+                    "period": f"{lesson_period.period.period}.",
+                    "period_sort": lesson_period.period.period,
+                    "groups": lesson_period.lesson.group_names,
+                    "teachers": teachers,
+                    "subject": lesson_period.get_subject().name,
+                }
+                if has_documentation:
+                    doc = filtered_documentations[0]
+                    entry["topic"] = doc.topic
+                    entry["homework"] = doc.homework
+                    entry["group_note"] = doc.group_note
+                register_objects.append(entry)
+    return register_objects
+
+
+def _generate_dicts_for_events_and_extra_lessons(
+    filter_dict: Dict[str, Any],
+    register_objects_start: Sequence[Union[Event, ExtraLesson]],
+    documentations: Optional[Iterable[LessonDocumentation]] = None,
+) -> List[Dict[str, Any]]:
+    """Generate a list of dicts for use with ``RegisterObjectTable``."""
+    register_objects = []
+    for register_object in register_objects_start:
+        filtered_documentations = list(
+            filter(
+                lambda d: getattr(d, f"{register_object.label_}_id") == register_object.pk,
+                documentations
+                if documentations is not None
+                else register_object.documentations.all(),
+            )
+        )
+        has_documentation = bool(filtered_documentations)
+
+        if filter_dict.get(
+            "has_documentation"
+        ) is not None and has_documentation != filter_dict.get("has_documentation"):
+            continue
+
+        if isinstance(register_object, ExtraLesson):
+            day = date_format(register_object.day)
+            day_sort = register_object.day
+            period = f"{register_object.period.period}."
+            period_sort = register_object.period.period
+        else:
+            day = (
+                f"{date_format(register_object.date_start)}"
+                f"–{date_format(register_object.date_end)}"
+            )
+            day_sort = register_object.date_start
+            period = f"{register_object.period_from.period}.–{register_object.period_to.period}."
+            period_sort = register_object.period_from.period
+
+        # Build table entry
+        entry = {
+            "pk": f"{register_object.label_}_{register_object.pk}",
+            "has_documentation": has_documentation,
+            "register_object": register_object,
+            "date": day,
+            "date_sort": day_sort,
+            "period": period,
+            "period_sort": period_sort,
+            "groups": register_object.group_names,
+            "teachers": register_object.teacher_names,
+            "subject": register_object.subject.name
+            if isinstance(register_object, ExtraLesson)
+            else _("Event"),
+        }
+        if has_documentation:
+            doc = filtered_documentations[0]
+            entry["topic"] = doc.topic
+            entry["homework"] = doc.homework
+            entry["group_note"] = doc.group_note
+        register_objects.append(entry)
+
+    return register_objects
+
+
+def generate_list_of_all_register_objects(filter_dict: Dict[str, Any]) -> List[Dict[str, Any]]:
+    """Generate a list of all register objects.
+
+    This list can be filtered using ``filter_dict``. The following keys are supported:
+    - ``school_term`` (defaults to the current school term)
+    - ``date_start`` and ``date_end`` (defaults to the last thirty days)
+    - ``groups`` and/or ``groups``
+    - ``person``
+    - ``subject``
+    """
+    # Always force a value for school term, start and end date so that queries won't get too big
+    initial_filter_data = FilterRegisterObjectForm.get_initial()
+    filter_dict["school_term"] = filter_dict.get("school_term", initial_filter_data["school_term"])
+    filter_dict["date_start"] = filter_dict.get("date_start", initial_filter_data["date_start"])
+    filter_dict["date_end"] = filter_dict.get("date_end", initial_filter_data["date_end"])
+    filter_dict["filter_date"] = bool(filter_dict.get("date_start")) and bool(
+        filter_dict.get("date_end")
+    )
+
+    # Get all holidays in the selected school term to sort all data in holidays out
+    holidays = Holiday.objects.within_dates(
+        filter_dict["school_term"].date_start, filter_dict["school_term"].date_end
+    )
+    holiday_days = holidays.get_all_days()
+
+    lesson_periods = _filter_register_objects_by_dict(
+        filter_dict,
+        LessonPeriod.objects.order_by("lesson__validity__date_start"),
+        LessonPeriod.label_,
+    )
+    events = _filter_register_objects_by_dict(
+        filter_dict, Event.objects.exclude_holidays(holidays), Event.label_
+    )
+    extra_lessons = _filter_register_objects_by_dict(
+        filter_dict, ExtraLesson.objects.exclude_holidays(holidays), ExtraLesson.label_
+    )
+
+    # Prefetch documentations for all register objects and substitutions for all lesson periods
+    # in order to prevent extra queries
+    documentations = LessonDocumentation.objects.not_empty().filter(
+        Q(event__in=events)
+        | Q(extra_lesson__in=extra_lessons)
+        | Q(lesson_period__in=lesson_periods)
+    )
+
+    if lesson_periods:
+        register_objects = _generate_dicts_for_lesson_periods(
+            filter_dict, lesson_periods, documentations, holiday_days
+        )
+        register_objects += _generate_dicts_for_events_and_extra_lessons(
+            filter_dict, list(events) + list(extra_lessons), documentations
+        )
+
+        # Sort table entries by date and period and configure table
+        register_objects = sorted(register_objects, key=itemgetter("date_sort", "period_sort"))
+        return register_objects
+    return []
diff --git a/aleksis/apps/alsijil/views.py b/aleksis/apps/alsijil/views.py
index f3990be4fc351f55607a83a014d7d0cfacf3504e..31ad878230fef101e954d1a1683dc0e9d1a62377 100644
--- a/aleksis/apps/alsijil/views.py
+++ b/aleksis/apps/alsijil/views.py
@@ -13,12 +13,14 @@ from django.urls import reverse, reverse_lazy
 from django.utils import timezone
 from django.utils.decorators import method_decorator
 from django.utils.translation import ugettext as _
+from django.views import View
 from django.views.decorators.cache import never_cache
 from django.views.generic import DetailView
 
 import reversion
 from calendarweek import CalendarWeek
-from django_tables2 import SingleTableView
+from django_tables2 import RequestConfig, SingleTableView
+from guardian.shortcuts import get_objects_for_user
 from reversion.views import RevisionMixin
 from rules.contrib.views import PermissionRequiredMixin, permission_required
 
@@ -35,16 +37,19 @@ from aleksis.core.mixins import (
 from aleksis.core.models import Group, Person, SchoolTerm
 from aleksis.core.util import messages
 from aleksis.core.util.core_helpers import get_site_preferences, objectgetter_optional
+from aleksis.core.util.predicates import check_global_permission
 
 from .forms import (
     AssignGroupRoleForm,
     ExcuseTypeForm,
     ExtraMarkForm,
+    FilterRegisterObjectForm,
     GroupRoleAssignmentEditForm,
     GroupRoleForm,
     LessonDocumentationForm,
     PersonalNoteFormSet,
     RegisterAbsenceForm,
+    RegisterObjectActionForm,
     SelectForm,
 )
 from .models import (
@@ -55,9 +60,16 @@ from .models import (
     LessonDocumentation,
     PersonalNote,
 )
-from .tables import ExcuseTypeTable, ExtraMarkTable, GroupRoleTable
+from .tables import (
+    ExcuseTypeTable,
+    ExtraMarkTable,
+    GroupRoleTable,
+    RegisterObjectSelectTable,
+    RegisterObjectTable,
+)
 from .util.alsijil_helpers import (
     annotate_documentations,
+    generate_list_of_all_register_objects,
     get_register_object_by_pk,
     get_timetable_instance_by_pk,
     register_objects_sorter,
@@ -898,6 +910,20 @@ def overview_person(request: HttpRequest, id_: Optional[int] = None) -> HttpResp
     context["excuse_types"] = excuse_types
     context["extra_marks"] = extra_marks
 
+    # Build filter with own form and logic as django-filter can't work with different models
+    filter_form = FilterRegisterObjectForm(request, request.GET or None, for_person=True)
+    filter_dict = filter_form.cleaned_data if filter_form.is_valid() else {}
+    filter_dict["person"] = person
+    context["filter_form"] = filter_form
+
+    register_objects = generate_list_of_all_register_objects(filter_dict)
+    if register_objects:
+        table = RegisterObjectTable(register_objects)
+        items_per_page = request.user.person.preferences[
+            "alsijil__register_objects_table_items_per_page"
+        ]
+        RequestConfig(request, paginate={"per_page": items_per_page}).configure(table)
+        context["register_object_table"] = table
     return render(request, "alsijil/class_register/person.html", context)
 
 
@@ -1236,3 +1262,51 @@ class GroupRoleAssignmentDeleteView(
     def get_success_url(self) -> str:
         pk = self.object.groups.first().pk
         return reverse("assigned_group_roles", args=[pk])
+
+
+class AllRegisterObjectsView(PermissionRequiredMixin, View):
+    """Provide overview of all register objects for coordinators."""
+
+    permission_required = "alsijil.view_register_objects_list"
+
+    def get_context_data(self, request):
+        context = {}
+        # Filter selectable groups by permissions
+        groups = Group.objects.all()
+        if not check_global_permission(request.user, "alsijil.view_full_register"):
+            allowed_groups = get_objects_for_user(
+                self.request.user, "core.view_full_register_group", Group
+            ).values_list("pk", flat=True)
+            groups = groups.filter(Q(parent_groups__in=allowed_groups) | Q(pk__in=allowed_groups))
+
+        # Build filter with own form and logic as django-filter can't work with different models
+        filter_form = FilterRegisterObjectForm(
+            request, request.GET or None, for_person=False, groups=groups
+        )
+        filter_dict = filter_form.cleaned_data if filter_form.is_valid() else {}
+        filter_dict["groups"] = groups
+        context["filter_form"] = filter_form
+
+        register_objects = generate_list_of_all_register_objects(filter_dict)
+
+        self.action_form = RegisterObjectActionForm(request, register_objects, request.POST or None)
+        context["action_form"] = self.action_form
+
+        if register_objects:
+            self.table = RegisterObjectSelectTable(register_objects)
+            items_per_page = request.user.person.preferences[
+                "alsijil__register_objects_table_items_per_page"
+            ]
+            RequestConfig(request, paginate={"per_page": items_per_page}).configure(self.table)
+            context["table"] = self.table
+        return context
+
+    def get(self, request: HttpRequest) -> HttpResponse:
+        context = self.get_context_data(request)
+        return render(request, "alsijil/class_register/all_objects.html", context)
+
+    def post(self, request: HttpRequest):
+        context = self.get_context_data(request)
+        if self.action_form.is_valid():
+            self.action_form.execute()
+        return render(request, "alsijil/class_register/all_objects.html", context)