diff --git a/app/views/resources/room_request/resolve.php b/app/views/resources/room_request/resolve.php
index 9a5ba378bd864a16931eef60c8cc7686fce61bfc..c56d63dca776a7123f5587587c130825702179ad 100644
--- a/app/views/resources/room_request/resolve.php
+++ b/app/views/resources/room_request/resolve.php
@@ -208,7 +208,7 @@
             </article>
             <article class="studip assign-dates">
                 <header><h1><?= _('Termine zuordnen') ?></h1></header>
-                <div>
+
                     <table id="resolve-dates-table" class="default">
                         <thead>
                         <tr>
@@ -328,7 +328,7 @@
                         <? endif ?>
                         </tbody>
                     </table>
-                </div>
+
             </article>
         <? endif ?>
     <? endif ?>
diff --git a/lib/classes/StudipCoreFormat.php b/lib/classes/StudipCoreFormat.php
index f0f9a62523aff3c41248791878f7b1af20f23f6d..665bb376dd91a5166cbc6739b718952c3b6e0d35 100644
--- a/lib/classes/StudipCoreFormat.php
+++ b/lib/classes/StudipCoreFormat.php
@@ -515,8 +515,7 @@ class StudipCoreFormat extends TextFormat
 
         $intern = $domain === $_SERVER['HTTP_HOST'];
 
-        return sprintf('<a class="%s" href="mailto:%s">%s</a>',
-            $intern ? "link-intern" : "link-extern",
+        return sprintf('<a href="mailto:%1$s">%2$s</a>',
             $email,
             $link_text
         );
@@ -655,13 +654,23 @@ class StudipCoreFormat extends TextFormat
         $linkmarkup->removeMarkup('links');
         $linkmarkup->removeMarkup('wiki-links');
 
-        return sprintf('<a class="%s" href="%s"%s>%s</a>%s',
-            $intern ? 'link-intern' : 'link-extern',
-            $url,
-            $intern ? '' : ' target="_blank" rel="noreferrer noopener"',
-            $linkmarkup->format($title),
-            $postfix
-        );
+        if (strpos($url, 'mailto:') === 0) {
+            return sprintf(
+                '<a href="%1$s">%2$s</a>%3$s',
+                $url,
+                $linkmarkup->format($title),
+                $postfix
+            );
+        } else {
+            return sprintf(
+                '<a class="%s" href="%s"%s>%s</a>%s',
+                $intern ? 'link-intern' : 'link-extern',
+                $url,
+                $intern ? '' : ' target="_blank" rel="noreferrer noopener"',
+                $linkmarkup->format($title),
+                $postfix
+            );
+        }
     }
 
     protected static function htmlAnchor($markup, $matches, $contents)
diff --git a/lib/classes/htmlpurifier/HTMLPurifier_Injector_ClassifyLinks.php b/lib/classes/htmlpurifier/HTMLPurifier_Injector_ClassifyLinks.php
index 4096bc8cbc242e1280ceed963bd6cea118388c48..a43e27e54925c70b06ab67ab578a3df6c8d55124 100644
--- a/lib/classes/htmlpurifier/HTMLPurifier_Injector_ClassifyLinks.php
+++ b/lib/classes/htmlpurifier/HTMLPurifier_Injector_ClassifyLinks.php
@@ -1,7 +1,7 @@
 <?php
 
 /**
- * Classify links as internal or external and set the class attribute 
+ * Classify links as internal or external and set the class attribute
  * accordingly.
  */
 class HTMLPurifier_Injector_ClassifyLinks extends HTMLPurifier_Injector
@@ -13,8 +13,10 @@ class HTMLPurifier_Injector_ClassifyLinks extends HTMLPurifier_Injector
     {
         if ($token->name === 'a' && isset($token->attr['href'])) {
             $is_link_intern = isLinkIntern($token->attr['href']);
-            $token->attr['class'] = $is_link_intern ? 'link-intern' : 'link-extern';
-            if (!$is_link_intern) {
+            if ($is_link_intern) {
+                $token->attr['class'] = 'link-intern';
+            } elseif (strpos($token->attr['href'], 'mailto:') !== 0) {
+                $token->attr['class'] = 'link-extern';
                 $token->attr['target'] = '_blank';
             }
         }
diff --git a/resources/vue/components/courseware/CoursewareStructuralElement.vue b/resources/vue/components/courseware/CoursewareStructuralElement.vue
index 78af7a4992d9197eebf10f059eb69eaa63803767..493c312e44d55da0a2dc5f355240e40b5606fadd 100755
--- a/resources/vue/components/courseware/CoursewareStructuralElement.vue
+++ b/resources/vue/components/courseware/CoursewareStructuralElement.vue
@@ -396,7 +396,7 @@
 
 <script>
 import ContainerComponents from './container-components.js';
-import CoursewarePluginComponents from './plugin-components.js'
+import CoursewarePluginComponents from './plugin-components.js';
 import CoursewareStructuralElementPermissions from './CoursewareStructuralElementPermissions.vue';
 import CoursewareAccordionContainer from './CoursewareAccordionContainer.vue';
 import CoursewareCompanionBox from './CoursewareCompanionBox.vue';
@@ -430,7 +430,7 @@ export default {
         IsoDate,
         StudipDialog,
     },
-    props: {},
+    props: ['orderedStructuralElements'],
 
     mixins: [CoursewareExport],
 
@@ -481,6 +481,9 @@ export default {
             courseware: 'courseware',
             consumeMode: 'consumeMode',
             containerById: 'courseware-containers/byId',
+            relatedContainers: 'courseware-containers/related',
+            relatedStructuralElements: 'courseware-structural-elements/related',
+            relatedUsers: 'users/related',
             structuralElementById: 'courseware-structural-elements/byId',
             userIsTeacher: 'userIsTeacher',
             pluginManager: 'pluginManager',
@@ -488,13 +491,13 @@ export default {
             showAddDialog: 'showStructuralElementAddDialog',
             showExportDialog: 'showStructuralElementExportDialog',
             showInfoDialog: 'showStructuralElementInfoDialog',
-            showDeleteDialog : 'showStructuralElementDeleteDialog',
-            showOerDialog : 'showStructuralElementOerDialog',
+            showDeleteDialog: 'showStructuralElementDeleteDialog',
+            showOerDialog: 'showStructuralElementOerDialog',
             oerEnabled: 'oerEnabled',
             oerTitle: 'oerTitle',
             licenses: 'licenses',
             exportState: 'exportState',
-            exportProgress: 'exportProgress'
+            exportProgress: 'exportProgress',
         }),
 
         textOer() {
@@ -502,7 +505,7 @@ export default {
                 title: this.$gettext('Seite auf') + ' ' + this.oerTitle + ' ' + this.$gettext('veröffentlichen'),
                 confirm: this.$gettext('Veröffentlichen'),
                 close: this.$gettext('Schließen'),
-            }
+            };
         },
 
         inCourse() {
@@ -555,87 +558,38 @@ export default {
         },
 
         ancestors() {
-            if (!this.currentElement) {
-                return [];
-            }
-            if (this.currentElement.relationships.ancestors.data) {
-                return this.currentElement.relationships.ancestors.data.map(({ id }) =>
-                    this.structuralElementById({ id })
-                );
-            }
-            return [];
-        },
-        parent() {
             if (!this.structuralElement) {
                 return [];
             }
-            if (this.structuralElement.relationships.parent.data) {
-                let id = this.structuralElement.relationships.parent.data.id;
-                return this.structuralElementById({ id });
-            }
-            return [];
-        },
-        hasSiblings() {
-            if (this.parent.length !== 0) {
-                return this.parent.relationships.children.data.length > 1;
-            } else {
-                return false;
-            }
+
+            return this.relatedStructuralElements({ parent: this.structuralElement, relationship: 'ancestors' });
         },
         prevElement() {
-            if (this.hasSiblings) {
-                let view = this;
-                let siblings = this.parent.relationships.children.data;
-                let id = '';
-                siblings.forEach((el, index) => {
-                    if (el.id === view.currentId && index !== 0) {
-                        id = siblings[index - 1].id;
-                    }
-                });
-                if (id === '') {
-                    return this.parent;
-                } else {
-                    return this.structuralElementById({ id });
-                }
-            } else if (this.parent.length !== 0) {
-                return this.parent;
-            } else {
+            const currentIndex = this.orderedStructuralElements.indexOf(this.structuralElement.id);
+            if (currentIndex <= 0) {
                 return null;
             }
+            const previousId = this.orderedStructuralElements[currentIndex - 1];
+            const previous = this.structuralElementById({ id: previousId });
+
+            return previous;
         },
         nextElement() {
-            let view = this;
-            if (this.structuralElement.relationships.children.data.length > 0) {
-                let id = this.structuralElement.relationships.children.data[0].id;
-                return this.structuralElementById({ id });
-            } else if (this.hasSiblings) {
-                let siblings = this.parent.relationships.children.data;
-                let id = '';
-                siblings.forEach((el, index) => {
-                    if (el.id === view.currentId && siblings.length > index + 1) {
-                        id = siblings[index + 1].id;
-                    }
-                });
-                if (id === '') {
-                    return this.getNextParentSibling(this.currentId);
-                } else {
-                    return this.structuralElementById({ id });
-                }
-            } else {
-                return this.getNextParentSibling(this.currentId);
+            const currentIndex = this.orderedStructuralElements.indexOf(this.structuralElement.id);
+            const lastIndex = this.orderedStructuralElements.length - 1;
+            if (currentIndex === -1 || currentIndex === lastIndex) {
+                return null;
             }
+            const nextId = this.orderedStructuralElements[currentIndex + 1];
+            const next = this.structuralElementById({ id: nextId });
+
+            return next;
         },
         empty() {
             if (this.containers === null) {
                 return true;
             } else {
-                let noBlockFound = true;
-                this.containers.forEach((container) => {
-                    if (container.relationships.blocks.data.length > 0) {
-                        noBlockFound = false;
-                    }
-                });
-                return noBlockFound;
+                return !this.containers.some((container) => container.relationships.blocks.data.length > 0);
             }
         },
         containers() {
@@ -643,12 +597,12 @@ export default {
                 return [];
             }
 
-            const containers = this.$store.getters['courseware-containers/related']({
-                parent: this.structuralElement,
-                relationship: 'containers',
-            });
-
-            return containers;
+            return (
+                this.relatedContainers({
+                    parent: this.structuralElement,
+                    relationship: 'containers',
+                }) ?? []
+            );
         },
         noContainers() {
             if (this.containers === null) {
@@ -679,7 +633,7 @@ export default {
         },
 
         owner() {
-            const owner = this.$store.getters['users/related']({
+            const owner = this.relatedUsers({
                 parent: this.structuralElement,
                 relationship: 'owner',
             });
@@ -688,7 +642,7 @@ export default {
         },
 
         editor() {
-            const editor = this.$store.getters['users/related']({
+            const editor = this.relatedUsers({
                 parent: this.structuralElement,
                 relationship: 'editor',
             });
@@ -764,7 +718,7 @@ export default {
         },
     },
 
-    async mounted() {
+    mounted() {
         if (!this.currentId) {
             this.setCurrentId(this.$route.params.id);
         }
@@ -797,7 +751,7 @@ export default {
             this.initCurrent();
         },
         initCurrent() {
-            this.currentElement = JSON.parse(JSON.stringify(this.structuralElement));
+            this.currentElement = _.cloneDeep(this.structuralElement);
             this.uploadFileError = '';
         },
         async menuAction(action) {
@@ -941,29 +895,6 @@ export default {
         containerComponent(container) {
             return 'courseware-' + container.attributes['container-type'] + '-container';
         },
-        getNextParentSibling(element_id) {
-            let current = this.structuralElementById({ id: element_id });
-            if (current.relationships.parent.data === null) {
-                return null;
-            }
-            let parent = this.structuralElementById({ id: current.relationships.parent.data.id });
-            if (parent.relationships.parent.data === null) {
-                return null;
-            }
-            let grandParent = this.structuralElementById({ id: parent.relationships.parent.data.id });
-            let parentSiblings = grandParent.relationships.children.data;
-            let id = '';
-            parentSiblings.forEach((el, index) => {
-                if (parseInt(el.id, 10) === parseInt(parent.id, 10) && parentSiblings.length > index + 1) {
-                    id = parentSiblings[index + 1].id;
-                }
-            });
-            if (id === '') {
-                this.getNextParentSibling(parent.id);
-            } else {
-                return this.structuralElementById({ id });
-            }
-        },
         setBookmark() {
             this.addBookmark(this.structuralElement);
             this.companionInfo({ info: this.$gettext('Das Lesezeichen wurde gesetzt') });
diff --git a/resources/vue/components/courseware/CoursewareTableOfContentsBlock.vue b/resources/vue/components/courseware/CoursewareTableOfContentsBlock.vue
index 0a0ea00e7dc4bd8365aa0ad474a1352ec989be5b..f6b45265e0b134cc9fcf23c2b2f1004eeef08788 100755
--- a/resources/vue/components/courseware/CoursewareTableOfContentsBlock.vue
+++ b/resources/vue/components/courseware/CoursewareTableOfContentsBlock.vue
@@ -111,7 +111,10 @@ export default {
             let children = this.structuralElement.relationships.children.data;
             let childElements = [];
             children.forEach((element) => {
-                childElements.push(view.structuralElementById({ id: element.id }));
+                let childElement = view.structuralElementById({ id: element.id });
+                if (childElement.attributes['can-read']) {
+                    childElements.push(childElement);
+                }
             });
 
             return childElements;
diff --git a/resources/vue/components/courseware/IndexApp.vue b/resources/vue/components/courseware/IndexApp.vue
index 2dfe493b8bb6b1c7f2d5638e5eb4c36665019722..dde33980a5ee2af7bc0d4de07489eb50721ba362 100755
--- a/resources/vue/components/courseware/IndexApp.vue
+++ b/resources/vue/components/courseware/IndexApp.vue
@@ -1,6 +1,8 @@
 <template>
     <div v-if="courseware">
-        <courseware-structural-element></courseware-structural-element>
+        <courseware-structural-element
+            :ordered-structural-elements="orderedStructuralElements"
+        ></courseware-structural-element>
         <MountingPortal mountTo="#courseware-action-widget" name="sidebar-actions">
             <courseware-action-widget></courseware-action-widget>
         </MountingPortal>
@@ -25,8 +27,14 @@ export default {
         CoursewareViewWidget,
         CoursewareActionWidget,
     },
+    data: () => ({ orderedStructuralElements: [] }),
     computed: {
-        ...mapGetters(['courseware', 'userId', 'blockAdder']),
+        ...mapGetters({
+            courseware: 'courseware',
+            relatedStructuralElement: 'courseware-structural-elements/related',
+            structuralElements: 'courseware-structural-elements/all',
+            userId: 'userId',
+        }),
     },
     methods: {
         ...mapActions(['loadCoursewareStructure', 'loadTeacherStatus', 'coursewareBlockAdder']),
@@ -34,12 +42,56 @@ export default {
     async mounted() {
         await this.loadCoursewareStructure();
         await this.loadTeacherStatus(this.userId);
-        // console.debug('IndexApp mounted for courseware:', this.courseware, this.$store);
     },
     watch: {
         $route() {
             this.coursewareBlockAdder({}); //reset block adder on navigate
+        },
+        structuralElements(newElements, oldElements) {
+            const nodes = buildNodes(this.structuralElements, this.relatedStructuralElement.bind(this));
+            this.orderedStructuralElements = [...visitTree(nodes, findRoot(nodes))];
+        },
+    },
+};
+
+function buildNodes(structuralElements, relatedStructuralElement) {
+    return structuralElements.reduce((memo, element) => {
+        if (element.attributes['can-read']) {
+            memo.push({
+                id: element.id,
+                parent:
+                    relatedStructuralElement({
+                        parent: element,
+                        relationship: 'parent',
+                    })?.id ?? null,
+
+                children:
+                    relatedStructuralElement({
+                        parent: element,
+                        relationship: 'children',
+                    })?.map((child) => child.id) ?? [],
+            });
+        }
+
+        return memo;
+    }, []);
+}
+
+function findRoot(nodes) {
+    return nodes.find((node) => node.parent === null);
+}
+
+function findNode(nodes, id) {
+    return nodes.find((node) => node.id === id);
+}
+
+function* visitTree(nodes, current) {
+    if (current) {
+        yield current.id;
+
+        for (let index = 0; index < current.children.length; index++) {
+            yield* visitTree(nodes, findNode(nodes, current.children[index]));
         }
     }
-};
+}
 </script>