diff --git a/app/controllers/my_courses.php b/app/controllers/my_courses.php
index 996ad87a60440556ef686db226388637240fc1ed..8216924637ec1504a7a7a46079269b1972fe7ce6 100644
--- a/app/controllers/my_courses.php
+++ b/app/controllers/my_courses.php
@@ -90,7 +90,7 @@ class MyCoursesController extends AuthenticatedController
             'order'               => 'asc',
             'studygroups_enabled' => Config::get()->MY_COURSES_ENABLE_STUDYGROUPS,
             'deputies_enabled'    => Config::get()->DEPUTIES_ENABLE,
-        ]);
+        ]) ?? [];
 
         // Waiting list
         $this->waiting_list = MyRealmModel::getWaitingList($GLOBALS['user']->id);
@@ -182,25 +182,26 @@ class MyCoursesController extends AuthenticatedController
         $this->current_semester = $sem ?: Semester::findCurrent()->semester_id;
         $this->semesters = Semester::findAllVisible();
 
-        $forced_grouping = Config::get()->MY_COURSES_FORCE_GROUPING;
-        if ($forced_grouping == 'not_grouped') {
-            $forced_grouping = 'sem_number';
-        }
-
-        $no_grouping_allowed = ($forced_grouping == 'sem_number' || !in_array($forced_grouping, getValidGroupingFields()));
-
-        $group_field = $GLOBALS['user']->cfg->MY_COURSES_GROUPING ? : $forced_grouping;
+        $group_field = $this->getGroupField();
 
         $groups     = [];
         $add_fields = '';
         $add_query  = '';
 
-        if ($group_field == 'sem_tree_id') {
+        if ($group_field === 'sem_tree_id') {
             $add_fields = ', sem_tree_id';
             $add_query  = "LEFT JOIN seminar_sem_tree sst ON (sst.seminar_id=seminare.Seminar_id)";
-        } else if ($group_field == 'dozent_id') {
+        } elseif ($group_field === 'dozent_id') {
             $add_fields = ', su1.user_id as dozent_id';
             $add_query  = "LEFT JOIN seminar_user as su1 ON (su1.seminar_id=seminare.Seminar_id AND su1.status='dozent')";
+        } elseif ($group_field === 'mvv') {
+            $add_fields = ', mm.`modul_id` AS mvv';
+            $add_query  = "LEFT JOIN `mvv_lvgruppe_seminar` AS mls ON (mls.`seminar_id` = seminare.`Seminar_id`) 
+                           LEFT JOIN `mvv_lvgruppe` AS ml ON (mls.`lvgruppe_id` = ml.`lvgruppe_id`)
+                           LEFT JOIN `mvv_lvgruppe_modulteil` AS mlm on(mls.`lvgruppe_id` = mlm.`lvgruppe_id`)
+                           LEFT JOIN `mvv_modulteil` AS mmt ON (mlm.`modulteil_id` = mmt.`modulteil_id`)
+                           LEFT JOIN `mvv_modul` AS mm ON (mmt.`modul_id` = mm.`modul_id`)";
+                              
         }
 
         $dbv = DbView::getView('sem_tree');
@@ -229,7 +230,7 @@ class MyCoursesController extends AuthenticatedController
         $query .= " ORDER BY sem_nr ASC";
 
         $statement = DBManager::get()->prepare($query);
-        $statement->execute([$GLOBALS['user']->id, studygroup_sem_types()]);
+        $statement->execute([$GLOBALS['user']->id]);
         while ($row = $statement->fetch(PDO::FETCH_ASSOC)) {
             $my_sem[$row['Seminar_id']] = [
                 'obj_type'       => 'sem',
@@ -276,12 +277,11 @@ class MyCoursesController extends AuthenticatedController
             }
         }
         $this->studygroups         = $studygroups;
-        $this->no_grouping_allowed = $no_grouping_allowed;
         $this->groups              = $groups;
         $this->group_names         = get_group_names($group_field, $groups);
         $this->group_field         = $group_field;
         $this->my_sem              = $my_sem;
-        $this->cid                 = Request::get('cid') ? Request::get('cid') : '';
+        $this->cid                 = Request::get('cid', '');
     }
 
     /**
@@ -712,8 +712,22 @@ class MyCoursesController extends AuthenticatedController
      *
      * @param array $sem_courses
      * @param string $group_field
+     * @return array{
+     *     courses: array,
+     *     groups: array,
+     *     user_id: string,
+     *     config: array{
+     *         allow_dozent_visibility: bool,
+     *         open_groups: array,
+     *         sem_number: bool,
+     *         display_type: string,
+     *         responsive_type: string,
+     *         navigation_show_only_new: bool,
+     *         group_by: string
+     *     }
+     * }
      */
-    private function getMyCoursesData($sem_courses, $group_field)
+    private function getMyCoursesData(array $sem_courses, string $group_field): array
     {
         $sem_data = Semester::getAllAsArray();
         $temp_courses = [];
@@ -813,6 +827,11 @@ class MyCoursesController extends AuthenticatedController
             'gruppe'      => _('Farbgruppen'),
             'dozent_id'   => _('Lehrende'),
         ];
+
+        if (LvgruppeSeminar::countBySql('1') > 0) {
+            $groups['mvv'] = _('Modul');
+        }
+
         $views = Sidebar::get()->addWidget(new ViewsWidget());
         $views->setTitle(_('Gruppierung'));
         foreach ($groups as $key => $group) {
diff --git a/app/views/my_courses/groups.php b/app/views/my_courses/groups.php
index c562d97db8e9fb1ca8a408e3f35ee586b2365730..22628c524350b94392e82b4fde7e30f1afb560e6 100644
--- a/app/views/my_courses/groups.php
+++ b/app/views/my_courses/groups.php
@@ -1,4 +1,16 @@
-<form method="post" action="<?= $controller->link_for('my_courses/store_groups/'.$studygroups) ?>" class="default">
+<?php
+/**
+ * @var MyCoursesController $controller
+ * @var bool $studygroups
+ * @var string $cid
+ * @var array $groups
+ * @var array $group_names
+ * @var array $semesters
+ * @var string $group_field
+ * @var string $current_semester
+ */
+?>
+<form method="post" action="<?= $controller->store_groups($studygroups) ?>" class="default">
     <?= CSRFProtection::tokenTag() ?>
 
     <input type="hidden" name="cid" value="<?= htmlReady($cid) ?>">
@@ -24,9 +36,9 @@
                     <th colspan="10" class="toggle-indicator">
                         <a class="toggler" href="#">
                             <? if (is_array($group_names[$group_id])): ?>
-                                <?= htmlReady(my_substr($group_names[$group_id][1] . ' > ' . $group_names[$group_id][0], 0, 70)) ?>
+                                <?= htmlReady($group_names[$group_id][1] . ' > ' . $group_names[$group_id][0]) ?>
                             <? else: ?>
-                                <?= htmlReady(my_substr($group_names[$group_id], 0, 70)) ?>
+                                <?= htmlReady($group_names[$group_id]) ?>
                             <? endif; ?>
                         </a>
                     </th>
diff --git a/lib/classes/MyRealmModel.php b/lib/classes/MyRealmModel.php
index 35232620f859ba3701d4a927da8706b7b1d2a79d..ce5694221bea437225fad48e9dc788221bde0126 100644
--- a/lib/classes/MyRealmModel.php
+++ b/lib/classes/MyRealmModel.php
@@ -333,8 +333,8 @@ class MyRealmModel
             // get teachers only if grouping selected (for better performance)
             if ($group_field === 'dozent_id') {
                 $teachers = new SimpleCollection($course->getMembersWithStatus('dozent'));
-                $teachers->filter(function ($a) use (&$_course) {
-                    return $_course['teachers'][] = $a->user->getFullName('no_title_rev');
+                $_course['teachers'] = $teachers->map(function (CourseMember $a) {
+                    return $a->user->getFullName('no_title_rev');
                 });
             }
 
@@ -344,7 +344,7 @@ class MyRealmModel
             $_course['gruppe']         = !$is_deputy ? $member_ships[$course->id]['gruppe'] ?? null : ($deputy ? $deputy->gruppe : null);
             $_course['sem_number_end'] = $course->isOpenEnded() ? $max_sem_key : Semester::getIndexById($course->end_semester->id);
             $_course['sem_number']     = Semester::getIndexById($course->start_semester->id);
-            $_course['tools']        = $course->tools;
+            $_course['tools']          = $course->tools;
             $_course['name']           = $course->name;
             $_course['temp_name']      = $course->name;
             $_course['number']         = $course->veranstaltungsnummer;
@@ -439,6 +439,11 @@ class MyRealmModel
             self::groupBySemTree($sem_courses);
         }
 
+        // Group by mvv module
+        if ($group_field === 'mvv') {
+            self::groupByMVVModule($sem_courses);
+        }
+
         return $sem_courses ?: null;
     }
 
@@ -771,9 +776,41 @@ class MyRealmModel
                 ksort($_tmp_courses[$sem_key]);
             }
         }
+
         $sem_courses = $_tmp_courses;
     }
 
+    /**
+     * Groups the list of courses by associated mvv module of the course..
+     *
+     * @param array $sem_courses
+     */
+    public static function groupByMVVModule(array &$sem_courses): void
+    {
+        $_tmp_courses = [];
+        foreach ($sem_courses as $sem_key => $collection) {
+            $_tmp_courses[$sem_key] = [];
+            foreach ($collection as $course) {
+                $modules = Course::getMVVModulesForCourseId($course['seminar_id']);
+                if ($modules) {
+                    $modules = array_map(function (Modul $module) {
+                        return $module->getDisplayName();
+                    }, $modules);
+                } else {
+                    $modules = [_('Keinem Modul zugeordnet')];
+                }
+
+                foreach ($modules as $module) {
+                    if (!isset($_tmp_courses[$sem_key][$module])) {
+                        $_tmp_courses[$sem_key][$module] = [];
+                    }
+                    $_tmp_courses[$sem_key][$module][$course['seminar_id']] = $course;
+                }
+            }
+            ksort($_tmp_courses[$sem_key]);
+        }
+        $sem_courses = $_tmp_courses;
+    }
 
     /**
      * Retrieves all study groups for the current user.
diff --git a/lib/meine_seminare_func.inc.php b/lib/meine_seminare_func.inc.php
index f2b49d7da534f8c02a8ec35f1f8fda2734fed978..28cb23b8726db7de49eecc824aae00489b1497e9 100644
--- a/lib/meine_seminare_func.inc.php
+++ b/lib/meine_seminare_func.inc.php
@@ -6,85 +6,86 @@
 
 /**
  *
- * @param unknown_type $group_field
- * @param unknown_type $groups
+ * @param string $group_field
+ * @param array $groups
  */
-function get_group_names($group_field, $groups)
+function get_group_names(string $group_field, array $groups): array
 {
-    global $SEM_TYPE, $SEM_CLASS;
-    $groupcount = 1;
-    if ($group_field == 'sem_tree_id') {
-        $the_tree = TreeAbstract::GetInstance("StudipSemTree", ["build_index" => true]);
-    }
-    if ($group_field == 'sem_number') {
+    $mapper = function (): string {
+        return 'unknown';
+    };
+    if ($group_field === 'sem_number') {
         $all_semester = Semester::findAllVisible();
-    }
-    foreach ($groups as $key => $value) {
-        switch ($group_field){
-            case 'sem_number':
-            $ret[$key] = (string) $all_semester[$key]['name'];
-            break;
-
-            case 'sem_tree_id':
-            if ($the_tree->tree_data[$key]) {
-                //$ret[$key] = $the_tree->getShortPath($key);
-                $ret[$key][0] = $the_tree->tree_data[$key]['name'];
-                $ret[$key][1] = $the_tree->getShortPath($the_tree->tree_data[$key]['parent_id']);
-            } else {
-                //$ret[$key] = _("keine Studienbereiche eingetragen");
-                $ret[$key][0] = _("keine Studienbereiche eingetragen");
-                $ret[$key][1] = '';
+        $mapper = function ($key) use ($all_semester): string {
+            return (string) $all_semester[$key]['name'];
+        };
+    } elseif ($group_field === 'sem_tree_id') {
+        $the_tree = TreeAbstract::GetInstance(StudipSemTree::class, ['build_index' => true]);
+        $mapper = function ($key) use ($the_tree): string {
+            if (!empty($the_tree->tree_data[$key])) {
+                return implode(' > ', array_filter([
+                    $the_tree->getShortPath($the_tree->tree_data[$key]['parent_id']),
+                    $the_tree->tree_data[$key]['name'],
+                ]));
             }
-            break;
 
-            case 'sem_status':
-            $ret[$key] = $SEM_TYPE[$key]["name"]." (". $SEM_CLASS[$SEM_TYPE[$key]["class"]]["name"].")";
-            break;
-
-            case 'not_grouped':
-            $ret[$key] = _("keine Gruppierung");
-            break;
-
-            case 'gruppe':
-            $ret[$key] = _("Gruppe")." ".$groupcount;
-            $groupcount++;
-            break;
-
-            case 'dozent_id':
-            $ret[$key] = get_fullname($key, 'no_title_short');
-            break;
+            return _('keine Studienbereiche eingetragen');
+        };
+    } elseif ($group_field === 'sem_status') {
+        $mapper = function ($key): string {
+            $sem_type = $GLOBALS['SEM_TYPE'][$key];
+            return "{$sem_type['name']} ({$GLOBALS['SEM_CLASS'][$sem_type['class']]['name']})";
+        };
+    } elseif ($group_field === 'no_grouped') {
+        $mapper = function (): string {
+            return _('keine Gruppierung');
+        };
+    } elseif ($group_field === 'gruppe') {
+        $groupcount = 0;
+        $mapper = function () use (&$groupcount): string {
+            $groupcount += 1;
+            return _('Gruppe') . " {$groupcount}";
+        };
+    } elseif ($group_field === 'dozent_id') {
+        $mapper = function ($key): string {
+            return get_fullname($key, 'no_title_short');
+        };
+    } elseif ($group_field === 'mvv') {
+        $mapper = function ($key): string {
+            $module = Modul::find($key);
+            return $module ? (string) $module->getDisplayName() : _('Keinem Modul zugeordnet');
+        };
+    }
 
-            default:
-            $ret[$key] = 'unknown';
-            break;
-        }
+    $result = [];
+    foreach (array_keys($groups) as $key) {
+        $result[$key] = $mapper($key);
     }
-    return $ret;
+    return $result;
 }
 
 /**
  *
- * @param unknown_type $group_field
- * @param unknown_type $groups
+ * @param string $group_field
+ * @param array $groups
  */
 function sort_groups($group_field, &$groups)
 {
-    switch ($group_field){
-
+    switch ($group_field) {
         case 'sem_number':
             krsort($groups, SORT_NUMERIC);
-        break;
+            break;
 
         case 'gruppe':
             ksort($groups, SORT_NUMERIC);
-        break;
+            break;
 
         case 'sem_tree_id':
             uksort($groups, function ($a, $b) {
                 $the_tree = TreeAbstract::GetInstance('StudipSemTree', ['build_index' => true]);
                 return $the_tree->tree_data[$a]['index'] - $the_tree->tree_data[$b]['index'];
             });
-        break;
+            break;
 
         case 'sem_status':
             uksort($groups, function ($a, $b) {
@@ -106,10 +107,23 @@ function sort_groups($group_field, &$groups)
             });
             break;
 
-        default:
+        case 'mvv':
+            uksort($groups, function ($a, $b): int {
+                $module_a = Modul::find($a);
+                $module_b = Modul::find($b);
+
+                if (!$module_a) {
+                    return 1;
+                }
+                if (!$module_b) {
+                    return -1;
+                }
+                return strnatcasecmp($module_a->getDisplayName(), $module_b->getDisplayName());
+            });
+            break;
     }
 
-    foreach ($groups as $key => $value) {
+    foreach ($groups as $key => &$value) {
         usort($value, function ($a, $b) {
             if ($a['gruppe'] != $b['gruppe']) {
                 return (int)($a['gruppe'] - $b['gruppe']);
@@ -121,17 +135,16 @@ function sort_groups($group_field, &$groups)
                 }
             }
         });
-        $groups[$key] = $value;
     }
     return true;
 }
 
 /**
  *
- * @param unknown_type $groups
- * @param unknown_type $my_obj
+ * @param array $groups
+ * @param array $my_obj
  */
-function correct_group_sem_number(&$groups, &$my_obj)
+function correct_group_sem_number(&$groups, &$my_obj): bool
 {
     if (is_array($groups) && is_array($my_obj)) {
         $sem_data = Semester::findAllVisible();
@@ -139,7 +152,9 @@ function correct_group_sem_number(&$groups, &$my_obj)
         //$max_sem = key($sem_data);
         foreach ($sem_data as $sem_key => $one_sem){
             $current_sem = $sem_key;
-            if (!$one_sem['past']) break;
+            if (!$one_sem['past']) {
+                break;
+            }
         }
         if (isset($sem_data[$current_sem + 1])){
             $max_sem = $current_sem + 1;
@@ -151,7 +166,9 @@ function correct_group_sem_number(&$groups, &$my_obj)
                 if ($values['sem_number_end'] == -1 && $values['sem_number'] < $current_sem) {
                     unset($groups[$values['sem_number']][$seminar_id]);
                     fill_groups($groups, $current_sem, ['seminar_id' => $seminar_id, 'name' => $values['name'], 'gruppe' => $values['gruppe']]);
-                    if (!count($groups[$values['sem_number']])) unset($groups[$values['sem_number']]);
+                    if (!count($groups[$values['sem_number']])) {
+                        unset($groups[$values['sem_number']]);
+                    }
                 } else {
                     $to_sem = $values['sem_number_end'];
                     for ($i = $values['sem_number']; $i <= $to_sem; ++$i){
@@ -172,9 +189,9 @@ function correct_group_sem_number(&$groups, &$my_obj)
 
 /**
  *
- * @param unknown_type $my_obj
+ * @param mixed $my_obj
  */
-function add_sem_name(&$my_obj)
+function add_sem_name(&$my_obj): bool
 {
     if ($GLOBALS['user']->cfg->getValue('SHOWSEM_ENABLE')) {
         $sem_data = Semester::findAllVisible();
@@ -195,11 +212,13 @@ function add_sem_name(&$my_obj)
 
 /**
  *
- * @param array $groups
- * @param string $group_key
- * @param array $group_entry
+ * @param array       $groups
+ * @param string|null $group_key
+ * @param array       $group_entry
+ *
+ * @return bool
  */
-function fill_groups(&$groups, $group_key, $group_entry)
+function fill_groups(array &$groups, ?string $group_key, array $group_entry): bool
 {
     if (is_null($group_key)){
         $group_key = 'not_grouped';
@@ -210,16 +229,16 @@ function fill_groups(&$groups, $group_key, $group_entry)
     }
 
     $group_entry['name'] = str_replace(
-        ["ä","ö","ü"],
-        ["ae","oe","ue"],
+        ['ä', 'ö', 'ü'],
+        ['ae', 'oe', 'ue'],
         mb_strtolower($group_entry['name'])
     );
     if (!in_array($group_entry, $groups[$group_key])) {
         $groups[$group_key][$group_entry['seminar_id']] = $group_entry;
         return true;
-    } else {
-        return false;
     }
+
+    return false;
 }
 
 /**
@@ -228,14 +247,20 @@ function fill_groups(&$groups, $group_key, $group_entry)
  *
  * @return array All fields that may be specified for course grouping
  */
-function getValidGroupingFields()
+function getValidGroupingFields(): array
 {
-    return [
+    $valid = [
         'not_grouped',
         'sem_number',
         'sem_tree_id',
         'sem_status',
         'gruppe',
-        'dozent_id'
+        'dozent_id',
     ];
+
+    if (LvgruppeSeminar::countBySql('1') > 0) {
+        $valid[] = 'mvv';
+    }
+
+    return $valid;
 }
diff --git a/lib/models/Course.class.php b/lib/models/Course.class.php
index 2d485490fc2481d1d7c3f0d6a2e4b1d74038b0db..86e9a471c53bc796be826dfc3c1036a28e25e351 100644
--- a/lib/models/Course.class.php
+++ b/lib/models/Course.class.php
@@ -288,6 +288,27 @@ class Course extends SimpleORMap implements Range, PrivacyObject, StudipItem, Fe
 
         return null;
     }
+
+    /**
+     * Returns the associated mvv modules for a given course id.
+     *
+     * @param string $course_id
+     * @return Modul[]
+     */
+    public static function getMVVModulesForCourseId(string $course_id): array
+    {
+        $query = "SELECT mvv_modul.*
+                  FROM mvv_lvgruppe_seminar
+                  JOIN `mvv_lvgruppe` on(`mvv_lvgruppe_seminar`.`lvgruppe_id` = `mvv_lvgruppe`.`lvgruppe_id`)
+                  JOIN `mvv_lvgruppe_modulteil` on(`mvv_lvgruppe_seminar`.`lvgruppe_id` = `mvv_lvgruppe_modulteil`.`lvgruppe_id`)
+                  JOIN `mvv_modulteil` on(`mvv_lvgruppe_modulteil`.`modulteil_id` = `mvv_modulteil`.`modulteil_id`)
+                  JOIN `mvv_modul` on(`mvv_modulteil`.`modul_id` = `mvv_modul`.`modul_id`)
+                  WHERE seminar_id = ?";
+        return DBManager::get()->fetchAll($query, [$course_id], function ($row) {
+            return Modul::buildExisting($row);
+        });
+    }
+
     public function getEnd_Time()
     {
         return $this->duration_time == -1 ? -1 : $this->start_time + $this->duration_time;
@@ -1030,7 +1051,6 @@ class Course extends SimpleORMap implements Range, PrivacyObject, StudipItem, Fe
         return array_filter($this->tools->getStudipModule());
     }
 
-
     /**
      * @see Range::__toString()
      */