diff --git a/app/controllers/course/courseware.php b/app/controllers/course/courseware.php
index b22ffb79bf89e2ae45e147f2b9e65cba12346c60..c4aae64c745e0c4c1b8ed9f23dad5f151ccb09c8 100755
--- a/app/controllers/course/courseware.php
+++ b/app/controllers/course/courseware.php
@@ -60,9 +60,9 @@ class Course_CoursewareController extends AuthenticatedController
 
         Navigation::activateItem('course/courseware/content');
         $this->setIndexSidebar();
-        $this->licenses = array();
-        $sorm_licenses = License::findBySQL("1 ORDER BY name ASC");
-        foreach($sorm_licenses as $license) {
+        $this->licenses = [];
+        $sorm_licenses = License::findBySQL('1 ORDER BY name ASC');
+        foreach ($sorm_licenses as $license) {
             array_push($this->licenses, $license->toArray());
         }
         $this->licenses = json_encode($this->licenses);
@@ -87,7 +87,6 @@ class Course_CoursewareController extends AuthenticatedController
         } else {
             Navigation::activateItem('course/courseware/manager');
         }
-
     }
 
     private function setIndexSidebar(): void
@@ -106,151 +105,145 @@ class Course_CoursewareController extends AuthenticatedController
         $sidebar->addWidget($views)->addLayoutCSSClass('courseware-view-widget');
     }
 
-    private function getProgressData(bool $course_progress = false): array
+    private function getProgressData(bool $showProgressForAllParticipants = false): iterable
     {
-        $data = [];
+        /** @var ?\Course $course */
+        $course = Context::get();
+        if (!$course || !$course->courseware) {
+            return [];
+        }
 
-        $cid = Context::getId();
-        $course = Course::find($cid);
-        $course_members = $course->getMembersWithStatus('autor');
-        $course_member_ids = array_column($course_members, 'user_id');
+        $instance = new Instance($course->courseware);
+        $user = \User::findCurrent();
 
-        $elements = StructuralElement::findBySQL('range_id = ?', [$cid]);
+        $elements = $this->findElements($instance, $user);
+        $progress = $this->computeSelfProgresses($instance, $user, $elements, $showProgressForAllParticipants);
+        $progress = $this->computeCumulativeProgresses($instance, $elements, $progress);
 
-        if ($course_progress) {
-            $cw_user_progresses = UserProgress::findBySQL('user_id IN (?)', [$course_member_ids]);
-        } else {
-            $cw_user_progresses = UserProgress::findBySQL('user_id = ?', [
-                $GLOBALS['user']->id,
-            ]);
-        }
+        return $this->prepareProgressData($elements, $progress);
+    }
 
-        foreach ($elements as $element) {
-            $el = [
-                'id' => $element->id,
-                'name' => $element->title,
-                'parent_id' => $element->parent->id,
-                'parent_name' => $element->parent->title,
-                'children' => $this->getChildren($element->children),
-            ];
-            $el['progress'] = $this->getProgress($course, $element, $course_progress, $cw_user_progresses, $course_member_ids);
+    private function findElements(Instance $instance, User $user): iterable
+    {
+        $elements = $instance->getRoot()->findDescendants($user);
+        $elements[] = $instance->getRoot();
 
-            array_push($data, $el);
-        }
+        return array_combine(array_column($elements, 'id'), $elements);
+    }
 
-        //update children progress
-        foreach ($data as &$element) {
-            if (count($element['children'])) {
-                foreach ($element['children'] as &$child) {
-                    foreach ($data as $el) {
-                        if ($el['id'] == $child['id']) {
-                            $child['progress'] = $el['progress'];
-                        }
-                    }
+    private function computeChildrenOf(iterable $elements): iterable
+    {
+        $childrenOf = [];
+        foreach ($elements as $elementId => $element) {
+            if ($element['parent_id']) {
+                if (!isset($childrenOf[$element['parent_id']])) {
+                    $childrenOf[$element['parent_id']] = [];
                 }
+                $childrenOf[$element['parent_id']][] = $elementId;
             }
         }
 
-        return $data;
+        return $childrenOf;
     }
 
-    private function getChildren($children): array
-    {
-        $data = [];
-        foreach ($children as $child) {
-            $el = [
-                'id' => $child->id,
-                'name' => $child->title,
+    private function computeSelfProgresses(
+        Instance $instance,
+        User $user,
+        iterable $elements,
+        bool $showProgressForAllParticipants
+    ): iterable {
+        $progress = [];
+        /** @var \Course $course */
+        $course = $instance->getRange();
+        $allBlocks = $instance->findAllBlocksGroupedByStructuralElementId();
+        $courseMemberIds = $showProgressForAllParticipants
+            ? array_column($course->getMembersWithStatus('autor'), 'user_id')
+            : [$user->getId()];
+        $userProgresses = UserProgress::findBySQL('user_id IN (?)', [$courseMemberIds]);
+        foreach ($elements as $elementId => $element) {
+            $selfProgress = $this->getSelfProgresses($allBlocks, $elementId, $userProgresses, $courseMemberIds);
+            $progress[$elementId] = [
+                'self' => $selfProgress['counter'] ? $selfProgress['progress'] / $selfProgress['counter'] : 1,
             ];
-            array_push($data, $el);
+        }
+
+        return $progress;
+    }
+
+    private function getSelfProgresses(
+        array $allBlocks,
+        string $elementId,
+        array $userProgresses,
+        array $courseMemberIds
+    ): array {
+        $blks = $allBlocks[$elementId] ?: [];
+
+        $data = [
+            'counter' => count($blks),
+            'progress' => 0,
+        ];
+        $usersCounter = count($courseMemberIds);
+        foreach ($blks as $blk) {
+            $progresses = array_filter($userProgresses, function ($progress) use ($blk, $courseMemberIds) {
+                return $progress->block_id === $blk->getId() && in_array($progress->user_id, $courseMemberIds);
+            });
+            $usersProgress = count($progresses) ? array_sum(array_column($progresses, 'grade')) : 0;
+
+            $data['progress'] += $usersProgress / $usersCounter;
         }
 
         return $data;
     }
 
-    private function getProgress(Course $course, StructuralElement $element, bool $course_progress = false, array $cw_user_progresses, array $course_member_ids): array
+    private function computeCumulativeProgresses(Instance $instance, iterable $elements, iterable $progress): iterable
     {
-        $descendants = $element->findDescendants(\User::findCurrent());
-        $count = count($descendants);
-        $progress = 0;
-        $own_progress = 0;
-
-        foreach ($descendants as $el) {
-            $block = $this->getBlocks($el->id, $course_progress, $cw_user_progresses, $course, $course_member_ids);
-            if ($block['counter'] > 0) {
-                $progress += $block['progress'] / $block['counter'];
-            } else {
-                $progress += 1;
+        $childrenOf = $this->computeChildrenOf($elements);
+
+        // compute `cumulative` of each element
+        $visitor = function (&$progress, $element) use (&$childrenOf, &$elements, &$visitor) {
+            $elementId = $element->getId();
+            $numberOfNodes = 0;
+            $cumulative = 0;
+
+            // visit children first
+            if (isset($childrenOf[$elementId])) {
+                foreach ($childrenOf[$elementId] as $childId) {
+                    $visitor($progress, $elements[$childId]);
+                    $numberOfNodes += $progress[$childId]['numberOfNodes'];
+                    $cumulative += $progress[$childId]['cumulative'];
+                }
             }
-        }
 
-        $own_blocks = $this->getBlocks($element->id, $course_progress, $cw_user_progresses, $course, $course_member_ids);
+            $progress[$elementId]['cumulative'] = $cumulative + $progress[$elementId]['self'];
+            $progress[$elementId]['numberOfNodes'] = $numberOfNodes + 1;
 
-        if ($own_blocks['counter'] > 0) {
-            $own_progress = $own_blocks['progress'] / $own_blocks['counter'];
-        } else {
-            $own_progress = 1;
-        }
+            return $progress;
+        };
 
-        $count = count($descendants);
-        if ($count > 0) {
-            $progress = ($progress + $own_progress) / ($count + 1);
-        } else {
-            $progress = $own_progress;
-        }
+        $visitor($progress, $instance->getRoot());
 
-        return ['total' => round($progress, 2) * 100, 'current' => round($own_progress, 2) * 100];
+        return $progress;
     }
 
-    private function getBlocks(string $element_id, bool $course_progress = false, array $cw_user_progresses, Course $course, array $course_member_ids): array
+    private function prepareProgressData(iterable $elements, iterable $progress): iterable
     {
-        $containers = Courseware\Container::findBySQL('structural_element_id = ?', [intval($element_id)]);
-        $blocks = [];
-        $blocks['counter'] = 0;
-        $blocks['progress'] = 0;
-        $users_counter = count($course->getMembersWithStatus('autor'));
-
-        foreach ($containers as $container) {
-            $counter = $container->countBlocks();
-
-            $blocks['counter'] += $counter;
-            if ($counter > 0) {
-                $blks = Courseware\Block::findBySQL('container_id = ?', [$container->id]);
-                foreach ($blks as $item) {
-                    if ($course_progress) {
-                        if ($users_counter > 0) {
-                            $progresses = array_filter($cw_user_progresses, function($progress) use ($item) {
-                                if ($progress->block_id === $item->id) {
-                                    return true;
-                                }
-                            });
-
-                            $users_progress = 0;
-                            foreach ($progresses as $prog) {
-                                if (in_array($prog->user_id, $course_member_ids)) {
-                                    $users_progress += $prog->grade;
-                                }
-                            }
-
-                            $blocks['progress'] += $users_progress / $users_counter;
-                        }
-                    } else {
-                        $uid = $GLOBALS['user']->id;
-                        $progresses = array_filter($cw_user_progresses, function($progress) use ($item, $uid) {
-                            if ($progress->block_id === $item->id && $progress->user_id === $uid) {
-                                return true;
-                            }
-                        });
-                        $progress = reset($progresses);
-                        if ($progress !== null) {
-                            $blocks['progress'] += intval($progress->grade);
-                        }
-                    }
-                }
-            }
+        $data = [];
+        foreach ($elements as $elementId => $element) {
+            $elementProgress = $progress[$elementId];
+            $cumulative = $elementProgress['cumulative'] / $elementProgress['numberOfNodes'];
+
+            $data[$elementId] = [
+                'id' => (int) $elementId,
+                'parent_id' => (int) $element['parent_id'],
+                'name' => $element['title'],
+                'progress' => [
+                    'cumulative' => round($cumulative, 2) * 100,
+                    'self' => round($elementProgress['self'], 2) * 100,
+                ],
+            ];
         }
 
-        return $blocks;
+        return $data;
     }
 
     private function getChapterCounter(array $chapters): array
@@ -261,13 +254,13 @@ class Course_CoursewareController extends AuthenticatedController
 
         foreach ($chapters as $chapter) {
             if ($chapter['parent_id'] != null) {
-                if ($chapter['progress']['current'] == 0) {
+                if ($chapter['progress']['self'] == 0) {
                     $ahead += 1;
                 }
-                if ($chapter['progress']['current'] > 0 && $chapter['progress']['current'] < 100) {
+                if ($chapter['progress']['self'] > 0 && $chapter['progress']['self'] < 100) {
                     $started += 1;
                 }
-                if ($chapter['progress']['current'] == 100) {
+                if ($chapter['progress']['self'] == 100) {
                     $finished += 1;
                 }
             }
diff --git a/lib/models/Course.class.php b/lib/models/Course.class.php
index f543c88ba9112f40da48c824d1ae3b2a86018f8b..b078c0ca2a875ce49cc88741671545095b972198 100644
--- a/lib/models/Course.class.php
+++ b/lib/models/Course.class.php
@@ -69,6 +69,7 @@
  * @property Course parent belongs_to Course
  * @property SimpleORMapCollection children has_many Course
  * @property CourseConfig config additional field
+ * @property ?\Courseware\StructuralElement $courseware has_one
  */
 
 class Course extends SimpleORMap implements Range, PrivacyObject, StudipItem, FeedbackRange
diff --git a/lib/models/Courseware/Instance.php b/lib/models/Courseware/Instance.php
index 49b7cae28919729bab08865d863a0f00bfa0f052..cc9189c6a8e690bbead142375e2f607f38aa7931 100755
--- a/lib/models/Courseware/Instance.php
+++ b/lib/models/Courseware/Instance.php
@@ -235,4 +235,64 @@ class Instance
     {
         return StructuralElement::findUsersBookmarksByRange($user, $this->getRange());
     }
+
+    public function findAllStructuralElements(): iterable
+    {
+        $sql = 'SELECT se.*
+                FROM cw_structural_elements se
+                WHERE se.range_id = ? AND se.range_type = ?';
+        $statement = \DBManager::get()->prepare($sql);
+        $statement->execute([$this->root['range_id'], $this->root['range_type']]);
+
+        $data = [];
+        foreach ($statement as $key => $row) {
+            $data[] = \Courseware\StructuralElement::build($row, false);
+        }
+
+        return $data;
+    }
+
+    public function findAllBlocks(): iterable
+    {
+        $sql = 'SELECT b.*
+                FROM cw_structural_elements se
+                JOIN cw_containers c ON se.id = c.structural_element_id
+                JOIN cw_blocks b ON c.id = b.container_id
+                WHERE se.range_id = ? AND se.range_type = ?';
+        $statement = \DBManager::get()->prepare($sql);
+        $statement->execute([$this->root['range_id'], $this->root['range_type']]);
+
+        $data = [];
+        foreach ($statement as $key => $row) {
+            $data[] = \Courseware\Block::build($row, false);
+        }
+
+        return $data;
+    }
+
+    public function findAllBlocksGroupedByStructuralElementId(): iterable
+    {
+        $sql = 'SELECT se.id AS structural_element_id, b.*
+                FROM cw_structural_elements se
+                JOIN cw_containers c ON se.id = c.structural_element_id
+                JOIN cw_blocks b ON c.id = b.container_id
+                WHERE se.range_id = ? AND se.range_type = ?';
+
+        $statement = \DBManager::get()->prepare($sql);
+        $statement->execute([$this->root['range_id'], $this->root['range_type']]);
+
+        $data = [];
+        foreach ($statement as $row) {
+            $structuralElementId = $row['structural_element_id'];
+            unset($row['structural_element_id']);
+
+            $block = \Courseware\Block::build($row, false);
+            if (!isset($data[$structuralElementId])) {
+                $data[$structuralElementId] = [];
+            }
+            $data[$structuralElementId][] = $block;
+        }
+
+        return $data;
+    }
 }
diff --git a/resources/assets/stylesheets/scss/courseware.scss b/resources/assets/stylesheets/scss/courseware.scss
index 08016e8cb038589fd9d5aed582ebb6b23228ec47..aa31c438f5421c63a4ed20f72b8a4c0539c2e4dc 100755
--- a/resources/assets/stylesheets/scss/courseware.scss
+++ b/resources/assets/stylesheets/scss/courseware.scss
@@ -104,12 +104,12 @@ c o n t e n t s
         background-position-x: 50%;
         padding: 24px;
         margin-bottom: 10px;
-    
+
         .cw-contents-overview-teaser-content {
             padding-top: 28%;
             padding-left: 0;
             text-align: justify;
-    
+
             header{
                 font-size: 1.5em;
                 margin: 1em 0 0.5em 0;
@@ -434,10 +434,10 @@ $consum_ribbon_width: calc(100% - 58px);
 }
 
 .responsive-display {
-    .cw-ribbon-sticky-top, 
+    .cw-ribbon-sticky-top,
     .cw-ribbon-sticky-bottom,
     .cw-ribbon-wrapper-consume,
-    .cw-ribbon-consume-bottom { 
+    .cw-ribbon-consume-bottom {
         width: 100%;
     }
     .cw-ribbon {
@@ -1994,6 +1994,9 @@ d a s h b o a r d
         width: 404px;
         color: $base-color;
         padding-left: 14px;
+        text-overflow: ellipsis;
+        overflow: hidden;
+        white-space: nowrap;
     }
 }
 
@@ -2587,12 +2590,12 @@ a u d i o  b l o c k
             padding-left: 0;
             list-style: none;
             cursor: pointer;
-    
+
             .cw-playlist-item {
                 @include background-icon(file-audio2, clickable, 24);
                 background-repeat: no-repeat;
                 background-position: 1em center;
-    
+
                 margin: 1em;
                 padding: 1em;
                 padding-left: 4em;
@@ -3637,7 +3640,7 @@ dialog cards block
             right: 0;
         }
 
-        &.cw-dialogcards-prev-disabled, 
+        &.cw-dialogcards-prev-disabled,
         &.cw-dialogcards-next-disabled {
             background-color: $light-gray-color-40;
         }
@@ -3849,7 +3852,7 @@ headline block
     .cw-block-headline {
         .cw-block-headline-content {
             min-height: 300px;
-    
+
             &.half {
                 min-height: 150px;
             }
@@ -3865,7 +3868,7 @@ headline block
                 .icon-layer {
                     background-position: center calc(50% - 4em);
                     min-height: 300px;
-    
+
                     &.half {
                         min-height: 150px;
                     }
@@ -3890,14 +3893,14 @@ headline block
                             @include background-icon($icon, status-green, 98);
                         }
                     };
-    
+
                     &.half {
                         background-size: 72px;
                         background-position: center calc(50% - 2em);
                     }
                 }
-    
-    
+
+
                 .cw-block-headline-textbox {
                     .cw-block-headline-title {
                         h1 {
@@ -3905,7 +3908,7 @@ headline block
                             font-size: 2em;
                         }
                     }
-    
+
                     .cw-block-headline-subtitle {
                         h2 {
                             font-size: 12px;
@@ -3942,7 +3945,7 @@ headline block
                         }
                     };
                 }
-    
+
                 .cw-block-headline-textbox {
                     .cw-block-headline-title {
                         h1 {
@@ -3952,7 +3955,7 @@ headline block
 
                 }
             }
-    
+
             &.ribbon {
                 .icon-layer {
                     min-height: 300px;
@@ -3960,14 +3963,14 @@ headline block
                     &.half {
                         min-height: 150px;
                     }
-    
+
                     .cw-block-headline-textbox {
                         .cw-block-headline-title {
                                 h1 {
                                 font-size: 2.5em;
                             }
                         }
-    
+
                         .cw-block-headline-subtitle {
                             h2 {
                                 font-size: 12px;
diff --git a/resources/vue/components/courseware/CoursewareDashboardProgress.vue b/resources/vue/components/courseware/CoursewareDashboardProgress.vue
index 6f2f4f8547b4ef77c1df35bb7fc8d78b3f5fd7fb..e011c889195e54aa6bfe1702752ddd5ebd699c80 100755
--- a/resources/vue/components/courseware/CoursewareDashboardProgress.vue
+++ b/resources/vue/components/courseware/CoursewareDashboardProgress.vue
@@ -1,33 +1,33 @@
 <template>
     <div class="cw-dashboard-progress">
         <div class="cw-dashboard-progress-breadcrumb">
-            <span v-if="currentChapter.parent_id !== null" @click="getRoot"><studip-icon shape="home" /></span>
-            <span v-if="currentChapter.parent_id !== null" @click="selectChapter(currentChapter.parent_id)">
-                / {{ currentChapter.parent_name }}</span
-            >
+            <span v-if="parent" @click="visitRoot"><studip-icon shape="home" /></span>
+            <span v-if="parent" @click="selectChapter(parent.id)"> / {{ parent.name }}</span>
         </div>
-        <div class="cw-dashboard-progress-chapter">
-                <h1><a :href="chapterUrl">{{ currentChapter.name }}</a></h1>
+        <div class="cw-dashboard-progress-chapter" v-if="selected">
+            <h1>
+                <a :href="chapterUrl">{{ selected.name }}</a>
+            </h1>
             <courseware-progress-circle
                 :title="$gettext('diese Seite inkl. darunter liegende Seiten')"
-                :value="parseInt(currentChapter.progress.total)"
+                :value="parseInt(selected.progress.cumulative)"
             />
             <courseware-progress-circle
                 :title="$gettext('diese Seite')"
                 class="cw-dashboard-progress-current"
-                :value="parseInt(currentChapter.progress.current)"
+                :value="parseInt(selected.progress.self)"
             />
         </div>
         <div class="cw-dashboard-progress-subchapter-list">
             <courseware-dashboard-progress-item
-                v-for="chapter in currentChapter.children"
+                v-for="chapter in children"
                 :key="chapter.id"
                 :name="chapter.name"
-                :value="chapter.progress.total"
+                :value="chapter.progress.cumulative"
                 :chapterId="chapter.id"
                 @selectChapter="selectChapter"
             />
-            <div v-if="currentChapter.children.length === 0">
+            <div v-if="!children.length">
                 <translate>Dieses Seite enthält keine darunter liegenden Seiten</translate>
             </div>
         </div>
@@ -48,44 +48,47 @@ export default {
     },
     data() {
         return {
-            currentProgressData: 0,
+            selected: null,
         };
     },
     computed: {
         progressData() {
             return STUDIP.courseware_progress_data;
         },
-        currentChapter() {
-            return this.progressData[this.currentProgressData];
-        },
         chapterUrl() {
-            return STUDIP.URLHelper.base_url + 'dispatch.php/course/courseware/?cid=' + STUDIP.URLHelper.parameters.cid + '#/structural_element/' + this.currentChapter.id;
+            return (
+                STUDIP.URLHelper.base_url +
+                'dispatch.php/course/courseware/?cid=' +
+                STUDIP.URLHelper.parameters.cid +
+                '#/structural_element/' +
+                this.selected.id
+            );
+        },
+        parent() {
+            if (!this.selected?.parent_id) {
+                return null;
+            }
+
+            return this.progressData[this.selected.parent_id];
+        },
+        children() {
+            if (!this.selected) {
+                return [];
+            }
+
+            return Object.values(this.progressData).filter(({ parent_id }) => parent_id === this.selected.id);
         },
     },
     methods: {
-        getRoot() {
-            this.progressData.every((element, index) => {
-                if (element.parent_id === null) {
-                    this.currentProgressData = index;
-                    return false;
-                } else {
-                    return true;
-                }
-            });
+        visitRoot() {
+            this.selected = Object.values(this.progressData).find(({ parent_id }) => !!parent_id) ?? null;
         },
         selectChapter(id) {
-            this.progressData.every((element, index) => {
-                if (element.id === id) {
-                    this.currentProgressData = index;
-                    return false;
-                } else {
-                    return true;
-                }
-            });
+            this.selected = this.progressData[id] ?? null;
         },
     },
     mounted() {
-        this.getRoot();
+        this.visitRoot();
     },
 };
 </script>
diff --git a/resources/vue/components/courseware/CoursewareDashboardProgressItem.vue b/resources/vue/components/courseware/CoursewareDashboardProgressItem.vue
index 1d340209e4da61f745818966886efcf2749b3b33..adc26b7a87225bc5edb51448638405262b3dfc53 100755
--- a/resources/vue/components/courseware/CoursewareDashboardProgressItem.vue
+++ b/resources/vue/components/courseware/CoursewareDashboardProgressItem.vue
@@ -20,7 +20,7 @@ export default {
     props: {
         name: String,
         value: Number,
-        chapterId: String,
+        chapterId: Number,
     },
 };
 </script>