diff --git a/.gitlab/issue_templates/StEP.md b/.gitlab/merge_request_templates/StEP.md
similarity index 100%
rename from .gitlab/issue_templates/StEP.md
rename to .gitlab/merge_request_templates/StEP.md
diff --git a/.gitlab/issue_templates/TIC.md b/.gitlab/merge_request_templates/TIC.md
similarity index 100%
rename from .gitlab/issue_templates/TIC.md
rename to .gitlab/merge_request_templates/TIC.md
diff --git a/app/controllers/web_migrate.php b/app/controllers/web_migrate.php
index d155ee6ec66489932bef48cf455ac43a7b488f6b..2637578d98d4e82692842f4e41d48453c87b1ae6 100644
--- a/app/controllers/web_migrate.php
+++ b/app/controllers/web_migrate.php
@@ -18,7 +18,8 @@ class WebMigrateController extends StudipController
         parent::before_filter($action, $args);
 
         $this->target   = Request::int('target');
-        $this->version  = new DBSchemaVersion('studip');
+        $this->branch   = Request::get('branch', '0');
+        $this->version  = new DBSchemaVersion('studip', $this->branch);
         $this->migrator = new Migrator(
             "{$GLOBALS['STUDIP_BASE_PATH']}/db/migrations",
             $this->version,
@@ -45,9 +46,7 @@ class WebMigrateController extends StudipController
 
         $this->lock->lock(['timestamp' => time(), 'user_id' => $GLOBALS['user']->id]);
 
-        foreach (Request::getArray('versions') as $version) {
-            $this->migrator->execute($version, 'up');
-        }
+        $this->migrator->migrateTo($this->target);
 
         $this->lock->release();
 
@@ -78,53 +77,34 @@ class WebMigrateController extends StudipController
 
     public function history_action()
     {
-        $this->history = array_diff_key(
-            $this->migrator->relevantMigrations(0),
-            $this->migrator->relevantMigrations(null)
-        );
-    }
-
-    public function revert_action()
-    {
-        ob_start();
-        set_time_limit(0);
-
-        $this->lock->lock(['timestamp' => time(), 'user_id' => $GLOBALS['user']->id]);
-
-        foreach (Request::getArray('versions') as $version) {
-            $this->migrator->execute($version, 'down');
-        }
-
-        $this->lock->release();
-
-        $announcements = ob_get_clean();
-        PageLayout::postSuccess(
-            _('Die Datenbank wurde erfolgreich migriert.'),
-            array_filter(explode("\n", $announcements))
-        );
-
-        $_SESSION['migration-check'] = [
-            'timestamp' => time(),
-            'count'     => 0,
-        ];
-
-        $this->redirect('history');
+        $this->migrations = $this->migrator->relevantMigrations(0);
+        $this->offset = -1;
+        $this->target = 0;
+        $this->render_action('index');
     }
 
     public function setupSidebar($action)
     {
         $views = Sidebar::get()->addWidget(new ViewsWidget());
         $views->addLink(
-            _('Offene Migrationen'),
-            $this->url_for('index')
+            _('Migrationen ausführen'),
+            $this->url_for('index', ['branch' => $this->branch])
         )->setActive($action === 'index');
         $views->addLink(
-            _('Ausgeführte Migrationen'),
-            $this->url_for('history')
+            _('Migrationen zurücknehmen'),
+            $this->url_for('history', ['branch' => $this->branch])
         )->setActive($action === 'history');
 
+        $widget = new SelectWidget(_('Branch'), $this->url_for($action), 'branch');
+        Sidebar::get()->addWidget($widget);
+
+        foreach ($this->version->getAllBranches() as $branch) {
+            $element = new SelectElement($branch, $branch ?: 'default', $branch == $this->branch);
+            $widget->addElement($element);
+        }
+
         $widget = Sidebar::get()->addWidget(new SidebarWidget());
         $widget->setTitle(_('Aktueller Versionsstand'));
-        $widget->addElement(new WidgetElement($this->version->get()));
+        $widget->addElement(new WidgetElement($this->version->get($this->branch)));
     }
 }
diff --git a/app/views/web_migrate/history.php b/app/views/web_migrate/history.php
deleted file mode 100644
index ed8b853c8dd38ea6203ca372fda55351e8df9d36..0000000000000000000000000000000000000000
--- a/app/views/web_migrate/history.php
+++ /dev/null
@@ -1,83 +0,0 @@
-<? if (STUDIP\ENV === 'development'): ?>
-<form method="post" action="<?= $controller->link_for('revert') ?>">
-    <?= CSRFProtection::tokenTag() ?>
-<? endif; ?>
-    <table class="default" id="migration-list">
-        <caption>
-        <? if (STUDIP\ENV === 'development'): ?>
-            <?= _('Die markierten Anpassungen werden beim Klick auf "Starten" zurückgesetzt:') ?>
-        <? else: ?>
-            <?= _('Diese Anpassungen wurden im System bereits ausgeführt.') ?>
-        <? endif; ?>
-        </caption>
-        <colgroup>
-        <? if (STUDIP\ENV === 'development' && !$lock->isLocked($lock_data)): ?>
-            <col style="width: 24px">
-        <? endif; ?>
-            <col style="width: 120px">
-            <col>
-        </colgroup>
-        <thead>
-            <tr>
-            <? if (STUDIP\ENV === 'development' && !$lock->isLocked($lock_data)): ?>
-                <th>
-                    <input type="checkbox"
-                           data-proxyfor="#migration-list tbody :checkbox"
-                           data-activates="#migration-list tfoot .button">
-                </th>
-            <? endif; ?>
-                <th><?= _('Nr.') ?></th>
-                <th><?= _('Beschreibung') ?></th>
-            </tr>
-        </thead>
-        <tbody>
-        <? foreach ($history as $number => $migration): ?>
-            <tr>
-            <? if (STUDIP\ENV === 'development' && !$lock->isLocked($lock_data)): ?>
-                <td>
-                    <input type="checkbox"
-                           name="versions[]" value="<?= htmlReady($number) ?>">
-                </td>
-            <? endif; ?>
-                <td>
-                    <?= htmlReady($number) ?>
-                </td>
-                <td>
-                <? if ($migration->description()): ?>
-                    <?= htmlReady($migration->description()) ?>
-                <? else: ?>
-                    <em><?= _('keine Beschreibung vorhanden') ?></em>
-                <? endif ?>
-                </td>
-            </tr>
-        <? endforeach; ?>
-        </tbody>
-    <? if (STUDIP\ENV === 'development'): ?>
-        <tfoot>
-            <tr>
-                <td colspan="3">
-                <? if ($lock->isLocked($lock_data)): ?>
-                    <?= MessageBox::info(sprintf(
-                        _('Die Migration wurde %s von %s bereits angestossen und läuft noch.'),
-                        reltime($lock_data['timestamp']),
-                        htmlReady(User::find($lock_data['user_id'])->getFullName()
-                    )), [
-                        sprintf(
-                            _('Sollte während der Migration ein Fehler aufgetreten sein, so können Sie '
-                            . 'diese Sperre durch den unten stehenden Link oder das Löschen der Datei '
-                            . '<em>%s</em> auflösen.'),
-                            $lock->getFilename()
-                        )
-                    ]) ?>
-                    <?= Studip\LinkButton::create(_('Sperre aufheben'), $controller->link_for('release', $target)) ?>
-                <? else: ?>
-                    <?= Studip\Button::createAccept(_('Starten'), 'start')?>
-                <? endif; ?>
-                </td>
-            </tr>
-        </tfoot>
-    <? endif; ?>
-    </table>
-<? if (STUDIP\ENV === 'development'): ?>
-</form>
-<? endif; ?>
diff --git a/app/views/web_migrate/index.php b/app/views/web_migrate/index.php
index c9b972595b8193c70be09e90d30d608026ea752d..6bba52fbf24ed5bc499e4fdf66d0f1bd5247cecf 100644
--- a/app/views/web_migrate/index.php
+++ b/app/views/web_migrate/index.php
@@ -3,54 +3,44 @@
 <? else: ?>
 <form method="post" action="<?= $controller->link_for('migrate') ?>">
     <?= CSRFProtection::tokenTag() ?>
-<? if (isset($target)): ?>
-    <input type="hidden" name="target" value="<?= htmlReady($target) ?>">
-<? endif ?>
-<? if (STUDIP\ENV !== 'development'): ?>
-    <?= addHiddenFields('versions', array_keys($migrations)) ?>
-<? endif; ?>
-
+    <? if (isset($target)): ?>
+        <input type="hidden" name="target" value="<?= htmlReady($target) ?>">
+    <? endif ?>
+    <input type="hidden" name="branch" value="<?= htmlReady($branch) ?>">
 
     <table class="default" id="migration-list">
         <caption>
-        <? if (STUDIP\ENV === 'development'): ?>
-            <?= _('Die markierten Anpassungen werden beim Klick auf "Starten" ausgeführt:') ?>
-        <? else: ?>
             <?= _('Die hier aufgeführten Anpassungen werden beim Klick auf "Starten" ausgeführt:') ?>
-        <? endif; ?>
         </caption>
         <colgroup>
-        <? if (STUDIP\ENV === 'development' && !$lock->isLocked($lock_data)): ?>
             <col style="width: 24px">
-        <? endif; ?>
             <col style="width: 120px">
             <col>
+            <col>
         </colgroup>
         <thead>
             <tr>
-            <? if (STUDIP\ENV === 'development' && !$lock->isLocked($lock_data)): ?>
-                <th>
-                    <input type="checkbox"
-                           data-proxyfor="#migration-list tbody :checkbox"
-                           data-activates="#migration-list tfoot .button">
-                </th>
-            <? endif; ?>
+                <th></th>
                 <th><?= _('Nr.') ?></th>
+                <th><?= _('Name') ?></th>
                 <th><?= _('Beschreibung') ?></th>
             </tr>
         </thead>
         <tbody>
         <? foreach ($migrations as $number => $migration): ?>
+            <? $version = $migrator->migrationBranchAndVersion($number) ?>
             <tr>
-            <? if (STUDIP\ENV === 'development' && !$lock->isLocked($lock_data)): ?>
                 <td>
-                    <input type="checkbox" checked
-                           name="versions[]" value="<?= htmlReady($number) ?>">
+                    <? if ($version[0] === $branch): ?>
+                        <input type="radio" name="target" value="<?= $version[1] + $offset ?>">
+                    <? endif ?>
                 </td>
-            <? endif; ?>
                 <td>
                     <?= htmlReady($number) ?>
                 </td>
+                <td>
+                    <?= htmlReady(get_class($migration)) ?>
+                </td>
                 <td>
                 <? if ($migration->description()): ?>
                     <?= htmlReady($migration->description()) ?>
@@ -63,7 +53,7 @@
         </tbody>
         <tfoot>
             <tr>
-                <td colspan="<?= 2 + (int) (STUDIP\ENV === 'development') ?>">
+                <td colspan="4">
                 <? if ($lock->isLocked($lock_data)):
                     $user = User::find($lock_data['user_id']);
                 ?>
diff --git a/cli/migrate.php b/cli/migrate.php
index 2b43b17b82e9d4d151407d32036cbcde77cc2a57..618132a8b8255fc0ab8f7dbf51e8f9b835d0daf5 100755
--- a/cli/migrate.php
+++ b/cli/migrate.php
@@ -17,14 +17,14 @@ require_once __DIR__ . '/studip_cli_env.inc.php';
 
 if (isset($_SERVER['argv'])) {
     # check for command line options
-    $options = getopt('1:d:lm:t:v');
+    $options = getopt('b:d:lm:t:v');
     if ($options === false) {
         exit(1);
     }
 
     # check for options
-    $single = false;
     $domain = 'studip';
+    $branch = '0';
     $list = false;
     $path = $STUDIP_BASE_PATH . '/db/migrations';
     $verbose = false;
@@ -32,8 +32,8 @@ if (isset($_SERVER['argv'])) {
 
     foreach ($options as $option => $value) {
         switch ($option) {
-            case '1':
-                $single = (string) $value;
+            case 'b':
+                $branch = (string) $value;
                 break;
             case 'd':
                 $domain = (string) $value;
@@ -53,7 +53,7 @@ if (isset($_SERVER['argv'])) {
         }
     }
 
-    $version = new DBSchemaVersion($domain);
+    $version = new DBSchemaVersion($domain, $branch);
     $migrator = new Migrator($path, $version, $verbose);
 
     if ($list) {
@@ -61,15 +61,8 @@ if (isset($_SERVER['argv'])) {
 
         foreach ($migrations as $number => $migration) {
             $description = $migration->description() ?: '(no description)';
-            printf("%3d %s\n", $number, $description);
+            printf("%6s %-20s %s\n", $number, get_class($migration), $description);
         }
-    } elseif ($single) {
-        $direction = 'up';
-        if ($single[0] === '-') {
-            $direction = 'down';
-            $single = substr($single, 1);
-        }
-        $migrator->execute($single, $direction);
     } else {
         $migrator->migrateTo($target);
     }
diff --git a/cli/plugin_manager b/cli/plugin_manager
index 9a065fe5bc4c610906d6c7a5e5e226f06590fce8..e566c75a13ef419f74a5b375debda62637c29d2c 100755
--- a/cli/plugin_manager
+++ b/cli/plugin_manager
@@ -132,18 +132,22 @@ if ($args) {
 
             // show usage
             if (!$pluginname) {
-                echo 'Usage: '. $args[0] .' migrate PLUGINNAME [-l] [-t] [-v]' . "\n";
+                echo 'Usage: '. $args[0] .' migrate PLUGINNAME [-l] [-v] [-t target] [-b branch]' . "\n";
                 exit(1);
             }
 
             // parse options
-            list($errors, $options, $args) = getopts(array('l' => 'Ss l list', 'v' => 'Ss v verbose', 't'=> 'Vs t target'));
+            list($errors, $options, $args) = getopts(array('l' => 'Ss l list', 'v' => 'Ss v verbose', 't' => 'Vs t target', 'b' => 'Vs b branch'));
             $list = false;
             $verbose = false;
             $target = NULL;
+            $branch = '0';
 
             foreach ($options as $option => $value) {
                 switch ($option) {
+                    case 'b':
+                        $branch = ($value === false) ? '0' : $value;
+                        break;
                     case 'l':
                         $list = $value;
                         break;
@@ -166,7 +170,7 @@ if ($args) {
 
                     if (is_dir($plugindir . '/migrations')) {
                         // if there are migrations, migrate
-                        $schema_version = new DBSchemaVersion($plugin['name']);
+                        $schema_version = new DBSchemaVersion($plugin['name'], $branch);
                         $migrator = new Migrator($plugindir . '/migrations', $schema_version, $verbose);
 
                         if ($list) {
@@ -175,7 +179,7 @@ if ($args) {
                             foreach ($migrations as $number => $migration) {
                                 $description = $migration->description() ?: '(no description)';
 
-                                printf("%3d %-20s %s\n", $number, get_class($migration), $description);
+                                printf("%6s %-20s %s\n", $number, get_class($migration), $description);
                             }
                         } else {
                             $migrator->migrateTo($target);
@@ -214,7 +218,7 @@ if ($args) {
                     if (is_dir($plugindir . '/migrations')) {
                         $schema_version = new DBSchemaVersion($plugin['name']);
                         $migrator = new Migrator($plugindir . '/migrations', $schema_version);
-                        $migrator->migrate_to(0);
+                        $migrator->migrateTo(0);
                     }
 
                     echo 'Das Plugin '. $plugin['name'] .' wurde ausgetragen.' . "\n";
diff --git a/db/migrations/100_step00248_chat_extinction.php b/db/migrations/1.100_step00248_chat_extinction.php
similarity index 100%
rename from db/migrations/100_step00248_chat_extinction.php
rename to db/migrations/1.100_step00248_chat_extinction.php
diff --git a/db/migrations/101_step00246_blubber.php b/db/migrations/1.101_step00246_blubber.php
similarity index 100%
rename from db/migrations/101_step00246_blubber.php
rename to db/migrations/1.101_step00246_blubber.php
diff --git a/db/migrations/102_remove_guestbook_migration.php b/db/migrations/1.102_remove_guestbook_migration.php
similarity index 100%
rename from db/migrations/102_remove_guestbook_migration.php
rename to db/migrations/1.102_remove_guestbook_migration.php
diff --git a/db/migrations/104_setup_cronjobs.php b/db/migrations/1.104_setup_cronjobs.php
similarity index 100%
rename from db/migrations/104_setup_cronjobs.php
rename to db/migrations/1.104_setup_cronjobs.php
diff --git a/db/migrations/105_step_00247_forum.php b/db/migrations/1.105_step_00247_forum.php
similarity index 100%
rename from db/migrations/105_step_00247_forum.php
rename to db/migrations/1.105_step_00247_forum.php
diff --git a/db/migrations/106_step_00247_forum_data_migration.php b/db/migrations/1.106_step_00247_forum_data_migration.php
similarity index 100%
rename from db/migrations/106_step_00247_forum_data_migration.php
rename to db/migrations/1.106_step_00247_forum_data_migration.php
diff --git a/db/migrations/107_step00247_forum_performance.php b/db/migrations/1.107_step00247_forum_performance.php
similarity index 100%
rename from db/migrations/107_step00247_forum_performance.php
rename to db/migrations/1.107_step00247_forum_performance.php
diff --git a/db/migrations/108_visibilityapi.php b/db/migrations/1.108_visibilityapi.php
similarity index 100%
rename from db/migrations/108_visibilityapi.php
rename to db/migrations/1.108_visibilityapi.php
diff --git a/db/migrations/109_init_custom_blubber_streams.php b/db/migrations/1.109_init_custom_blubber_streams.php
similarity index 100%
rename from db/migrations/109_init_custom_blubber_streams.php
rename to db/migrations/1.109_init_custom_blubber_streams.php
diff --git a/db/migrations/10_image_proxy.php b/db/migrations/1.10_image_proxy.php
similarity index 100%
rename from db/migrations/10_image_proxy.php
rename to db/migrations/1.10_image_proxy.php
diff --git a/db/migrations/110_scm_add_position.php b/db/migrations/1.110_scm_add_position.php
similarity index 100%
rename from db/migrations/110_scm_add_position.php
rename to db/migrations/1.110_scm_add_position.php
diff --git a/db/migrations/111_step_3574_domain.php b/db/migrations/1.111_step_3574_domain.php
similarity index 100%
rename from db/migrations/111_step_3574_domain.php
rename to db/migrations/1.111_step_3574_domain.php
diff --git a/db/migrations/113_init_mailqueue.php b/db/migrations/1.113_init_mailqueue.php
similarity index 100%
rename from db/migrations/113_init_mailqueue.php
rename to db/migrations/1.113_init_mailqueue.php
diff --git a/db/migrations/114_create_table_blubber_reshares.php b/db/migrations/1.114_create_table_blubber_reshares.php
similarity index 100%
rename from db/migrations/114_create_table_blubber_reshares.php
rename to db/migrations/1.114_create_table_blubber_reshares.php
diff --git a/db/migrations/115_performance_tic_3759.php b/db/migrations/1.115_performance_tic_3759.php
similarity index 100%
rename from db/migrations/115_performance_tic_3759.php
rename to db/migrations/1.115_performance_tic_3759.php
diff --git a/db/migrations/116_step_00263_inst_gendering.php b/db/migrations/1.116_step_00263_inst_gendering.php
similarity index 100%
rename from db/migrations/116_step_00263_inst_gendering.php
rename to db/migrations/1.116_step_00263_inst_gendering.php
diff --git a/db/migrations/117_forum_add_close.php b/db/migrations/1.117_forum_add_close.php
similarity index 100%
rename from db/migrations/117_forum_add_close.php
rename to db/migrations/1.117_forum_add_close.php
diff --git a/db/migrations/118_forum_sticky_posts.php b/db/migrations/1.118_forum_sticky_posts.php
similarity index 100%
rename from db/migrations/118_forum_sticky_posts.php
rename to db/migrations/1.118_forum_sticky_posts.php
diff --git a/db/migrations/119_init_termin_related_groups_table.php b/db/migrations/1.119_init_termin_related_groups_table.php
similarity index 100%
rename from db/migrations/119_init_termin_related_groups_table.php
rename to db/migrations/1.119_init_termin_related_groups_table.php
diff --git a/db/migrations/11_lock_rulez.php b/db/migrations/1.11_lock_rulez.php
similarity index 100%
rename from db/migrations/11_lock_rulez.php
rename to db/migrations/1.11_lock_rulez.php
diff --git a/db/migrations/120_create_open_graph_data_table.php b/db/migrations/1.120_create_open_graph_data_table.php
similarity index 100%
rename from db/migrations/120_create_open_graph_data_table.php
rename to db/migrations/1.120_create_open_graph_data_table.php
diff --git a/db/migrations/121_step_00266_forced_lock_rules.php b/db/migrations/1.121_step_00266_forced_lock_rules.php
similarity index 100%
rename from db/migrations/121_step_00266_forced_lock_rules.php
rename to db/migrations/1.121_step_00266_forced_lock_rules.php
diff --git a/db/migrations/122_add_seminar_id_to_folder.php b/db/migrations/1.122_add_seminar_id_to_folder.php
similarity index 100%
rename from db/migrations/122_add_seminar_id_to_folder.php
rename to db/migrations/1.122_add_seminar_id_to_folder.php
diff --git a/db/migrations/123_tic_3993_remove_dont_delete.php b/db/migrations/1.123_tic_3993_remove_dont_delete.php
similarity index 100%
rename from db/migrations/123_tic_3993_remove_dont_delete.php
rename to db/migrations/1.123_tic_3993_remove_dont_delete.php
diff --git a/db/migrations/124_step_00255_important_semnumber.php b/db/migrations/1.124_step_00255_important_semnumber.php
similarity index 100%
rename from db/migrations/124_step_00255_important_semnumber.php
rename to db/migrations/1.124_step_00255_important_semnumber.php
diff --git a/db/migrations/125_repair_statusgroup_user_numberation.php b/db/migrations/1.125_repair_statusgroup_user_numberation.php
similarity index 100%
rename from db/migrations/125_repair_statusgroup_user_numberation.php
rename to db/migrations/1.125_repair_statusgroup_user_numberation.php
diff --git a/db/migrations/126_tic_4044_invisible_studygroups.php b/db/migrations/1.126_tic_4044_invisible_studygroups.php
similarity index 100%
rename from db/migrations/126_tic_4044_invisible_studygroups.php
rename to db/migrations/1.126_tic_4044_invisible_studygroups.php
diff --git a/db/migrations/127_setup_api.php b/db/migrations/1.127_setup_api.php
similarity index 100%
rename from db/migrations/127_setup_api.php
rename to db/migrations/1.127_setup_api.php
diff --git a/db/migrations/128_step00240_coursesets.php b/db/migrations/1.128_step00240_coursesets.php
similarity index 100%
rename from db/migrations/128_step00240_coursesets.php
rename to db/migrations/1.128_step00240_coursesets.php
diff --git a/db/migrations/129_wysiwyg_config_option.php b/db/migrations/1.129_wysiwyg_config_option.php
similarity index 100%
rename from db/migrations/129_wysiwyg_config_option.php
rename to db/migrations/1.129_wysiwyg_config_option.php
diff --git a/db/migrations/12_step_120_userpic.php b/db/migrations/1.12_step_120_userpic.php
similarity index 100%
rename from db/migrations/12_step_120_userpic.php
rename to db/migrations/1.12_step_120_userpic.php
diff --git a/db/migrations/130_step_00267_preliminary_accounts.php b/db/migrations/1.130_step_00267_preliminary_accounts.php
similarity index 100%
rename from db/migrations/130_step_00267_preliminary_accounts.php
rename to db/migrations/1.130_step_00267_preliminary_accounts.php
diff --git a/db/migrations/131_score_config_option.php b/db/migrations/1.131_score_config_option.php
similarity index 100%
rename from db/migrations/131_score_config_option.php
rename to db/migrations/1.131_score_config_option.php
diff --git a/db/migrations/132_step_00268_event_log.php b/db/migrations/1.132_step_00268_event_log.php
similarity index 100%
rename from db/migrations/132_step_00268_event_log.php
rename to db/migrations/1.132_step_00268_event_log.php
diff --git a/db/migrations/133_tic_4072_new_password_hashing.php b/db/migrations/1.133_tic_4072_new_password_hashing.php
similarity index 100%
rename from db/migrations/133_tic_4072_new_password_hashing.php
rename to db/migrations/1.133_tic_4072_new_password_hashing.php
diff --git a/db/migrations/134_step_00269_plugin_roles.php b/db/migrations/1.134_step_00269_plugin_roles.php
similarity index 100%
rename from db/migrations/134_step_00269_plugin_roles.php
rename to db/migrations/1.134_step_00269_plugin_roles.php
diff --git a/db/migrations/135_tic_4011_remove_teilnehmer_view.php b/db/migrations/1.135_tic_4011_remove_teilnehmer_view.php
similarity index 100%
rename from db/migrations/135_tic_4011_remove_teilnehmer_view.php
rename to db/migrations/1.135_tic_4011_remove_teilnehmer_view.php
diff --git a/db/migrations/136_wiki_remove_camel_case.php b/db/migrations/1.136_wiki_remove_camel_case.php
similarity index 100%
rename from db/migrations/136_wiki_remove_camel_case.php
rename to db/migrations/1.136_wiki_remove_camel_case.php
diff --git a/db/migrations/137_studygroup_invitations.php b/db/migrations/1.137_studygroup_invitations.php
similarity index 100%
rename from db/migrations/137_studygroup_invitations.php
rename to db/migrations/1.137_studygroup_invitations.php
diff --git a/db/migrations/138_create_tags_for_messaging.php b/db/migrations/1.138_create_tags_for_messaging.php
similarity index 100%
rename from db/migrations/138_create_tags_for_messaging.php
rename to db/migrations/1.138_create_tags_for_messaging.php
diff --git a/db/migrations/139_step00272_tours.php b/db/migrations/1.139_step00272_tours.php
similarity index 100%
rename from db/migrations/139_step00272_tours.php
rename to db/migrations/1.139_step00272_tours.php
diff --git a/db/migrations/13_remove_fields_from_extermine.php b/db/migrations/1.13_remove_fields_from_extermine.php
similarity index 100%
rename from db/migrations/13_remove_fields_from_extermine.php
rename to db/migrations/1.13_remove_fields_from_extermine.php
diff --git a/db/migrations/140_tic4450_archiv_protected_files.php b/db/migrations/1.140_tic4450_archiv_protected_files.php
similarity index 100%
rename from db/migrations/140_tic4450_archiv_protected_files.php
rename to db/migrations/1.140_tic4450_archiv_protected_files.php
diff --git a/db/migrations/141_tic4454_roomrequest_options.php b/db/migrations/1.141_tic4454_roomrequest_options.php
similarity index 100%
rename from db/migrations/141_tic4454_roomrequest_options.php
rename to db/migrations/1.141_tic4454_roomrequest_options.php
diff --git a/db/migrations/142_tic4463_remove_stm.php b/db/migrations/1.142_tic4463_remove_stm.php
similarity index 100%
rename from db/migrations/142_tic4463_remove_stm.php
rename to db/migrations/1.142_tic4463_remove_stm.php
diff --git a/db/migrations/143_step00249_my_courses_config.php b/db/migrations/1.143_step00249_my_courses_config.php
similarity index 100%
rename from db/migrations/143_step00249_my_courses_config.php
rename to db/migrations/1.143_step00249_my_courses_config.php
diff --git a/db/migrations/144_files.php b/db/migrations/1.144_files.php
similarity index 100%
rename from db/migrations/144_files.php
rename to db/migrations/1.144_files.php
diff --git a/db/migrations/145_tic4491_help_content.php b/db/migrations/1.145_tic4491_help_content.php
similarity index 100%
rename from db/migrations/145_tic4491_help_content.php
rename to db/migrations/1.145_tic4491_help_content.php
diff --git a/db/migrations/146_step_00253_startseite.php b/db/migrations/1.146_step_00253_startseite.php
similarity index 100%
rename from db/migrations/146_step_00253_startseite.php
rename to db/migrations/1.146_step_00253_startseite.php
diff --git a/db/migrations/148_tic_4520_sem_tree_display.php b/db/migrations/1.148_tic_4520_sem_tree_display.php
similarity index 100%
rename from db/migrations/148_tic_4520_sem_tree_display.php
rename to db/migrations/1.148_tic_4520_sem_tree_display.php
diff --git a/db/migrations/14_step_00123_admission2.php b/db/migrations/1.14_step_00123_admission2.php
similarity index 100%
rename from db/migrations/14_step_00123_admission2.php
rename to db/migrations/1.14_step_00123_admission2.php
diff --git a/db/migrations/150_help_tours_and_content.php b/db/migrations/1.150_help_tours_and_content.php
similarity index 100%
rename from db/migrations/150_help_tours_and_content.php
rename to db/migrations/1.150_help_tours_and_content.php
diff --git a/db/migrations/151_add_automatic_updates_to_plugins.php b/db/migrations/1.151_add_automatic_updates_to_plugins.php
similarity index 100%
rename from db/migrations/151_add_automatic_updates_to_plugins.php
rename to db/migrations/1.151_add_automatic_updates_to_plugins.php
diff --git a/db/migrations/152_tic_5117_course_member_admission.php b/db/migrations/1.152_tic_5117_course_member_admission.php
similarity index 100%
rename from db/migrations/152_tic_5117_course_member_admission.php
rename to db/migrations/1.152_tic_5117_course_member_admission.php
diff --git a/db/migrations/153_tic_4163_add_studip_shortname.php b/db/migrations/1.153_tic_4163_add_studip_shortname.php
similarity index 100%
rename from db/migrations/153_tic_4163_add_studip_shortname.php
rename to db/migrations/1.153_tic_4163_add_studip_shortname.php
diff --git a/db/migrations/154_recalculate_score.php b/db/migrations/1.154_recalculate_score.php
similarity index 100%
rename from db/migrations/154_recalculate_score.php
rename to db/migrations/1.154_recalculate_score.php
diff --git a/db/migrations/155_tic_5170_clean_up.php b/db/migrations/1.155_tic_5170_clean_up.php
similarity index 100%
rename from db/migrations/155_tic_5170_clean_up.php
rename to db/migrations/1.155_tic_5170_clean_up.php
diff --git a/db/migrations/156_tic_5204_add_datafield_type.php b/db/migrations/1.156_tic_5204_add_datafield_type.php
similarity index 100%
rename from db/migrations/156_tic_5204_add_datafield_type.php
rename to db/migrations/1.156_tic_5204_add_datafield_type.php
diff --git a/db/migrations/157_contact_rework.php b/db/migrations/1.157_contact_rework.php
similarity index 100%
rename from db/migrations/157_contact_rework.php
rename to db/migrations/1.157_contact_rework.php
diff --git a/db/migrations/158_step_00283_calendar_sorm.php b/db/migrations/1.158_step_00283_calendar_sorm.php
similarity index 100%
rename from db/migrations/158_step_00283_calendar_sorm.php
rename to db/migrations/1.158_step_00283_calendar_sorm.php
diff --git a/db/migrations/159_step_00275_plus.php b/db/migrations/1.159_step_00275_plus.php
similarity index 100%
rename from db/migrations/159_step_00275_plus.php
rename to db/migrations/1.159_step_00275_plus.php
diff --git a/db/migrations/15_step_00129_email_restriction.php b/db/migrations/1.15_step_00129_email_restriction.php
similarity index 100%
rename from db/migrations/15_step_00129_email_restriction.php
rename to db/migrations/1.15_step_00129_email_restriction.php
diff --git a/db/migrations/160_step_00283_update_calendar_settings.php b/db/migrations/1.160_step_00283_update_calendar_settings.php
similarity index 100%
rename from db/migrations/160_step_00283_update_calendar_settings.php
rename to db/migrations/1.160_step_00283_update_calendar_settings.php
diff --git a/db/migrations/161_step_00284_help_editor.php b/db/migrations/1.161_step_00284_help_editor.php
similarity index 100%
rename from db/migrations/161_step_00284_help_editor.php
rename to db/migrations/1.161_step_00284_help_editor.php
diff --git a/db/migrations/162_step_00283_calendar_user.php b/db/migrations/1.162_step_00283_calendar_user.php
similarity index 100%
rename from db/migrations/162_step_00283_calendar_user.php
rename to db/migrations/1.162_step_00283_calendar_user.php
diff --git a/db/migrations/163_transfer_calpermission.php b/db/migrations/1.163_transfer_calpermission.php
similarity index 100%
rename from db/migrations/163_transfer_calpermission.php
rename to db/migrations/1.163_transfer_calpermission.php
diff --git a/db/migrations/164_help_tours_en.php b/db/migrations/1.164_help_tours_en.php
similarity index 100%
rename from db/migrations/164_help_tours_en.php
rename to db/migrations/1.164_help_tours_en.php
diff --git a/db/migrations/166_add_cache_operations_table.php b/db/migrations/1.166_add_cache_operations_table.php
similarity index 100%
rename from db/migrations/166_add_cache_operations_table.php
rename to db/migrations/1.166_add_cache_operations_table.php
diff --git a/db/migrations/167_tic5661_setup_cronjob.php b/db/migrations/1.167_tic5661_setup_cronjob.php
similarity index 100%
rename from db/migrations/167_tic5661_setup_cronjob.php
rename to db/migrations/1.167_tic5661_setup_cronjob.php
diff --git a/db/migrations/168_tic5661_add_config.php b/db/migrations/1.168_tic5661_add_config.php
similarity index 100%
rename from db/migrations/168_tic5661_add_config.php
rename to db/migrations/1.168_tic5661_add_config.php
diff --git a/db/migrations/16_step_00126_embedding_flash_movies.php b/db/migrations/1.16_step_00126_embedding_flash_movies.php
similarity index 100%
rename from db/migrations/16_step_00126_embedding_flash_movies.php
rename to db/migrations/1.16_step_00126_embedding_flash_movies.php
diff --git a/db/migrations/170_step_00286_coursewizard.php b/db/migrations/1.170_step_00286_coursewizard.php
similarity index 100%
rename from db/migrations/170_step_00286_coursewizard.php
rename to db/migrations/1.170_step_00286_coursewizard.php
diff --git a/db/migrations/171_open_personal_file_areas.php b/db/migrations/1.171_open_personal_file_areas.php
similarity index 100%
rename from db/migrations/171_open_personal_file_areas.php
rename to db/migrations/1.171_open_personal_file_areas.php
diff --git a/db/migrations/172_tic_5961_add_config_default_sem.php b/db/migrations/1.172_tic_5961_add_config_default_sem.php
similarity index 100%
rename from db/migrations/172_tic_5961_add_config_default_sem.php
rename to db/migrations/1.172_tic_5961_add_config_default_sem.php
diff --git a/db/migrations/173_biest5982_fix_resources_objects_level.php b/db/migrations/1.173_biest5982_fix_resources_objects_level.php
similarity index 100%
rename from db/migrations/173_biest5982_fix_resources_objects_level.php
rename to db/migrations/1.173_biest5982_fix_resources_objects_level.php
diff --git a/db/migrations/174_tic_6018_clean_news.php b/db/migrations/1.174_tic_6018_clean_news.php
similarity index 100%
rename from db/migrations/174_tic_6018_clean_news.php
rename to db/migrations/1.174_tic_6018_clean_news.php
diff --git a/db/migrations/175_biest6024_fix_help_tours_en.php b/db/migrations/1.175_biest6024_fix_help_tours_en.php
similarity index 100%
rename from db/migrations/175_biest6024_fix_help_tours_en.php
rename to db/migrations/1.175_biest6024_fix_help_tours_en.php
diff --git a/db/migrations/176_limit_mailqueue.php b/db/migrations/1.176_limit_mailqueue.php
similarity index 100%
rename from db/migrations/176_limit_mailqueue.php
rename to db/migrations/1.176_limit_mailqueue.php
diff --git a/db/migrations/177_tic5415_plugin_assets.php b/db/migrations/1.177_tic5415_plugin_assets.php
similarity index 100%
rename from db/migrations/177_tic5415_plugin_assets.php
rename to db/migrations/1.177_tic5415_plugin_assets.php
diff --git a/db/migrations/178_change_opengraph_data_pk.php b/db/migrations/1.178_change_opengraph_data_pk.php
similarity index 100%
rename from db/migrations/178_change_opengraph_data_pk.php
rename to db/migrations/1.178_change_opengraph_data_pk.php
diff --git a/db/migrations/179_tic6188_view_resource_occupation.php b/db/migrations/1.179_tic6188_view_resource_occupation.php
similarity index 100%
rename from db/migrations/179_tic6188_view_resource_occupation.php
rename to db/migrations/1.179_tic6188_view_resource_occupation.php
diff --git a/db/migrations/17_db_optimierung_kontingentierung.php b/db/migrations/1.17_db_optimierung_kontingentierung.php
similarity index 100%
rename from db/migrations/17_db_optimierung_kontingentierung.php
rename to db/migrations/1.17_db_optimierung_kontingentierung.php
diff --git a/db/migrations/180_add_seminar_is_complete_status.php b/db/migrations/1.180_add_seminar_is_complete_status.php
similarity index 100%
rename from db/migrations/180_add_seminar_is_complete_status.php
rename to db/migrations/1.180_add_seminar_is_complete_status.php
diff --git a/db/migrations/181_extend_wiki_size.php b/db/migrations/1.181_extend_wiki_size.php
similarity index 100%
rename from db/migrations/181_extend_wiki_size.php
rename to db/migrations/1.181_extend_wiki_size.php
diff --git a/db/migrations/182_step_raum_zeit_end_offset.php b/db/migrations/1.182_step_raum_zeit_end_offset.php
similarity index 100%
rename from db/migrations/182_step_raum_zeit_end_offset.php
rename to db/migrations/1.182_step_raum_zeit_end_offset.php
diff --git a/db/migrations/183_tic6000_datafields_visibility.php b/db/migrations/1.183_tic6000_datafields_visibility.php
similarity index 100%
rename from db/migrations/183_tic6000_datafields_visibility.php
rename to db/migrations/1.183_tic6000_datafields_visibility.php
diff --git a/db/migrations/184_add_wiki_help_tours.php b/db/migrations/1.184_add_wiki_help_tours.php
similarity index 100%
rename from db/migrations/184_add_wiki_help_tours.php
rename to db/migrations/1.184_add_wiki_help_tours.php
diff --git a/db/migrations/185_tic6025_performance.php b/db/migrations/1.185_tic6025_performance.php
similarity index 100%
rename from db/migrations/185_tic6025_performance.php
rename to db/migrations/1.185_tic6025_performance.php
diff --git a/db/migrations/186_step_00294_innodb.php b/db/migrations/1.186_step_00294_innodb.php
similarity index 100%
rename from db/migrations/186_step_00294_innodb.php
rename to db/migrations/1.186_step_00294_innodb.php
diff --git a/db/migrations/187_step_291_questionnaires.php b/db/migrations/1.187_step_291_questionnaires.php
similarity index 100%
rename from db/migrations/187_step_291_questionnaires.php
rename to db/migrations/1.187_step_291_questionnaires.php
diff --git a/db/migrations/188_step_00296_appointment_requests.php b/db/migrations/1.188_step_00296_appointment_requests.php
similarity index 100%
rename from db/migrations/188_step_00296_appointment_requests.php
rename to db/migrations/1.188_step_00296_appointment_requests.php
diff --git a/db/migrations/189_step_00288_sem_class_raumzeit.php b/db/migrations/1.189_step_00288_sem_class_raumzeit.php
similarity index 100%
rename from db/migrations/189_step_00288_sem_class_raumzeit.php
rename to db/migrations/1.189_step_00288_sem_class_raumzeit.php
diff --git a/db/migrations/18_step_00139_upload_file_reorg.php b/db/migrations/1.18_step_00139_upload_file_reorg.php
similarity index 83%
rename from db/migrations/18_step_00139_upload_file_reorg.php
rename to db/migrations/1.18_step_00139_upload_file_reorg.php
index cb301540d6555b0f5cf5c74999368f61319ba724..51201e2a54e9f686ed775fd39780e640ff7c1327 100644
--- a/db/migrations/18_step_00139_upload_file_reorg.php
+++ b/db/migrations/1.18_step_00139_upload_file_reorg.php
@@ -1,14 +1,4 @@
 <?php
-$requirements = [
-    __DIR__ . '/lib/datei.inc.php',
-];
-
-foreach ($requirements as $file) {
-    if (!file_exists($file)) {
-        throw new Exception('Migration is not compatible with your Stud.IP version');
-    }
-    require_once $file;
-}
 
 class Step00139UploadFileReorg extends Migration
 {
diff --git a/db/migrations/190_allow_topics_to_be_public.php b/db/migrations/1.190_allow_topics_to_be_public.php
similarity index 100%
rename from db/migrations/190_allow_topics_to_be_public.php
rename to db/migrations/1.190_allow_topics_to_be_public.php
diff --git a/db/migrations/191_add_dedicated_admins_role.php b/db/migrations/1.191_add_dedicated_admins_role.php
similarity index 100%
rename from db/migrations/191_add_dedicated_admins_role.php
rename to db/migrations/1.191_add_dedicated_admins_role.php
diff --git a/db/migrations/192_add_user_config_for_admin_display_settings.php b/db/migrations/1.192_add_user_config_for_admin_display_settings.php
similarity index 100%
rename from db/migrations/192_add_user_config_for_admin_display_settings.php
rename to db/migrations/1.192_add_user_config_for_admin_display_settings.php
diff --git a/db/migrations/193_tic6653_add_advanced_coursewizardstep.php b/db/migrations/1.193_tic6653_add_advanced_coursewizardstep.php
similarity index 100%
rename from db/migrations/193_tic6653_add_advanced_coursewizardstep.php
rename to db/migrations/1.193_tic6653_add_advanced_coursewizardstep.php
diff --git a/db/migrations/194_tic_6576_preferential_admission.php b/db/migrations/1.194_tic_6576_preferential_admission.php
similarity index 100%
rename from db/migrations/194_tic_6576_preferential_admission.php
rename to db/migrations/1.194_tic_6576_preferential_admission.php
diff --git a/db/migrations/195_tic_6655_admissionrule_compatibility.php b/db/migrations/1.195_tic_6655_admissionrule_compatibility.php
similarity index 100%
rename from db/migrations/195_tic_6655_admissionrule_compatibility.php
rename to db/migrations/1.195_tic_6655_admissionrule_compatibility.php
diff --git a/db/migrations/196_extend_ressources.php b/db/migrations/1.196_extend_ressources.php
similarity index 100%
rename from db/migrations/196_extend_ressources.php
rename to db/migrations/1.196_extend_ressources.php
diff --git a/db/migrations/197_step_00301_admission_conditiongroups.php b/db/migrations/1.197_step_00301_admission_conditiongroups.php
similarity index 100%
rename from db/migrations/197_step_00301_admission_conditiongroups.php
rename to db/migrations/1.197_step_00301_admission_conditiongroups.php
diff --git a/db/migrations/198_add_activities.php b/db/migrations/1.198_add_activities.php
similarity index 100%
rename from db/migrations/198_add_activities.php
rename to db/migrations/1.198_add_activities.php
diff --git a/db/migrations/199_step_00302_modulverwaltung.php b/db/migrations/1.199_step_00302_modulverwaltung.php
similarity index 100%
rename from db/migrations/199_step_00302_modulverwaltung.php
rename to db/migrations/1.199_step_00302_modulverwaltung.php
diff --git a/db/migrations/19_step_00141_zip_download_restrictions.php b/db/migrations/1.19_step_00141_zip_download_restrictions.php
similarity index 100%
rename from db/migrations/19_step_00141_zip_download_restrictions.php
rename to db/migrations/1.19_step_00141_zip_download_restrictions.php
diff --git a/db/migrations/01_init_migrations.php b/db/migrations/1.1_init_migrations.php
similarity index 100%
rename from db/migrations/01_init_migrations.php
rename to db/migrations/1.1_init_migrations.php
diff --git a/db/migrations/200_step_00299_statusgroups.php b/db/migrations/1.200_step_00299_statusgroups.php
similarity index 100%
rename from db/migrations/200_step_00299_statusgroups.php
rename to db/migrations/1.200_step_00299_statusgroups.php
diff --git a/db/migrations/201_i18n_content.php b/db/migrations/1.201_i18n_content.php
similarity index 100%
rename from db/migrations/201_i18n_content.php
rename to db/migrations/1.201_i18n_content.php
diff --git a/db/migrations/202_remove_skype_status.php b/db/migrations/1.202_remove_skype_status.php
similarity index 100%
rename from db/migrations/202_remove_skype_status.php
rename to db/migrations/1.202_remove_skype_status.php
diff --git a/db/migrations/203_archive_help_texts.php b/db/migrations/1.203_archive_help_texts.php
similarity index 100%
rename from db/migrations/203_archive_help_texts.php
rename to db/migrations/1.203_archive_help_texts.php
diff --git a/db/migrations/204_dialog_from_notification.php b/db/migrations/1.204_dialog_from_notification.php
similarity index 100%
rename from db/migrations/204_dialog_from_notification.php
rename to db/migrations/1.204_dialog_from_notification.php
diff --git a/db/migrations/205_course_scm_helptext.php b/db/migrations/1.205_course_scm_helptext.php
similarity index 100%
rename from db/migrations/205_course_scm_helptext.php
rename to db/migrations/1.205_course_scm_helptext.php
diff --git a/db/migrations/206_alter_columns_weekoffset_to_int.php b/db/migrations/1.206_alter_columns_weekoffset_to_int.php
similarity index 100%
rename from db/migrations/206_alter_columns_weekoffset_to_int.php
rename to db/migrations/1.206_alter_columns_weekoffset_to_int.php
diff --git a/db/migrations/207_add_etask_tables.php b/db/migrations/1.207_add_etask_tables.php
similarity index 100%
rename from db/migrations/207_add_etask_tables.php
rename to db/migrations/1.207_add_etask_tables.php
diff --git a/db/migrations/208_migrate_questionnaire_questions.php b/db/migrations/1.208_migrate_questionnaire_questions.php
similarity index 100%
rename from db/migrations/208_migrate_questionnaire_questions.php
rename to db/migrations/1.208_migrate_questionnaire_questions.php
diff --git a/db/migrations/209_convert_cronjob_logs.php b/db/migrations/1.209_convert_cronjob_logs.php
similarity index 100%
rename from db/migrations/209_convert_cronjob_logs.php
rename to db/migrations/1.209_convert_cronjob_logs.php
diff --git a/db/migrations/20_calendar_events_class_default.php b/db/migrations/1.20_calendar_events_class_default.php
similarity index 100%
rename from db/migrations/20_calendar_events_class_default.php
rename to db/migrations/1.20_calendar_events_class_default.php
diff --git a/db/migrations/210_tic_7206.php b/db/migrations/1.210_tic_7206.php
similarity index 100%
rename from db/migrations/210_tic_7206.php
rename to db/migrations/1.210_tic_7206.php
diff --git a/db/migrations/211_tic7307_admission_rule_path.php b/db/migrations/1.211_tic7307_admission_rule_path.php
similarity index 100%
rename from db/migrations/211_tic7307_admission_rule_path.php
rename to db/migrations/1.211_tic7307_admission_rule_path.php
diff --git a/db/migrations/212_refactor_config_local.php b/db/migrations/1.212_refactor_config_local.php
similarity index 100%
rename from db/migrations/212_refactor_config_local.php
rename to db/migrations/1.212_refactor_config_local.php
diff --git a/db/migrations/213_log_action_statusgroups.php b/db/migrations/1.213_log_action_statusgroups.php
similarity index 100%
rename from db/migrations/213_log_action_statusgroups.php
rename to db/migrations/1.213_log_action_statusgroups.php
diff --git a/db/migrations/214_textmarkup_datafield.php b/db/migrations/1.214_textmarkup_datafield.php
similarity index 100%
rename from db/migrations/214_textmarkup_datafield.php
rename to db/migrations/1.214_textmarkup_datafield.php
diff --git a/db/migrations/215_default_data_field_values.php b/db/migrations/1.215_default_data_field_values.php
similarity index 100%
rename from db/migrations/215_default_data_field_values.php
rename to db/migrations/1.215_default_data_field_values.php
diff --git a/db/migrations/216_add_evaldate.php b/db/migrations/1.216_add_evaldate.php
similarity index 100%
rename from db/migrations/216_add_evaldate.php
rename to db/migrations/1.216_add_evaldate.php
diff --git a/db/migrations/217_course_number_format_config.php b/db/migrations/1.217_course_number_format_config.php
similarity index 100%
rename from db/migrations/217_course_number_format_config.php
rename to db/migrations/1.217_course_number_format_config.php
diff --git a/db/migrations/218_extend_phone_fax_size.php b/db/migrations/1.218_extend_phone_fax_size.php
similarity index 100%
rename from db/migrations/218_extend_phone_fax_size.php
rename to db/migrations/1.218_extend_phone_fax_size.php
diff --git a/db/migrations/219_loginbackgrounds.php b/db/migrations/1.219_loginbackgrounds.php
similarity index 100%
rename from db/migrations/219_loginbackgrounds.php
rename to db/migrations/1.219_loginbackgrounds.php
diff --git a/db/migrations/21_more_indexing.php b/db/migrations/1.21_more_indexing.php
similarity index 100%
rename from db/migrations/21_more_indexing.php
rename to db/migrations/1.21_more_indexing.php
diff --git a/db/migrations/220_step_00313_coursegroups.php b/db/migrations/1.220_step_00313_coursegroups.php
similarity index 100%
rename from db/migrations/220_step_00313_coursegroups.php
rename to db/migrations/1.220_step_00313_coursegroups.php
diff --git a/db/migrations/221_moadb.php b/db/migrations/1.221_moadb.php
similarity index 100%
rename from db/migrations/221_moadb.php
rename to db/migrations/1.221_moadb.php
diff --git a/db/migrations/222_utf8_conversion.php b/db/migrations/1.222_utf8_conversion.php
similarity index 100%
rename from db/migrations/222_utf8_conversion.php
rename to db/migrations/1.222_utf8_conversion.php
diff --git a/db/migrations/223_course_mailing_for_students.php b/db/migrations/1.223_course_mailing_for_students.php
similarity index 100%
rename from db/migrations/223_course_mailing_for_students.php
rename to db/migrations/1.223_course_mailing_for_students.php
diff --git a/db/migrations/224_db_cache_table.php b/db/migrations/1.224_db_cache_table.php
similarity index 100%
rename from db/migrations/224_db_cache_table.php
rename to db/migrations/1.224_db_cache_table.php
diff --git a/db/migrations/225_extend_course_completion.php b/db/migrations/1.225_extend_course_completion.php
similarity index 100%
rename from db/migrations/225_extend_course_completion.php
rename to db/migrations/1.225_extend_course_completion.php
diff --git a/db/migrations/226_config_values.php b/db/migrations/1.226_config_values.php
similarity index 100%
rename from db/migrations/226_config_values.php
rename to db/migrations/1.226_config_values.php
diff --git a/db/migrations/227_biest_8034_config_switch_for_child_insts.php b/db/migrations/1.227_biest_8034_config_switch_for_child_insts.php
similarity index 100%
rename from db/migrations/227_biest_8034_config_switch_for_child_insts.php
rename to db/migrations/1.227_biest_8034_config_switch_for_child_insts.php
diff --git a/db/migrations/228_show_adressees.php b/db/migrations/1.228_show_adressees.php
similarity index 100%
rename from db/migrations/228_show_adressees.php
rename to db/migrations/1.228_show_adressees.php
diff --git a/db/migrations/229_step_00278_global_search.php b/db/migrations/1.229_step_00278_global_search.php
similarity index 100%
rename from db/migrations/229_step_00278_global_search.php
rename to db/migrations/1.229_step_00278_global_search.php
diff --git a/db/migrations/22_lit_list_content_note_from_varchar_to_text.php b/db/migrations/1.22_lit_list_content_note_from_varchar_to_text.php
similarity index 100%
rename from db/migrations/22_lit_list_content_note_from_varchar_to_text.php
rename to db/migrations/1.22_lit_list_content_note_from_varchar_to_text.php
diff --git a/db/migrations/230_widgets.php b/db/migrations/1.230_widgets.php
similarity index 100%
rename from db/migrations/230_widgets.php
rename to db/migrations/1.230_widgets.php
diff --git a/db/migrations/231_add_files_search_index.php b/db/migrations/1.231_add_files_search_index.php
similarity index 100%
rename from db/migrations/231_add_files_search_index.php
rename to db/migrations/1.231_add_files_search_index.php
diff --git a/db/migrations/232_step_00315_mvv_i18n.php b/db/migrations/1.232_step_00315_mvv_i18n.php
similarity index 100%
rename from db/migrations/232_step_00315_mvv_i18n.php
rename to db/migrations/1.232_step_00315_mvv_i18n.php
diff --git a/db/migrations/233_step_00316_mvvkernintegration.php b/db/migrations/1.233_step_00316_mvvkernintegration.php
similarity index 100%
rename from db/migrations/233_step_00316_mvvkernintegration.php
rename to db/migrations/1.233_step_00316_mvvkernintegration.php
diff --git a/db/migrations/234_add_mkdate_to_statusgruppe_user.php b/db/migrations/1.234_add_mkdate_to_statusgruppe_user.php
similarity index 100%
rename from db/migrations/234_add_mkdate_to_statusgruppe_user.php
rename to db/migrations/1.234_add_mkdate_to_statusgruppe_user.php
diff --git a/db/migrations/235_tic8335_paper_related_topics.php b/db/migrations/1.235_tic8335_paper_related_topics.php
similarity index 100%
rename from db/migrations/235_tic8335_paper_related_topics.php
rename to db/migrations/1.235_tic8335_paper_related_topics.php
diff --git a/db/migrations/236_config_public_topics.php b/db/migrations/1.236_config_public_topics.php
similarity index 100%
rename from db/migrations/236_config_public_topics.php
rename to db/migrations/1.236_config_public_topics.php
diff --git a/db/migrations/237_config_mail_subject.php b/db/migrations/1.237_config_mail_subject.php
similarity index 100%
rename from db/migrations/237_config_mail_subject.php
rename to db/migrations/1.237_config_mail_subject.php
diff --git a/db/migrations/238_db_updates_for_42.php b/db/migrations/1.238_db_updates_for_42.php
similarity index 100%
rename from db/migrations/238_db_updates_for_42.php
rename to db/migrations/1.238_db_updates_for_42.php
diff --git a/db/migrations/239_step_00327.php b/db/migrations/1.239_step_00327.php
similarity index 100%
rename from db/migrations/239_step_00327.php
rename to db/migrations/1.239_step_00327.php
diff --git a/db/migrations/23_enlarge_object_contentmodules_module_id.php b/db/migrations/1.23_enlarge_object_contentmodules_module_id.php
similarity index 100%
rename from db/migrations/23_enlarge_object_contentmodules_module_id.php
rename to db/migrations/1.23_enlarge_object_contentmodules_module_id.php
diff --git a/db/migrations/240_tic8538.php b/db/migrations/1.240_tic8538.php
similarity index 100%
rename from db/migrations/240_tic8538.php
rename to db/migrations/1.240_tic8538.php
diff --git a/db/migrations/241_step_00320_config_search_navigation.php b/db/migrations/1.241_step_00320_config_search_navigation.php
similarity index 100%
rename from db/migrations/241_step_00320_config_search_navigation.php
rename to db/migrations/1.241_step_00320_config_search_navigation.php
diff --git a/db/migrations/242_tic_8546_search_show_admission_state.php b/db/migrations/1.242_tic_8546_search_show_admission_state.php
similarity index 100%
rename from db/migrations/242_tic_8546_search_show_admission_state.php
rename to db/migrations/1.242_tic_8546_search_show_admission_state.php
diff --git a/db/migrations/243_tic8773_sort_news_by_chdate.php b/db/migrations/1.243_tic8773_sort_news_by_chdate.php
similarity index 100%
rename from db/migrations/243_tic8773_sort_news_by_chdate.php
rename to db/migrations/1.243_tic8773_sort_news_by_chdate.php
diff --git a/db/migrations/244_config_default_seats.php b/db/migrations/1.244_config_default_seats.php
similarity index 100%
rename from db/migrations/244_config_default_seats.php
rename to db/migrations/1.244_config_default_seats.php
diff --git a/db/migrations/245_tic7804_wiki_permissions.php b/db/migrations/1.245_tic7804_wiki_permissions.php
similarity index 100%
rename from db/migrations/245_tic7804_wiki_permissions.php
rename to db/migrations/1.245_tic7804_wiki_permissions.php
diff --git a/db/migrations/246_consultations.php b/db/migrations/1.246_consultations.php
similarity index 100%
rename from db/migrations/246_consultations.php
rename to db/migrations/1.246_consultations.php
diff --git a/db/migrations/247_step_00330.php b/db/migrations/1.247_step_00330.php
similarity index 100%
rename from db/migrations/247_step_00330.php
rename to db/migrations/1.247_step_00330.php
diff --git a/db/migrations/248_step_00331_ilias_interface.php b/db/migrations/1.248_step_00331_ilias_interface.php
similarity index 100%
rename from db/migrations/248_step_00331_ilias_interface.php
rename to db/migrations/1.248_step_00331_ilias_interface.php
diff --git a/db/migrations/249_lti_consumer.php b/db/migrations/1.249_lti_consumer.php
similarity index 100%
rename from db/migrations/249_lti_consumer.php
rename to db/migrations/1.249_lti_consumer.php
diff --git a/db/migrations/24_another_index_for_ex_termine.php b/db/migrations/1.24_another_index_for_ex_termine.php
similarity index 100%
rename from db/migrations/24_another_index_for_ex_termine.php
rename to db/migrations/1.24_another_index_for_ex_termine.php
diff --git a/db/migrations/250_disable_archive_search.php b/db/migrations/1.250_disable_archive_search.php
similarity index 100%
rename from db/migrations/250_disable_archive_search.php
rename to db/migrations/1.250_disable_archive_search.php
diff --git a/db/migrations/251_log_event_autoincrement.php b/db/migrations/1.251_log_event_autoincrement.php
similarity index 100%
rename from db/migrations/251_log_event_autoincrement.php
rename to db/migrations/1.251_log_event_autoincrement.php
diff --git a/db/migrations/252_js_assets.php b/db/migrations/1.252_js_assets.php
similarity index 100%
rename from db/migrations/252_js_assets.php
rename to db/migrations/1.252_js_assets.php
diff --git a/db/migrations/253_adjust_token_table.php b/db/migrations/1.253_adjust_token_table.php
similarity index 100%
rename from db/migrations/253_adjust_token_table.php
rename to db/migrations/1.253_adjust_token_table.php
diff --git a/db/migrations/254_hash_opengraph_table.php b/db/migrations/1.254_hash_opengraph_table.php
similarity index 100%
rename from db/migrations/254_hash_opengraph_table.php
rename to db/migrations/1.254_hash_opengraph_table.php
diff --git a/db/migrations/255_tic9368_datafield_for_institution.php b/db/migrations/1.255_tic9368_datafield_for_institution.php
similarity index 100%
rename from db/migrations/255_tic9368_datafield_for_institution.php
rename to db/migrations/1.255_tic9368_datafield_for_institution.php
diff --git a/db/migrations/256_tic9543_update_help_content.php b/db/migrations/1.256_tic9543_update_help_content.php
similarity index 100%
rename from db/migrations/256_tic9543_update_help_content.php
rename to db/migrations/1.256_tic9543_update_help_content.php
diff --git a/db/migrations/257_tic9544_add_external_id_semester.php b/db/migrations/1.257_tic9544_add_external_id_semester.php
similarity index 100%
rename from db/migrations/257_tic9544_add_external_id_semester.php
rename to db/migrations/1.257_tic9544_add_external_id_semester.php
diff --git a/db/migrations/258_tic7443_create_lvgruppen_independently.php b/db/migrations/1.258_tic7443_create_lvgruppen_independently.php
similarity index 100%
rename from db/migrations/258_tic7443_create_lvgruppen_independently.php
rename to db/migrations/1.258_tic7443_create_lvgruppen_independently.php
diff --git a/db/migrations/1.259_migrations_reloaded.php b/db/migrations/1.259_migrations_reloaded.php
new file mode 100644
index 0000000000000000000000000000000000000000..1ffb583d4c7d8748c1b2e417ead2426daa74da05
--- /dev/null
+++ b/db/migrations/1.259_migrations_reloaded.php
@@ -0,0 +1,38 @@
+<?php
+
+class MigrationsReloaded extends Migration
+{
+    public function description()
+    {
+        return 'add branch column to schema_version';
+    }
+
+    public function up()
+    {
+        $db = DBManager::get();
+
+        $sql = "ALTER TABLE schema_version
+                CHANGE domain domain VARCHAR(255) COLLATE latin1_bin NOT NULL,
+                ADD branch VARCHAR(64) COLLATE latin1_bin NOT NULL DEFAULT '0' AFTER domain,
+                DROP PRIMARY KEY,
+                ADD PRIMARY KEY (domain, branch)";
+        $db->exec($sql);
+
+        $sql = "UPDATE schema_version SET branch = '1' WHERE domain = 'studip'";
+        $db->exec($sql);
+    }
+
+    public function down()
+    {
+        $db = DBManager::get();
+
+        $sql = "DELETE FROM schema_version WHERE branch != '0'";
+        $db->exec($sql);
+
+        $sql = 'ALTER TABLE schema_version
+                DROP PRIMARY KEY,
+                ADD PRIMARY KEY (domain),
+                DROP branch';
+        $db->exec($sql);
+    }
+}
diff --git a/db/migrations/25_step_00098_user_domains.php b/db/migrations/1.25_step_00098_user_domains.php
similarity index 100%
rename from db/migrations/25_step_00098_user_domains.php
rename to db/migrations/1.25_step_00098_user_domains.php
diff --git a/db/migrations/20190702_tfa.php b/db/migrations/1.260_tfa.php
similarity index 100%
rename from db/migrations/20190702_tfa.php
rename to db/migrations/1.260_tfa.php
diff --git a/db/migrations/20190705_gradebook.php b/db/migrations/1.261_gradebook.php
similarity index 100%
rename from db/migrations/20190705_gradebook.php
rename to db/migrations/1.261_gradebook.php
diff --git a/db/migrations/201908014_blubbermessenger.php b/db/migrations/1.262_blubbermessenger.php
similarity index 100%
rename from db/migrations/201908014_blubbermessenger.php
rename to db/migrations/1.262_blubbermessenger.php
diff --git a/db/migrations/201908015_blubbermessenger_keys.php b/db/migrations/1.263_blubbermessenger_keys.php
similarity index 100%
rename from db/migrations/201908015_blubbermessenger_keys.php
rename to db/migrations/1.263_blubbermessenger_keys.php
diff --git a/db/migrations/201908016_blubbermessenger_search.php b/db/migrations/1.264_blubbermessenger_search.php
similarity index 100%
rename from db/migrations/201908016_blubbermessenger_search.php
rename to db/migrations/1.264_blubbermessenger_search.php
diff --git a/db/migrations/201908017_blubbermessenger_flat.php b/db/migrations/1.265_blubbermessenger_flat.php
similarity index 100%
rename from db/migrations/201908017_blubbermessenger_flat.php
rename to db/migrations/1.265_blubbermessenger_flat.php
diff --git a/db/migrations/20190823_consultations_option_exclude_expired.php b/db/migrations/1.266_consultations_option_exclude_expired.php
similarity index 100%
rename from db/migrations/20190823_consultations_option_exclude_expired.php
rename to db/migrations/1.266_consultations_option_exclude_expired.php
diff --git a/db/migrations/20190903_jsonapi_dangerous_routes_config.php b/db/migrations/1.267_jsonapi_dangerous_routes_config.php
similarity index 100%
rename from db/migrations/20190903_jsonapi_dangerous_routes_config.php
rename to db/migrations/1.267_jsonapi_dangerous_routes_config.php
diff --git a/db/migrations/20190904_unrestricted_userdomains.php b/db/migrations/1.268_unrestricted_userdomains.php
similarity index 100%
rename from db/migrations/20190904_unrestricted_userdomains.php
rename to db/migrations/1.268_unrestricted_userdomains.php
diff --git a/db/migrations/20190917_fix_missing_consultation_events.php b/db/migrations/1.269_fix_missing_consultation_events.php
similarity index 100%
rename from db/migrations/20190917_fix_missing_consultation_events.php
rename to db/migrations/1.269_fix_missing_consultation_events.php
diff --git a/db/migrations/26_step_00146_lock_rules2.php b/db/migrations/1.26_step_00146_lock_rules2.php
similarity index 100%
rename from db/migrations/26_step_00146_lock_rules2.php
rename to db/migrations/1.26_step_00146_lock_rules2.php
diff --git a/db/migrations/20190919_step_00332_mvv_overlapping_courses.php b/db/migrations/1.270_step_00332_mvv_overlapping_courses.php
similarity index 100%
rename from db/migrations/20190919_step_00332_mvv_overlapping_courses.php
rename to db/migrations/1.270_step_00332_mvv_overlapping_courses.php
diff --git a/db/migrations/20191002_jsonapi_cors_origin_config.php b/db/migrations/1.271_jsonapi_cors_origin_config.php
similarity index 100%
rename from db/migrations/20191002_jsonapi_cors_origin_config.php
rename to db/migrations/1.271_jsonapi_cors_origin_config.php
diff --git a/db/migrations/20191014_step_00338_instituteplaning.php b/db/migrations/1.272_step_00338_instituteplaning.php
similarity index 100%
rename from db/migrations/20191014_step_00338_instituteplaning.php
rename to db/migrations/1.272_step_00338_instituteplaning.php
diff --git a/db/migrations/20191018_config_for_download_counter_display.php b/db/migrations/1.273_config_for_download_counter_display.php
similarity index 100%
rename from db/migrations/20191018_config_for_download_counter_display.php
rename to db/migrations/1.273_config_for_download_counter_display.php
diff --git a/db/migrations/20191105_add_enable_free_access_for_courses_only.php b/db/migrations/1.274_add_enable_free_access_for_courses_only.php
similarity index 100%
rename from db/migrations/20191105_add_enable_free_access_for_courses_only.php
rename to db/migrations/1.274_add_enable_free_access_for_courses_only.php
diff --git a/db/migrations/20191112_additional_mvv_tables.php b/db/migrations/1.275_additional_mvv_tables.php
similarity index 100%
rename from db/migrations/20191112_additional_mvv_tables.php
rename to db/migrations/1.275_additional_mvv_tables.php
diff --git a/db/migrations/20191115_tic8458_add_upload_description.php b/db/migrations/1.276_tic8458_add_upload_description.php
similarity index 100%
rename from db/migrations/20191115_tic8458_add_upload_description.php
rename to db/migrations/1.276_tic8458_add_upload_description.php
diff --git a/db/migrations/20191120_room_management_migration.php b/db/migrations/1.277_room_management_migration.php
similarity index 100%
rename from db/migrations/20191120_room_management_migration.php
rename to db/migrations/1.277_room_management_migration.php
diff --git a/db/migrations/20191122_resize_auth_user_md5_email_field.php b/db/migrations/1.278_resize_auth_user_md5_email_field.php
similarity index 100%
rename from db/migrations/20191122_resize_auth_user_md5_email_field.php
rename to db/migrations/1.278_resize_auth_user_md5_email_field.php
diff --git a/db/migrations/20191210_config_wiki_comments_enable.php b/db/migrations/1.279_config_wiki_comments_enable.php
similarity index 100%
rename from db/migrations/20191210_config_wiki_comments_enable.php
rename to db/migrations/1.279_config_wiki_comments_enable.php
diff --git a/db/migrations/27_step_147_mail_activationlink.php b/db/migrations/1.27_step_147_mail_activationlink.php
similarity index 100%
rename from db/migrations/27_step_147_mail_activationlink.php
rename to db/migrations/1.27_step_147_mail_activationlink.php
diff --git a/db/migrations/20191211_step_00338_institutecolors.php b/db/migrations/1.280_step_00338_institutecolors.php
similarity index 100%
rename from db/migrations/20191211_step_00338_institutecolors.php
rename to db/migrations/1.280_step_00338_institutecolors.php
diff --git a/db/migrations/20192208_step_00333_feedback.php b/db/migrations/1.281_step_00333_feedback.php
similarity index 100%
rename from db/migrations/20192208_step_00333_feedback.php
rename to db/migrations/1.281_step_00333_feedback.php
diff --git a/db/migrations/20200108_config_for_stgteilversion_userfilter.php b/db/migrations/1.282_config_for_stgteilversion_userfilter.php
similarity index 100%
rename from db/migrations/20200108_config_for_stgteilversion_userfilter.php
rename to db/migrations/1.282_config_for_stgteilversion_userfilter.php
diff --git a/db/migrations/202001291_add_filetypes.php b/db/migrations/1.283_add_filetypes.php
similarity index 100%
rename from db/migrations/202001291_add_filetypes.php
rename to db/migrations/1.283_add_filetypes.php
diff --git a/db/migrations/20200306_change_schedule_color_with_category_index.php b/db/migrations/1.284_change_schedule_color_with_category_index.php
similarity index 100%
rename from db/migrations/20200306_change_schedule_color_with_category_index.php
rename to db/migrations/1.284_change_schedule_color_with_category_index.php
diff --git a/db/migrations/20200307_fixes_on_schedule_coloring.php b/db/migrations/1.285_fixes_on_schedule_coloring.php
similarity index 100%
rename from db/migrations/20200307_fixes_on_schedule_coloring.php
rename to db/migrations/1.285_fixes_on_schedule_coloring.php
diff --git a/db/migrations/20200414_remove_new_widget_system.php b/db/migrations/1.286_remove_new_widget_system.php
similarity index 100%
rename from db/migrations/20200414_remove_new_widget_system.php
rename to db/migrations/1.286_remove_new_widget_system.php
diff --git a/db/migrations/20200423_replace_library_system.php b/db/migrations/1.287_replace_library_system.php
similarity index 100%
rename from db/migrations/20200423_replace_library_system.php
rename to db/migrations/1.287_replace_library_system.php
diff --git a/db/migrations/20200514_tic9898_add_course_wizard_step.php b/db/migrations/1.288_tic9898_add_course_wizard_step.php
similarity index 100%
rename from db/migrations/20200514_tic9898_add_course_wizard_step.php
rename to db/migrations/1.288_tic9898_add_course_wizard_step.php
diff --git a/db/migrations/20200515_tic10318_http_proxy.php b/db/migrations/1.289_tic10318_http_proxy.php
similarity index 100%
rename from db/migrations/20200515_tic10318_http_proxy.php
rename to db/migrations/1.289_tic10318_http_proxy.php
diff --git a/db/migrations/28_delete_wiki_links.php b/db/migrations/1.28_delete_wiki_links.php
similarity index 100%
rename from db/migrations/28_delete_wiki_links.php
rename to db/migrations/1.28_delete_wiki_links.php
diff --git a/db/migrations/20200522_termsconfig.php b/db/migrations/1.290_termsconfig.php
similarity index 100%
rename from db/migrations/20200522_termsconfig.php
rename to db/migrations/1.290_termsconfig.php
diff --git a/db/migrations/20200709_step345_wiki_ancestor.php b/db/migrations/1.291_step345_wiki_ancestor.php
similarity index 100%
rename from db/migrations/20200709_step345_wiki_ancestor.php
rename to db/migrations/1.291_step345_wiki_ancestor.php
diff --git a/db/migrations/20200713_add_admin_courses_sidebar_active_elements.php b/db/migrations/1.292_add_admin_courses_sidebar_active_elements.php
similarity index 100%
rename from db/migrations/20200713_add_admin_courses_sidebar_active_elements.php
rename to db/migrations/1.292_add_admin_courses_sidebar_active_elements.php
diff --git a/db/migrations/20200811_adjust_i18n_tables.php b/db/migrations/1.293_adjust_i18n_tables.php
similarity index 100%
rename from db/migrations/20200811_adjust_i18n_tables.php
rename to db/migrations/1.293_adjust_i18n_tables.php
diff --git a/db/migrations/20200909_mvv_bugs.php b/db/migrations/1.294_mvv_bugs.php
similarity index 100%
rename from db/migrations/20200909_mvv_bugs.php
rename to db/migrations/1.294_mvv_bugs.php
diff --git a/db/migrations/20200910_remove_invalid_modul_user_assignments.php b/db/migrations/1.295_remove_invalid_modul_user_assignments.php
similarity index 100%
rename from db/migrations/20200910_remove_invalid_modul_user_assignments.php
rename to db/migrations/1.295_remove_invalid_modul_user_assignments.php
diff --git a/db/migrations/20201002_biest_10803_fix.php b/db/migrations/1.296_biest_10803_fix.php
similarity index 100%
rename from db/migrations/20201002_biest_10803_fix.php
rename to db/migrations/1.296_biest_10803_fix.php
diff --git a/db/migrations/20201005_database_changes_tic_9218.php b/db/migrations/1.297_database_changes_tic_9218.php
similarity index 100%
rename from db/migrations/20201005_database_changes_tic_9218.php
rename to db/migrations/1.297_database_changes_tic_9218.php
diff --git a/db/migrations/20201007_hide_studygroups_from_profile.php b/db/migrations/1.298_hide_studygroups_from_profile.php
similarity index 100%
rename from db/migrations/20201007_hide_studygroups_from_profile.php
rename to db/migrations/1.298_hide_studygroups_from_profile.php
diff --git a/db/migrations/20201023_tiled_courses.php b/db/migrations/1.299_tiled_courses.php
similarity index 100%
rename from db/migrations/20201023_tiled_courses.php
rename to db/migrations/1.299_tiled_courses.php
diff --git a/db/migrations/29_step_00138_studienbereichszuordnung.php b/db/migrations/1.29_step_00138_studienbereichszuordnung.php
similarity index 100%
rename from db/migrations/29_step_00138_studienbereichszuordnung.php
rename to db/migrations/1.29_step_00138_studienbereichszuordnung.php
diff --git a/db/migrations/02_step_102_datenfeldtypen.php b/db/migrations/1.2_step_102_datenfeldtypen.php
similarity index 100%
rename from db/migrations/02_step_102_datenfeldtypen.php
rename to db/migrations/1.2_step_102_datenfeldtypen.php
diff --git a/db/migrations/20201024_tiled_courses_2.php b/db/migrations/1.300_tiled_courses_2.php
similarity index 100%
rename from db/migrations/20201024_tiled_courses_2.php
rename to db/migrations/1.300_tiled_courses_2.php
diff --git a/db/migrations/20201025_tiled_courses_3.php b/db/migrations/1.301_tiled_courses_3.php
similarity index 100%
rename from db/migrations/20201025_tiled_courses_3.php
rename to db/migrations/1.301_tiled_courses_3.php
diff --git a/db/migrations/202011031_add_missing_indices_resources.php b/db/migrations/1.302_add_missing_indices_resources.php
similarity index 77%
rename from db/migrations/202011031_add_missing_indices_resources.php
rename to db/migrations/1.302_add_missing_indices_resources.php
index 063c60975e75e71cc46b5c8a878fc0384fca8012..a3da2c2bb26995e38406147c37103effd6ae19a5 100644
--- a/db/migrations/202011031_add_missing_indices_resources.php
+++ b/db/migrations/1.302_add_missing_indices_resources.php
@@ -12,6 +12,14 @@ class AddMissingIndicesResources extends Migration
 
     public function up()
     {
+        // avoid running this migration twice
+        $query = "SHOW INDEX FROM resource_temporary_permissions WHERE Key_name = 'user_id'";
+        $result = DBManager::get()->query($query);
+
+        if ($result && $result->rowCount() > 0) {
+            return;
+        }
+
         $query = "ALTER TABLE `resource_temporary_permissions` ADD INDEX (`user_id`)";
         DBManager::get()->exec($query);
 
diff --git a/db/migrations/20201103_add_missing_indices.php b/db/migrations/1.303_add_missing_indices.php
similarity index 79%
rename from db/migrations/20201103_add_missing_indices.php
rename to db/migrations/1.303_add_missing_indices.php
index 70124c919fac7745f09f5108f71080586557c784..b1b652bc53de0df53052f10e199202dd62dcbd31 100644
--- a/db/migrations/20201103_add_missing_indices.php
+++ b/db/migrations/1.303_add_missing_indices.php
@@ -13,6 +13,14 @@ class AddMissingIndices extends Migration
 
     public function up()
     {
+        // avoid running this migration twice
+        $query = "SHOW INDEX FROM activities WHERE Key_name = 'object_id'";
+        $result = DBManager::get()->query($query);
+
+        if ($result && $result->rowCount() > 0) {
+            return;
+        }
+
         $query = "ALTER TABLE `activities`
                   ADD INDEX `object_id` (`object_id`(32))";
         DBManager::get()->exec($query);
diff --git a/db/migrations/20201108_config_i18n.php b/db/migrations/1.304_config_i18n.php
similarity index 100%
rename from db/migrations/20201108_config_i18n.php
rename to db/migrations/1.304_config_i18n.php
diff --git a/db/migrations/20201110_course_members_hide.php b/db/migrations/1.305_course_members_hide.php
similarity index 100%
rename from db/migrations/20201110_course_members_hide.php
rename to db/migrations/1.305_course_members_hide.php
diff --git a/db/migrations/20201113_tic_9101.php b/db/migrations/1.306_tic_9101.php
similarity index 100%
rename from db/migrations/20201113_tic_9101.php
rename to db/migrations/1.306_tic_9101.php
diff --git a/db/migrations/20201115_introduce_range_config.php b/db/migrations/1.307_introduce_range_config.php
similarity index 100%
rename from db/migrations/20201115_introduce_range_config.php
rename to db/migrations/1.307_introduce_range_config.php
diff --git a/db/migrations/20201116_consultation_extension.php b/db/migrations/1.308_consultation_extension.php
similarity index 100%
rename from db/migrations/20201116_consultation_extension.php
rename to db/migrations/1.308_consultation_extension.php
diff --git a/db/migrations/20201203_course_groups_hide.php b/db/migrations/1.309_course_groups_hide.php
similarity index 100%
rename from db/migrations/20201203_course_groups_hide.php
rename to db/migrations/1.309_course_groups_hide.php
diff --git a/db/migrations/30_auth_user_md5_perms.php b/db/migrations/1.30_auth_user_md5_perms.php
similarity index 100%
rename from db/migrations/30_auth_user_md5_perms.php
rename to db/migrations/1.30_auth_user_md5_perms.php
diff --git a/db/migrations/20201211_add_seminare_semester_table.php b/db/migrations/1.310_add_seminare_semester_table.php
similarity index 100%
rename from db/migrations/20201211_add_seminare_semester_table.php
rename to db/migrations/1.310_add_seminare_semester_table.php
diff --git a/db/migrations/20201212_add_seminare_semester_course_index.php b/db/migrations/1.311_add_seminare_semester_course_index.php
similarity index 100%
rename from db/migrations/20201212_add_seminare_semester_course_index.php
rename to db/migrations/1.311_add_seminare_semester_course_index.php
diff --git a/db/migrations/20210104_tic_11041.php b/db/migrations/1.312_tic_11041.php
similarity index 100%
rename from db/migrations/20210104_tic_11041.php
rename to db/migrations/1.312_tic_11041.php
diff --git a/db/migrations/20210108_tic11044_admin_course_notices.php b/db/migrations/1.313_tic11044_admin_course_notices.php
similarity index 100%
rename from db/migrations/20210108_tic11044_admin_course_notices.php
rename to db/migrations/1.313_tic11044_admin_course_notices.php
diff --git a/db/migrations/20210201_step_00349.php b/db/migrations/1.314_step_00349.php
similarity index 100%
rename from db/migrations/20210201_step_00349.php
rename to db/migrations/1.314_step_00349.php
diff --git a/db/migrations/20210204_tic_8183_terms_admission.php b/db/migrations/1.315_tic_8183_terms_admission.php
similarity index 100%
rename from db/migrations/20210204_tic_8183_terms_admission.php
rename to db/migrations/1.315_tic_8183_terms_admission.php
diff --git a/db/migrations/20210212_admin_related_inst.php b/db/migrations/1.316_admin_related_inst.php
similarity index 100%
rename from db/migrations/20210212_admin_related_inst.php
rename to db/migrations/1.316_admin_related_inst.php
diff --git a/db/migrations/202102161_oercampus_integration.php b/db/migrations/1.317_oercampus_integration.php
similarity index 100%
rename from db/migrations/202102161_oercampus_integration.php
rename to db/migrations/1.317_oercampus_integration.php
diff --git a/db/migrations/20210226_step_00353_cache.php b/db/migrations/1.318_step_00353_cache.php
similarity index 100%
rename from db/migrations/20210226_step_00353_cache.php
rename to db/migrations/1.318_step_00353_cache.php
diff --git a/db/migrations/20210317_change_blubber_thread_following.php b/db/migrations/1.319_change_blubber_thread_following.php
similarity index 92%
rename from db/migrations/20210317_change_blubber_thread_following.php
rename to db/migrations/1.319_change_blubber_thread_following.php
index 96098cdeb23868bc218f517d6c90414f993dd3ca..dadd953ec43d765b99808651a12fff9b458ed65d 100644
--- a/db/migrations/20210317_change_blubber_thread_following.php
+++ b/db/migrations/1.319_change_blubber_thread_following.php
@@ -8,6 +8,14 @@ class ChangeBlubberThreadFollowing extends Migration
 
     public function up()
     {
+        // avoid running this migration twice
+        $query = "SHOW TABLES LIKE 'blubber_threads_followstates'";
+        $result = DBManager::get()->query($query);
+
+        if ($result && $result->rowCount() > 0) {
+            return;
+        }
+
         // Alter table to reflect new state
         $query = "RENAME TABLE `blubber_threads_unfollow` TO `blubber_threads_followstates`";
         DBManager::get()->exec($query);
diff --git a/db/migrations/31_high_priority_messages.php b/db/migrations/1.31_high_priority_messages.php
similarity index 100%
rename from db/migrations/31_high_priority_messages.php
rename to db/migrations/1.31_high_priority_messages.php
diff --git a/db/migrations/20210322_migration_history_reworked.php b/db/migrations/1.320_migration_history_reworked.php
similarity index 73%
rename from db/migrations/20210322_migration_history_reworked.php
rename to db/migrations/1.320_migration_history_reworked.php
index 4863e7276fc58ccb1447d886b92c95148099ebbd..4809b8de608438814f1dbaffb051e81639478817 100644
--- a/db/migrations/20210322_migration_history_reworked.php
+++ b/db/migrations/1.320_migration_history_reworked.php
@@ -3,17 +3,11 @@ class MigrationHistoryReworked extends Migration
 {
     public function description()
     {
-        return 'Reverts migration 20201114 and adds appropriate log actions';
+        return 'Add log actions for migrations';
     }
 
     public function up()
     {
-        // Drop columns
-        $query = "ALTER TABLE `schema_versions`
-                  DROP COLUMN `user_id`,
-                  DROP COLUMN `mkdate`";
-        DBManager::get()->exec($query);
-
         // Add log actions
         $query = "INSERT IGNORE INTO log_actions (
                     `action_id`, `name`, `description`, `info_template`, `active`, `expires`, `mkdate`, `chdate`
@@ -46,11 +40,5 @@ class MigrationHistoryReworked extends Migration
         $statement = DBManager::get()->prepare($query);
         $statement->execute([':name' => 'MIGRATE_UP']);
         $statement->execute([':name' => 'MIGRATE_DOWN']);
-
-        // Add columns
-        $query = "ALTER TABLE `schema_versions`
-                  ADD COLUMN `user_id` CHAR(32) CHARACTER SET latin1 COLLATE latin1_bin NULL DEFAULT NULL,
-                  ADD COLUMN `mkdate` INT(11) UNSIGNED NULL DEFAULT NULL";
-        DBManager::get()->exec($query);
     }
 }
diff --git a/db/migrations/20210406_add_terms_accepted_config.php b/db/migrations/1.321_add_terms_accepted_config.php
similarity index 100%
rename from db/migrations/20210406_add_terms_accepted_config.php
rename to db/migrations/1.321_add_terms_accepted_config.php
diff --git a/db/migrations/20210422_add_missing_evaluation_configuration.php b/db/migrations/1.322_add_missing_evaluation_configuration.php
similarity index 100%
rename from db/migrations/20210422_add_missing_evaluation_configuration.php
rename to db/migrations/1.322_add_missing_evaluation_configuration.php
diff --git a/db/migrations/20210425_biest_11462_change_column_types.php b/db/migrations/1.323_biest_11462_change_column_types.php
similarity index 100%
rename from db/migrations/20210425_biest_11462_change_column_types.php
rename to db/migrations/1.323_biest_11462_change_column_types.php
diff --git a/db/migrations/20210503_drop_citation_style.php b/db/migrations/1.324_drop_citation_style.php
similarity index 100%
rename from db/migrations/20210503_drop_citation_style.php
rename to db/migrations/1.324_drop_citation_style.php
diff --git a/db/migrations/20210505_tic11044_admin_course_notices_2.php b/db/migrations/1.325_tic11044_admin_course_notices_2.php
similarity index 100%
rename from db/migrations/20210505_tic11044_admin_course_notices_2.php
rename to db/migrations/1.325_tic11044_admin_course_notices_2.php
diff --git a/db/migrations/20210511_courseware_integration.php b/db/migrations/1.326_courseware_integration.php
similarity index 100%
rename from db/migrations/20210511_courseware_integration.php
rename to db/migrations/1.326_courseware_integration.php
diff --git a/db/migrations/202106231_add_content_widget.php b/db/migrations/1.327_add_content_widget.php
similarity index 100%
rename from db/migrations/202106231_add_content_widget.php
rename to db/migrations/1.327_add_content_widget.php
diff --git a/db/migrations/32_restricted_user_management.php b/db/migrations/1.32_restricted_user_management.php
similarity index 100%
rename from db/migrations/32_restricted_user_management.php
rename to db/migrations/1.32_restricted_user_management.php
diff --git a/db/migrations/33_lock_rule_admin_perm.php b/db/migrations/1.33_lock_rule_admin_perm.php
similarity index 100%
rename from db/migrations/33_lock_rule_admin_perm.php
rename to db/migrations/1.33_lock_rule_admin_perm.php
diff --git a/db/migrations/34_add_allow_selfassign_institute.php b/db/migrations/1.34_add_allow_selfassign_institute.php
similarity index 100%
rename from db/migrations/34_add_allow_selfassign_institute.php
rename to db/migrations/1.34_add_allow_selfassign_institute.php
diff --git a/db/migrations/35_add_additional_log_actions.php b/db/migrations/1.35_add_additional_log_actions.php
similarity index 100%
rename from db/migrations/35_add_additional_log_actions.php
rename to db/migrations/1.35_add_additional_log_actions.php
diff --git a/db/migrations/36_step_00156_editierbares_impressum.php b/db/migrations/1.36_step_00156_editierbares_impressum.php
similarity index 100%
rename from db/migrations/36_step_00156_editierbares_impressum.php
rename to db/migrations/1.36_step_00156_editierbares_impressum.php
diff --git a/db/migrations/37_log_actions_expires.php b/db/migrations/1.37_log_actions_expires.php
similarity index 100%
rename from db/migrations/37_log_actions_expires.php
rename to db/migrations/1.37_log_actions_expires.php
diff --git a/db/migrations/38_allow_admin_useraccess.php b/db/migrations/1.38_allow_admin_useraccess.php
similarity index 100%
rename from db/migrations/38_allow_admin_useraccess.php
rename to db/migrations/1.38_allow_admin_useraccess.php
diff --git a/db/migrations/39_step_00153_studienmodulmanagement.php b/db/migrations/1.39_step_00153_studienmodulmanagement.php
similarity index 100%
rename from db/migrations/39_step_00153_studienmodulmanagement.php
rename to db/migrations/1.39_step_00153_studienmodulmanagement.php
diff --git a/db/migrations/03_step_87_extern_configurations.php b/db/migrations/1.3_step_87_extern_configurations.php
similarity index 79%
rename from db/migrations/03_step_87_extern_configurations.php
rename to db/migrations/1.3_step_87_extern_configurations.php
index e4cf33bf553c55ca99f115d3a699bfa42a38d628..5b7b1f9d5b4d5155db2c4074601cbf4ffd7df616 100644
--- a/db/migrations/03_step_87_extern_configurations.php
+++ b/db/migrations/1.3_step_87_extern_configurations.php
@@ -1,18 +1,4 @@
 <?php
-$requirements = [
-    "lib/functions.php",
-    $GLOBALS["RELATIVE_PATH_EXTERN"]."/extern_config.inc.php",
-    $GLOBALS["RELATIVE_PATH_EXTERN"]."/lib/ExternConfig.class.php",
-    $GLOBALS["RELATIVE_PATH_EXTERN"]."/lib/ExternConfigIni.class.php",
-    $GLOBALS["RELATIVE_PATH_EXTERN"]."/lib/ExternConfigDb.class.php",
-];
-
-foreach ($requirements as $file) {
-    if (!file_exists($file)) {
-        throw new Exception('Migration is not compatible with your Stud.IP version');
-    }
-    require_once $file;
-}
 
 class Step87ExternConfigurations extends Migration
 {
diff --git a/db/migrations/40_add_index_to_log_events.php b/db/migrations/1.40_add_index_to_log_events.php
similarity index 100%
rename from db/migrations/40_add_index_to_log_events.php
rename to db/migrations/1.40_add_index_to_log_events.php
diff --git a/db/migrations/41_step_00157_rolemanagment.php b/db/migrations/1.41_step_00157_rolemanagment.php
similarity index 100%
rename from db/migrations/41_step_00157_rolemanagment.php
rename to db/migrations/1.41_step_00157_rolemanagment.php
diff --git a/db/migrations/42_change_action_id_inst_create.php b/db/migrations/1.42_change_action_id_inst_create.php
similarity index 100%
rename from db/migrations/42_change_action_id_inst_create.php
rename to db/migrations/1.42_change_action_id_inst_create.php
diff --git a/db/migrations/43_step_00159_datafieldentry.php b/db/migrations/1.43_step_00159_datafieldentry.php
similarity index 100%
rename from db/migrations/43_step_00159_datafieldentry.php
rename to db/migrations/1.43_step_00159_datafieldentry.php
diff --git a/db/migrations/44_add_switch_to_preselect_semester.php b/db/migrations/1.44_add_switch_to_preselect_semester.php
similarity index 100%
rename from db/migrations/44_add_switch_to_preselect_semester.php
rename to db/migrations/1.44_add_switch_to_preselect_semester.php
diff --git a/db/migrations/45_refine_logevents.php b/db/migrations/1.45_refine_logevents.php
similarity index 100%
rename from db/migrations/45_refine_logevents.php
rename to db/migrations/1.45_refine_logevents.php
diff --git a/db/migrations/46_step00172_remove_ilias_connect.php b/db/migrations/1.46_step00172_remove_ilias_connect.php
similarity index 100%
rename from db/migrations/46_step00172_remove_ilias_connect.php
rename to db/migrations/1.46_step00172_remove_ilias_connect.php
diff --git a/db/migrations/47_add_option_resources_hide_past_single_dates.php b/db/migrations/1.47_add_option_resources_hide_past_single_dates.php
similarity index 100%
rename from db/migrations/47_add_option_resources_hide_past_single_dates.php
rename to db/migrations/1.47_add_option_resources_hide_past_single_dates.php
diff --git a/db/migrations/48_step_00174_plugin_interfaces.php b/db/migrations/1.48_step_00174_plugin_interfaces.php
similarity index 100%
rename from db/migrations/48_step_00174_plugin_interfaces.php
rename to db/migrations/1.48_step_00174_plugin_interfaces.php
diff --git a/db/migrations/49_step_00150_studygroups.php b/db/migrations/1.49_step_00150_studygroups.php
similarity index 100%
rename from db/migrations/49_step_00150_studygroups.php
rename to db/migrations/1.49_step_00150_studygroups.php
diff --git a/db/migrations/04_step_116_participant_view.php b/db/migrations/1.4_step_116_participant_view.php
similarity index 100%
rename from db/migrations/04_step_116_participant_view.php
rename to db/migrations/1.4_step_116_participant_view.php
diff --git a/db/migrations/50_add_option_step_152.php b/db/migrations/1.50_add_option_step_152.php
similarity index 100%
rename from db/migrations/50_add_option_step_152.php
rename to db/migrations/1.50_add_option_step_152.php
diff --git a/db/migrations/51_unhide_dozents.php b/db/migrations/1.51_unhide_dozents.php
similarity index 100%
rename from db/migrations/51_unhide_dozents.php
rename to db/migrations/1.51_unhide_dozents.php
diff --git a/db/migrations/52_additional_semtree_log_actions.php b/db/migrations/1.52_additional_semtree_log_actions.php
similarity index 100%
rename from db/migrations/52_additional_semtree_log_actions.php
rename to db/migrations/1.52_additional_semtree_log_actions.php
diff --git a/db/migrations/53_file_and_folder_priority.php b/db/migrations/1.53_file_and_folder_priority.php
similarity index 100%
rename from db/migrations/53_file_and_folder_priority.php
rename to db/migrations/1.53_file_and_folder_priority.php
diff --git a/db/migrations/54_step_00161_plugin_admin.php b/db/migrations/1.54_step_00161_plugin_admin.php
similarity index 100%
rename from db/migrations/54_step_00161_plugin_admin.php
rename to db/migrations/1.54_step_00161_plugin_admin.php
diff --git a/db/migrations/55_add_missing_log_actions.php b/db/migrations/1.55_add_missing_log_actions.php
similarity index 100%
rename from db/migrations/55_add_missing_log_actions.php
rename to db/migrations/1.55_add_missing_log_actions.php
diff --git a/db/migrations/56_step_00176_wap.php b/db/migrations/1.56_step_00176_wap.php
similarity index 100%
rename from db/migrations/56_step_00176_wap.php
rename to db/migrations/1.56_step_00176_wap.php
diff --git a/db/migrations/57_step_00158_privacy.php b/db/migrations/1.57_step_00158_privacy.php
similarity index 100%
rename from db/migrations/57_step_00158_privacy.php
rename to db/migrations/1.57_step_00158_privacy.php
diff --git a/db/migrations/58_add_config_allow_fakadmin.php b/db/migrations/1.58_add_config_allow_fakadmin.php
similarity index 100%
rename from db/migrations/58_add_config_allow_fakadmin.php
rename to db/migrations/1.58_add_config_allow_fakadmin.php
diff --git a/db/migrations/59_step_00194_studycourse.php b/db/migrations/1.59_step_00194_studycourse.php
similarity index 100%
rename from db/migrations/59_step_00194_studycourse.php
rename to db/migrations/1.59_step_00194_studycourse.php
diff --git a/db/migrations/05_step_25_raumzeit_migrations.php b/db/migrations/1.5_step_25_raumzeit_migrations.php
similarity index 100%
rename from db/migrations/05_step_25_raumzeit_migrations.php
rename to db/migrations/1.5_step_25_raumzeit_migrations.php
diff --git a/db/migrations/60_step_00191_modulesenable.php b/db/migrations/1.60_step_00191_modulesenable.php
similarity index 100%
rename from db/migrations/60_step_00191_modulesenable.php
rename to db/migrations/1.60_step_00191_modulesenable.php
diff --git a/db/migrations/61_remove_studygroupdozent.php b/db/migrations/1.61_remove_studygroupdozent.php
similarity index 100%
rename from db/migrations/61_remove_studygroupdozent.php
rename to db/migrations/1.61_remove_studygroupdozent.php
diff --git a/db/migrations/62_gender_iso_5218.php b/db/migrations/1.62_gender_iso_5218.php
similarity index 100%
rename from db/migrations/62_gender_iso_5218.php
rename to db/migrations/1.62_gender_iso_5218.php
diff --git a/db/migrations/63_tic1207_config.php b/db/migrations/1.63_tic1207_config.php
similarity index 100%
rename from db/migrations/63_tic1207_config.php
rename to db/migrations/1.63_tic1207_config.php
diff --git a/db/migrations/64_step_00199_forced_course_grouping.php b/db/migrations/1.64_step_00199_forced_course_grouping.php
similarity index 100%
rename from db/migrations/64_step_00199_forced_course_grouping.php
rename to db/migrations/1.64_step_00199_forced_course_grouping.php
diff --git a/db/migrations/65_step_00198_deputies.php b/db/migrations/1.65_step_00198_deputies.php
similarity index 100%
rename from db/migrations/65_step_00198_deputies.php
rename to db/migrations/1.65_step_00198_deputies.php
diff --git a/db/migrations/66_config_filesystem_multicopy_enable.php b/db/migrations/1.66_config_filesystem_multicopy_enable.php
similarity index 100%
rename from db/migrations/66_config_filesystem_multicopy_enable.php
rename to db/migrations/1.66_config_filesystem_multicopy_enable.php
diff --git a/db/migrations/67_step_00204_no_document_deletion.php b/db/migrations/1.67_step_00204_no_document_deletion.php
similarity index 100%
rename from db/migrations/67_step_00204_no_document_deletion.php
rename to db/migrations/1.67_step_00204_no_document_deletion.php
diff --git a/db/migrations/68_add_schedule_table.php b/db/migrations/1.68_add_schedule_table.php
similarity index 100%
rename from db/migrations/68_add_schedule_table.php
rename to db/migrations/1.68_add_schedule_table.php
diff --git a/db/migrations/69_step_00202_enhanced_seminar_cycle.php b/db/migrations/1.69_step_00202_enhanced_seminar_cycle.php
similarity index 100%
rename from db/migrations/69_step_00202_enhanced_seminar_cycle.php
rename to db/migrations/1.69_step_00202_enhanced_seminar_cycle.php
diff --git a/db/migrations/06_step_25_raumzeit_db_conversion.php b/db/migrations/1.6_step_25_raumzeit_db_conversion.php
similarity index 100%
rename from db/migrations/06_step_25_raumzeit_db_conversion.php
rename to db/migrations/1.6_step_25_raumzeit_db_conversion.php
diff --git a/db/migrations/70_step_00184_html5_video.php b/db/migrations/1.70_step_00184_html5_video.php
similarity index 100%
rename from db/migrations/70_step_00184_html5_video.php
rename to db/migrations/1.70_step_00184_html5_video.php
diff --git a/db/migrations/71_step_00192_page_layout.php b/db/migrations/1.71_step_00192_page_layout.php
similarity index 100%
rename from db/migrations/71_step_00192_page_layout.php
rename to db/migrations/1.71_step_00192_page_layout.php
diff --git a/db/migrations/72_config_ajax_autocomplete_disabled.php b/db/migrations/1.72_config_ajax_autocomplete_disabled.php
similarity index 100%
rename from db/migrations/72_config_ajax_autocomplete_disabled.php
rename to db/migrations/1.72_config_ajax_autocomplete_disabled.php
diff --git a/db/migrations/73_step_00193_html_email.php b/db/migrations/1.73_step_00193_html_email.php
similarity index 100%
rename from db/migrations/73_step_00193_html_email.php
rename to db/migrations/1.73_step_00193_html_email.php
diff --git a/db/migrations/74_tic1422_pagination.php b/db/migrations/1.74_tic1422_pagination.php
similarity index 100%
rename from db/migrations/74_tic1422_pagination.php
rename to db/migrations/1.74_tic1422_pagination.php
diff --git a/db/migrations/75_pdf_logo_configuration.php b/db/migrations/1.75_pdf_logo_configuration.php
similarity index 100%
rename from db/migrations/75_pdf_logo_configuration.php
rename to db/migrations/1.75_pdf_logo_configuration.php
diff --git a/db/migrations/76_termin_related_persons.php b/db/migrations/1.76_termin_related_persons.php
similarity index 100%
rename from db/migrations/76_termin_related_persons.php
rename to db/migrations/1.76_termin_related_persons.php
diff --git a/db/migrations/77_step_00223_lockrules.php b/db/migrations/1.77_step_00223_lockrules.php
similarity index 100%
rename from db/migrations/77_step_00223_lockrules.php
rename to db/migrations/1.77_step_00223_lockrules.php
diff --git a/db/migrations/78_step00219_webservice_access.php b/db/migrations/1.78_step00219_webservice_access.php
similarity index 100%
rename from db/migrations/78_step00219_webservice_access.php
rename to db/migrations/1.78_step00219_webservice_access.php
diff --git a/db/migrations/79_step_216_automatisiertes_eintragen.php b/db/migrations/1.79_step_216_automatisiertes_eintragen.php
similarity index 100%
rename from db/migrations/79_step_216_automatisiertes_eintragen.php
rename to db/migrations/1.79_step_216_automatisiertes_eintragen.php
diff --git a/db/migrations/07_table_token_class.php b/db/migrations/1.7_table_token_class.php
similarity index 100%
rename from db/migrations/07_table_token_class.php
rename to db/migrations/1.7_table_token_class.php
diff --git a/db/migrations/80_skiplinks_enable_configuration.php b/db/migrations/1.80_skiplinks_enable_configuration.php
similarity index 100%
rename from db/migrations/80_skiplinks_enable_configuration.php
rename to db/migrations/1.80_skiplinks_enable_configuration.php
diff --git a/db/migrations/81_step_209_teilnehmericon.php b/db/migrations/1.81_step_209_teilnehmericon.php
similarity index 100%
rename from db/migrations/81_step_209_teilnehmericon.php
rename to db/migrations/1.81_step_209_teilnehmericon.php
diff --git a/db/migrations/83_tic1992_privacydefaults.php b/db/migrations/1.83_tic1992_privacydefaults.php
similarity index 100%
rename from db/migrations/83_tic1992_privacydefaults.php
rename to db/migrations/1.83_tic1992_privacydefaults.php
diff --git a/db/migrations/84_step_226_dozenten_labels.php b/db/migrations/1.84_step_226_dozenten_labels.php
similarity index 100%
rename from db/migrations/84_step_226_dozenten_labels.php
rename to db/migrations/1.84_step_226_dozenten_labels.php
diff --git a/db/migrations/85_tic2007_schedule_enable.php b/db/migrations/1.85_tic2007_schedule_enable.php
similarity index 100%
rename from db/migrations/85_tic2007_schedule_enable.php
rename to db/migrations/1.85_tic2007_schedule_enable.php
diff --git a/db/migrations/86_step_228_raumanfragen.php b/db/migrations/1.86_step_228_raumanfragen.php
similarity index 100%
rename from db/migrations/86_step_228_raumanfragen.php
rename to db/migrations/1.86_step_228_raumanfragen.php
diff --git a/db/migrations/87_tic1091_chat.php b/db/migrations/1.87_tic1091_chat.php
similarity index 100%
rename from db/migrations/87_tic1091_chat.php
rename to db/migrations/1.87_tic1091_chat.php
diff --git a/db/migrations/88_biest2055_terms.php b/db/migrations/1.88_biest2055_terms.php
similarity index 100%
rename from db/migrations/88_biest2055_terms.php
rename to db/migrations/1.88_biest2055_terms.php
diff --git a/db/migrations/89_step_00205_group_calendar.php b/db/migrations/1.89_step_00205_group_calendar.php
similarity index 100%
rename from db/migrations/89_step_00205_group_calendar.php
rename to db/migrations/1.89_step_00205_group_calendar.php
diff --git a/db/migrations/08_step_117_studienmodule.php b/db/migrations/1.8_step_117_studienmodule.php
similarity index 100%
rename from db/migrations/08_step_117_studienmodule.php
rename to db/migrations/1.8_step_117_studienmodule.php
diff --git a/db/migrations/90_tic2395_smileys.php b/db/migrations/1.90_tic2395_smileys.php
similarity index 100%
rename from db/migrations/90_tic2395_smileys.php
rename to db/migrations/1.90_tic2395_smileys.php
diff --git a/db/migrations/91_tic2568_comment_internal.php b/db/migrations/1.91_tic2568_comment_internal.php
similarity index 100%
rename from db/migrations/91_tic2568_comment_internal.php
rename to db/migrations/1.91_tic2568_comment_internal.php
diff --git a/db/migrations/92_remove_schedule_user_table.php b/db/migrations/1.92_remove_schedule_user_table.php
similarity index 100%
rename from db/migrations/92_remove_schedule_user_table.php
rename to db/migrations/1.92_remove_schedule_user_table.php
diff --git a/db/migrations/93_sem_classes_convert_into_db.php b/db/migrations/1.93_sem_classes_convert_into_db.php
similarity index 100%
rename from db/migrations/93_sem_classes_convert_into_db.php
rename to db/migrations/1.93_sem_classes_convert_into_db.php
diff --git a/db/migrations/94_step237_datafields_mandatory.php b/db/migrations/1.94_step237_datafields_mandatory.php
similarity index 100%
rename from db/migrations/94_step237_datafields_mandatory.php
rename to db/migrations/1.94_step237_datafields_mandatory.php
diff --git a/db/migrations/95_extend_userstudiengang_primarykey.php b/db/migrations/1.95_extend_userstudiengang_primarykey.php
similarity index 100%
rename from db/migrations/95_extend_userstudiengang_primarykey.php
rename to db/migrations/1.95_extend_userstudiengang_primarykey.php
diff --git a/db/migrations/96_step00234_homepageplugin_activation.php b/db/migrations/1.96_step00234_homepageplugin_activation.php
similarity index 100%
rename from db/migrations/96_step00234_homepageplugin_activation.php
rename to db/migrations/1.96_step00234_homepageplugin_activation.php
diff --git a/db/migrations/97_init_personal_notifications.php b/db/migrations/1.97_init_personal_notifications.php
similarity index 100%
rename from db/migrations/97_init_personal_notifications.php
rename to db/migrations/1.97_init_personal_notifications.php
diff --git a/db/migrations/98_user_data_to_config.php b/db/migrations/1.98_user_data_to_config.php
similarity index 100%
rename from db/migrations/98_user_data_to_config.php
rename to db/migrations/1.98_user_data_to_config.php
diff --git a/db/migrations/99_step00245_simpleormap.php b/db/migrations/1.99_step00245_simpleormap.php
similarity index 100%
rename from db/migrations/99_step00245_simpleormap.php
rename to db/migrations/1.99_step00245_simpleormap.php
diff --git a/db/migrations/09_step_00111_admission.php b/db/migrations/1.9_step_00111_admission.php
similarity index 100%
rename from db/migrations/09_step_00111_admission.php
rename to db/migrations/1.9_step_00111_admission.php
diff --git a/db/migrations/20201114_migration_history.php b/db/migrations/20201114_migration_history.php
deleted file mode 100644
index 7d82a8c39cb932e014c852a5f4f4d37716f4bdb5..0000000000000000000000000000000000000000
--- a/db/migrations/20201114_migration_history.php
+++ /dev/null
@@ -1,24 +0,0 @@
-<?php
-class MigrationHistory extends Migration
-{
-    public function description()
-    {
-        return 'Extends migration table to allow a history of who did what when';
-    }
-
-    public function up()
-    {
-        $query = "ALTER TABLE `schema_versions`
-                  ADD COLUMN `user_id` CHAR(32) CHARACTER SET latin1 COLLATE latin1_bin NULL DEFAULT NULL,
-                  ADD COLUMN `mkdate` INT(11) UNSIGNED NULL DEFAULT NULL";
-        DBManager::get()->exec($query);
-    }
-
-    public function down()
-    {
-        $query = "ALTER TABLE `schema_versions`
-                  DROP COLUMN `user_id`,
-                  DROP COLUMN `mkdate`";
-        DBManager::get()->exec($query);
-    }
-}
diff --git a/db/migrations/259_migrations_reloaded.php b/db/migrations/259_migrations_reloaded.php
deleted file mode 100644
index 722c7e243918d680072a297578e303153406af25..0000000000000000000000000000000000000000
--- a/db/migrations/259_migrations_reloaded.php
+++ /dev/null
@@ -1,58 +0,0 @@
-<?php
-class MigrationsReloaded extends Migration
-{
-    public function description()
-    {
-        return 'switch from a single successive migration version number to '
-             . 'a collection of already executed migrations';
-    }
-
-    public function up()
-    {
-        $query = "CREATE TABLE IF NOT EXISTS `schema_versions` (
-                    `domain` VARCHAR(255) COLLATE latin1_bin NOT NULL,
-                    `version` BIGINT(20) UNSIGNED NOT NULL,
-                    PRIMARY KEY `domain` (`domain`, `version`)
-                ) ENGINE=InnoDB ROW_FORMAT=DYNAMIC";
-        DBManager::get()->exec($query);
-
-        $query = "SELECT `domain`, `version`
-                  FROM `schema_version`
-                  WHERE `version` > 0";
-        $rows = DBManager::get()->query($query)->fetchAll(PDO::FETCH_NUM);
-
-        $query = "INSERT INTO `schema_versions`
-                  VALUES (:domain, :version)";
-        $statement = DBManager::get()->prepare($query);
-        foreach ($rows as list($domain, $version)) {
-            $statement->bindValue(':domain', $domain);
-
-            for ($i = 1; $i <= $version; $i += 1) {
-                $statement->bindValue(':version', $i);
-                $statement->execute();
-            }
-        }
-
-       $query = "DROP TABLE IF EXISTS `schema_version`";
-       DBManager::get()->exec($query);
-    }
-
-    public function down()
-    {
-        $query = "CREATE TABLE IF NOT EXISTS `schema_version` (
-                    `domain` VARCHAR(255) COLLATE latin1_bin NOT NULL,
-                    `version` INT(11) UNSIGNED NOT NULL,
-                    PRIMARY KEY (`domain`)
-                  ) ENGINE=InnoDB ROW_FORMAT=DYNAMIC";
-        DBManager::get()->exec($query);
-
-        $query = "INSERT IGNORE INTO `schema_version`
-                  SELECT `domain`, MAX(`version`)
-                  FROM `schema_versions`
-                  GROUP BY `domain`";
-        DBManager::get()->exec($query);
-
-        $query = "DROP TABLE IF EXISTS `schema_versions`";
-        DBManager::get()->exec($query);
-    }
-};
diff --git a/db/migrations/20210603_add_config_resources_confirm_plan_drag_and_drop.php b/db/migrations/5.1.1_add_config_resources_confirm_plan_drag_and_drop.php
similarity index 96%
rename from db/migrations/20210603_add_config_resources_confirm_plan_drag_and_drop.php
rename to db/migrations/5.1.1_add_config_resources_confirm_plan_drag_and_drop.php
index 1bbfc1d41d994f68df901cbbfebb783cf462dd43..c722c9cee913e7a4b015a14b517363e8e7909ec8 100644
--- a/db/migrations/20210603_add_config_resources_confirm_plan_drag_and_drop.php
+++ b/db/migrations/5.1.1_add_config_resources_confirm_plan_drag_and_drop.php
@@ -14,7 +14,7 @@ class AddConfigResourcesConfirmPlanDragAndDrop extends Migration
         $db = DBManager::get();
 
         $db->exec(
-            "INSERT INTO `config`
+            "INSERT IGNORE INTO `config`
             (`field`, `value`, `type`, `range`,
             `section`,
             `mkdate`, `chdate`,
diff --git a/lib/classes/JsonApi/Routes/Courseware/BlocksCopy.php b/lib/classes/JsonApi/Routes/Courseware/BlocksCopy.php
index 26c49ce5cf8e995548480901a48b9b67ab710749..53c3bf06198182001dcbde3fcafe2d375b5e3343 100755
--- a/lib/classes/JsonApi/Routes/Courseware/BlocksCopy.php
+++ b/lib/classes/JsonApi/Routes/Courseware/BlocksCopy.php
@@ -23,7 +23,7 @@ use Psr\Http\Message\ServerRequestInterface as Request;
 
 class BlocksCopy extends NonJsonApiController
 {
-    public function __invoke(Request $request, Response $response, $args)
+    public function __invoke(Request $request, Response $response, array $args)
     {
 
         $data = $request->getParsedBody()['data'];
diff --git a/lib/classes/JsonApi/Routes/Courseware/ContainersCopy.php b/lib/classes/JsonApi/Routes/Courseware/ContainersCopy.php
index 1ab0f885d8650b7cf2158d7424e81b0eccec791e..f476a038d35025c177d6fd8e9613fc0e4c9b157d 100755
--- a/lib/classes/JsonApi/Routes/Courseware/ContainersCopy.php
+++ b/lib/classes/JsonApi/Routes/Courseware/ContainersCopy.php
@@ -24,7 +24,7 @@ use Psr\Http\Message\ServerRequestInterface as Request;
 
 class ContainersCopy extends NonJsonApiController
 {
-    public function __invoke(Request $request, Response $response, $args)
+    public function __invoke(Request $request, Response $response, array $args)
     {
         $data = $request->getParsedBody()['data'];
 
diff --git a/lib/classes/JsonApi/Routes/Courseware/StructuralElementsCopy.php b/lib/classes/JsonApi/Routes/Courseware/StructuralElementsCopy.php
index 24b4475a57a558788c2625d1b0b6a9719521ddb6..96b0815db12fe1d08e96960c7e60a62ce5b3bb5e 100755
--- a/lib/classes/JsonApi/Routes/Courseware/StructuralElementsCopy.php
+++ b/lib/classes/JsonApi/Routes/Courseware/StructuralElementsCopy.php
@@ -22,7 +22,7 @@ use Psr\Http\Message\ServerRequestInterface as Request;
 
 class StructuralElementsCopy extends NonJsonApiController
 {
-    public function __invoke(Request $request, Response $response, $args)
+    public function __invoke(Request $request, Response $response, array $args)
     {
         $data = $request->getParsedBody()['data'];
 
diff --git a/lib/classes/JsonApi/Routes/Courseware/StructuralElementsImageDelete.php b/lib/classes/JsonApi/Routes/Courseware/StructuralElementsImageDelete.php
index 54dfb1265fba349e4d0643839a28e41b53126302..ff9e5f5ffebaa08e2b1fe5230f8bca7707960fcd 100755
--- a/lib/classes/JsonApi/Routes/Courseware/StructuralElementsImageDelete.php
+++ b/lib/classes/JsonApi/Routes/Courseware/StructuralElementsImageDelete.php
@@ -11,7 +11,7 @@ use Psr\Http\Message\ServerRequestInterface as Request;
 
 class StructuralElementsImageDelete extends NonJsonApiController
 {
-    public function invoke(Request $request, Response $response, $args)
+    public function invoke(Request $request, Response $response, array $args)
     {
         if (!($structuralElement = StructuralElement::find($args['id']))) {
             throw new RecordNotFoundException();
diff --git a/lib/classes/JsonApi/Routes/Courseware/StructuralElementsImageUpload.php b/lib/classes/JsonApi/Routes/Courseware/StructuralElementsImageUpload.php
index b2b43bc5230ea0cf5634c7ed8f48b6b32c5c5bdf..d594d752b4e06ad3c20750c4b43481732e278f0e 100755
--- a/lib/classes/JsonApi/Routes/Courseware/StructuralElementsImageUpload.php
+++ b/lib/classes/JsonApi/Routes/Courseware/StructuralElementsImageUpload.php
@@ -17,7 +17,7 @@ class StructuralElementsImageUpload extends NonJsonApiController
 {
     use CoursewareInstancesHelper, FilesRoutesHelper;
 
-    public function invoke(Request $request, Response $response, $args)
+    public function invoke(Request $request, Response $response, array $args)
     {
         if (!($structuralElement = StructuralElement::find($args['id']))) {
             throw new RecordNotFoundException();
diff --git a/lib/classes/JsonApi/Routes/Events/UserEventsIcal.php b/lib/classes/JsonApi/Routes/Events/UserEventsIcal.php
index 2dbc4c2e33d2fd74872ea397ee51880509ce7848..364474ec6e69acaeb5c10a877fe2410805d53854 100644
--- a/lib/classes/JsonApi/Routes/Events/UserEventsIcal.php
+++ b/lib/classes/JsonApi/Routes/Events/UserEventsIcal.php
@@ -11,7 +11,7 @@ use Psr\Http\Message\ServerRequestInterface as Request;
 
 class UserEventsIcal extends NonJsonApiController
 {
-    public function __invoke(Request $request, Response $response, $args)
+    public function __invoke(Request $request, Response $response, array $args)
     {
         if (!$observedUser = \User::find($args['id'])) {
             throw new RecordNotFoundException();
diff --git a/lib/classes/JsonApi/Routes/Files/FileRefsContentShow.php b/lib/classes/JsonApi/Routes/Files/FileRefsContentShow.php
index 6f54f72ccd14c76d1ddd73d0f10b2019bf6c7c21..5b470c5ebb6ff438a05df02a27addea0adba8688 100644
--- a/lib/classes/JsonApi/Routes/Files/FileRefsContentShow.php
+++ b/lib/classes/JsonApi/Routes/Files/FileRefsContentShow.php
@@ -14,7 +14,7 @@ class FileRefsContentShow extends NonJsonApiController
 {
     use EtagHelperTrait;
 
-    public function invoke(Request $request, Response $response, $args)
+    public function invoke(Request $request, Response $response, array $args)
     {
         if (!$fileRef = \FileRef::find($args['id'])) {
             throw new RecordNotFoundException();
diff --git a/lib/classes/JsonApi/Routes/Files/FileRefsContentUpdate.php b/lib/classes/JsonApi/Routes/Files/FileRefsContentUpdate.php
index 936ef84b6f7ef66e2334f779eb944851c1ac5e82..30c7325d791b137ba964b18fb612f00aaf068438 100644
--- a/lib/classes/JsonApi/Routes/Files/FileRefsContentUpdate.php
+++ b/lib/classes/JsonApi/Routes/Files/FileRefsContentUpdate.php
@@ -13,7 +13,7 @@ class FileRefsContentUpdate extends NonJsonApiController
 {
     use RoutesHelperTrait;
 
-    public function invoke(Request $request, Response $response, $args)
+    public function invoke(Request $request, Response $response, array $args)
     {
         if (!$fileRef = \FileRef::find($args['id'])) {
             throw new RecordNotFoundException();
diff --git a/lib/classes/JsonApi/Routes/Files/FoldersCopy.php b/lib/classes/JsonApi/Routes/Files/FoldersCopy.php
index 046fac118bea30d1dce2d4d31f8ea5c831b5c5f6..cee761d4b2c9ae2b70f60ad93bad70cfa0233884 100644
--- a/lib/classes/JsonApi/Routes/Files/FoldersCopy.php
+++ b/lib/classes/JsonApi/Routes/Files/FoldersCopy.php
@@ -14,7 +14,7 @@ class FoldersCopy extends NonJsonApiController
     /**
      * @SuppressWarnings(PHPMD.UnusedFormalParameters)
      */
-    public function __invoke(Request $request, Response $response, $args)
+    public function __invoke(Request $request, Response $response, array $args)
     {
         if (!$sourceFolder = \FileManager::getTypedFolder($args['id'])) {
             throw new RecordNotFoundException('Could not find source folder.');
diff --git a/lib/classes/PluginAdministration.php b/lib/classes/PluginAdministration.php
index 8a3dda5bc5e1155192323aca2fd270b8dbe7162c..f42f594794e2e667db8dff8a612de89946a5d0e4 100644
--- a/lib/classes/PluginAdministration.php
+++ b/lib/classes/PluginAdministration.php
@@ -251,7 +251,8 @@ class PluginAdministration
 
             if (is_dir($new_pluginpath . '/migrations')) {
                 $migrator = new Migrator($new_pluginpath . '/migrations', $schema_version);
-                $new_version = $migrator->topVersion();
+                $all_branches = array_fill_keys($schema_version->getAllBranches(), 0);
+                $new_version = $migrator->topVersion(true) + $all_branches;
             }
 
             $migrator = new Migrator($plugindir . '/migrations', $schema_version);
diff --git a/lib/classes/StudipCacheFactory.class.php b/lib/classes/StudipCacheFactory.class.php
index 805aa9d8be8ae3c41bda9849f89766ad2050fbb0..5ce2985e2781c4fc90e68e5993d3f64c3b6633ef 100644
--- a/lib/classes/StudipCacheFactory.class.php
+++ b/lib/classes/StudipCacheFactory.class.php
@@ -189,7 +189,7 @@ class StudipCacheFactory
         # default class
         if (is_null($cache_class)) {
             $version = new DBSchemaVersion();
-            if (!$version->contains(224)) {
+            if ($version->get(1) < 224) {
                 // db cache is not yet available, use StudipMemoryCache
                 return 'StudipMemoryCache';
             }
diff --git a/lib/classes/UserManagement.class.php b/lib/classes/UserManagement.class.php
index 2abc36cf22ba94b418f5e0efd4b2c99cd5dde937..b4c04ba8899f4f0f5f25b3813651172b95766e7e 100644
--- a/lib/classes/UserManagement.class.php
+++ b/lib/classes/UserManagement.class.php
@@ -1276,7 +1276,10 @@ class UserManagement
         // include language-specific subject and mailbody
         setTempLanguage($this->user_data['auth_user_md5.user_id']);
 
-        $subject = _("[Stud.IP - %s] Passwortänderung");
+        $subject = sprintf(
+            _("[Stud.IP - %s] Passwortänderung"),
+            Config::get()->UNI_NAME_CLEAN
+        );
 
         $mailbody = sprintf(
             _("Dies ist eine Informationsmail des Stud.IP-Systems\n"
diff --git a/lib/migrations/DBSchemaVersion.php b/lib/migrations/DBSchemaVersion.php
index 797a1c8b5e451519c75c9aa289471abc57d53b79..9d9113a38de87ca168371b43c94023c1b78a5e8e 100644
--- a/lib/migrations/DBSchemaVersion.php
+++ b/lib/migrations/DBSchemaVersion.php
@@ -6,8 +6,8 @@
  *
  * @author    Elmar Ludwig
  * @copyright 2007 Elmar Ludwig
- * @license   GPL2 or any later version
- * @package   migrations
+ * @license    GPL2 or any later version
+ * @package migrations
  */
 class DBSchemaVersion implements SchemaVersion
 {
@@ -19,26 +19,33 @@ class DBSchemaVersion implements SchemaVersion
     private $domain;
 
     /**
-     * schema versions
+     * branch of schema version
      *
-     * @var array
+     * @var string
      */
-    private $versions = [];
+    private $branch;
 
     /**
+     * current schema version numbers
+     *
+     * @access private
      * @var array
      */
-    private $data = [];
+    private $versions;
 
     /**
      * Initialize a new DBSchemaVersion for a given domain.
      * The default domain name is 'studip'.
      *
      * @param string $domain domain name (optional)
+     * @param string $branch schema branch (optional)
      */
-    public function __construct($domain = 'studip')
+    public function __construct($domain = 'studip', $branch = 0)
     {
         $this->domain = $domain;
+        $this->branch = $branch;
+        $this->versions = [0];
+        $this->validateSchemaVersion();
         $this->initSchemaInfo();
     }
 
@@ -53,132 +60,159 @@ class DBSchemaVersion implements SchemaVersion
     }
 
     /**
-     * Initialize the current schema version.
+     * Retrieve the branch of this schema.
+     *
+     * @return string schema branch
      */
-    private function initSchemaInfo()
+    public function getBranch()
     {
-        $this->data = [];
-
-        try {
-            $query = "SELECT version FROM schema_versions WHERE domain = ?";
-            $statement = DBManager::get()->prepare($query);
-            $statement->execute([$this->domain]);
-            $this->versions = $statement->fetchAll(PDO::FETCH_COLUMN);
-        } catch (PDOException $e) {
-            $query = "SELECT version FROM schema_version WHERE domain = ?";
-            $statement = DBManager::get()->prepare($query);
-            $statement->execute([$this->domain]);
-            $this->versions = range(1, $statement->fetchColumn());
-        }
+        return $this->branch;
     }
 
     /**
-     * Retrieve the current schema version.
+     * Retrieve all branches of this schema.
      *
-     * @return int schema version
+     * @return array all schema branches
      */
-    public function get()
+    public function getAllBranches()
     {
-        return $this->versions ? max($this->versions) : 0;
+        return array_keys($this->versions);
     }
 
     /**
-     * Returns whether the given version is already present for the given
-     * domain.
-     *
-     * @param  int $version Version number
-     * @return bool
+     * Check whether the current schema_version supports branches.
      */
-    public function contains($version)
+    private function branchSupported()
     {
-        return in_array($version, $this->versions);
+        $result = DBManager::get()->query("DESCRIBE schema_version 'branch'");
+        return $result && $result->rowCount() > 0;
     }
 
     /**
-     * Set the current schema version.
-     *
-     * @param int $version new schema version
+     * Initialize the current schema versions.
      */
-    public function add($version)
+    private function initSchemaInfo()
     {
-        $version = (int) $version;
-
-        try {
-            $query = "INSERT INTO `schema_versions` (`domain`, `version`)
-                      VALUES (?, ?)";
-            DBManager::get()->execute($query, [
-                $this->domain,
-                $version,
-            ]);
+        if (!$this->branchSupported()) {
+            $branch = $this->domain === 'studip' ? 1 : 0;
+            $query = "SELECT $branch, version FROM schema_version WHERE domain = ?";
+        } else {
+            $query = "SELECT branch, version FROM schema_version WHERE domain = ? ORDER BY branch";
+        }
+        $statement = DBManager::get()->prepare($query);
+        $statement->execute([$this->domain]);
+        $versions = $statement->fetchAll(PDO::FETCH_COLUMN | PDO::FETCH_GROUP | PDO::FETCH_UNIQUE);
 
-            StudipLog::log(
-                'MIGRATE_UP',
-                $version,
-                $this->domain
-            );
-        } catch (PDOException $e) {
-            $query = "UPDATE `schema_version`
-                      SET `version` = ?
-                      WHERE `domain` = ?";;
-            DBManager::get()->execute($query, [
-                $version,
-                $this->domain,
-            ]);
+        foreach ($versions as $branch => $version) {
+            $this->versions[$branch] = (int) $version;
         }
-        NotificationCenter::postNotification(
-            'SchemaVersionDidUpdate',
-            $this->domain,
-            $version
-        );
     }
 
     /**
-     * Removes a schema version.
+     * Retrieve the current schema version.
      *
-     * @param int $version schema version to remove
+     * @param string $branch schema branch (optional)
+     * @return int schema version
      */
-    public function remove($version)
+    public function get($branch = 0)
     {
-        $version = (int) $version;
+        return $this->versions[$branch];
+    }
 
-        try {
-            $query = "DELETE FROM `schema_versions`
-                      WHERE `domain` = ? AND `version` = ?";
-            DBManager::get()->execute($query, [
+    /**
+     * Set the current schema version.
+     *
+     * @param int $version new schema version
+     * @param string $branch schema branch (optional)
+     */
+    public function set($version, $branch = 0)
+    {
+        $this->versions[$branch] = (int) $version;
+
+        if (!$this->branchSupported()) {
+            $query = "INSERT INTO schema_version (domain, version)
+                      VALUES (?, ?)
+                      ON DUPLICATE KEY UPDATE version = VALUES(version)";
+            $statement = DBManager::get()->prepare($query);
+            $statement->execute([
                 $this->domain,
                 $version
             ]);
-
-            StudipLog::log(
-                'MIGRATE_DOWN',
-                $version,
-                $this->domain
-            );
-        } catch (PDOException $e) {
-            $query = "UPDATE `schema_version`
-                      SET `version` = ?
-                      WHERE `domain` = ?";
-            DBManager::get()->execute($query, [
-                $version,
+        } else {
+            $query = "INSERT INTO schema_version (domain, branch, version)
+                      VALUES (?, ?, ?)
+                      ON DUPLICATE KEY UPDATE version = VALUES(version)";
+            $statement = DBManager::get()->prepare($query);
+            $statement->execute([
                 $this->domain,
+                $branch,
+                $version
             ]);
         }
         NotificationCenter::postNotification(
-            'SchemaVersionDidDelete',
+            'SchemaVersionDidUpdate',
             $this->domain,
             $version
         );
     }
 
     /**
-     * @param $domain
-     * @param $version
-     * @return string
+     * Validate correct structure of schema_version table. This
+     * will upgrade the schema from 4.4 style to 5.1 if necessary.
      */
-    static public function exists($domain, $version)
+    private function validateSchemaVersion()
     {
-        return (bool)DBManager::get()->fetchColumn(
-            "SELECT 1 FROM schema_versions WHERE `domain` = ? AND `version` = ?",
-            [$domain, $version]);
+        $db = DBManager::get();
+        $result = $db->query("SHOW TABLES LIKE 'schema_versions'");
+
+        if ($result && $result->rowCount() > 0) {
+            $backported_migrations = [
+                20200306, 20200713, 20200811, 20200909, 20200910,
+                20201002, 20201103, 202011031, 20210317
+            ];
+
+            // drop backported migrations
+            $query = "DELETE FROM schema_versions
+                      WHERE domain = 'studip' AND version in (?)";
+            $db->execute($query, [$backported_migrations]);
+
+            // drop migrations with irregular numbers
+            $query = "DELETE FROM schema_versions
+                      WHERE domain = 'studip' AND LENGTH(version) > 8";
+            $db->exec($query);
+
+            $query = "CREATE TABLE schema_version (
+                        domain VARCHAR(255) COLLATE latin1_bin NOT NULL,
+                        branch VARCHAR(64) COLLATE latin1_bin NOT NULL DEFAULT '0',
+                        version INT(11) UNSIGNED NOT NULL,
+                        PRIMARY KEY (domain, branch)
+                      ) ENGINE=InnoDB ROW_FORMAT=DYNAMIC";
+            $db->exec($query);
+
+            $query = "INSERT INTO schema_version
+                      SELECT domain, '0', MAX(version) FROM schema_versions
+                      GROUP BY domain";
+            $db->exec($query);
+
+            $query = "DROP TABLE schema_versions";
+            $db->exec($query);
+
+            $schema_mapping = [
+                20190917 => 269,
+                20200307 => 285,
+                20200522 => 290,
+                20210511 => 327,
+                20210603 => 327
+            ];
+
+            $query = "UPDATE schema_version SET branch = '1' WHERE domain = 'studip'";
+            $db->exec($query);
+
+            foreach ($schema_mapping as $old_version => $new_version) {
+                $query = "UPDATE schema_version SET version = ?
+                          WHERE domain = 'studip' AND version = ?";
+                $db->execute($query, [$new_version, $old_version]);
+            }
+        }
     }
 }
diff --git a/lib/migrations/Migration.php b/lib/migrations/Migration.php
index 355bfe2dee502d2acfe32145468886f72721ccf6..d989d5341e36eceec6cd1d406f302d15419a3662 100644
--- a/lib/migrations/Migration.php
+++ b/lib/migrations/Migration.php
@@ -100,12 +100,28 @@ abstract class Migration
      *
      * @param string $format,... printf-style format string and parameters
      */
-    protected function announce($format /* , ... */)
+    public function announce($format /* , ... */)
     {
         # format message
         $args = func_get_args();
         $message = vsprintf(array_shift($args), $args);
 
-        return $this->write(Migrator::mark($message));
+        return $this->write($this->mark($message));
     }
+
+    /**
+     * Pads and highlights a given text to a specific length with the given
+     * sign.
+     *
+     * @param string $text
+     * @param string $sign
+     */
+    protected function mark($text, $sign = '=')
+    {
+        $text = trim($text);
+        if ($text) {
+            $text = " {$text} ";
+        }
+        return str_pad("{$sign}{$sign}{$text}", 79, $sign, STR_PAD_RIGHT);
+     }
 }
diff --git a/lib/migrations/Migrator.php b/lib/migrations/Migrator.php
index 92ec808c8dee567284eb18ba7390c3b5c5aabc91..a959317acca1138abef9864238a1e25304e0cfc8 100644
--- a/lib/migrations/Migrator.php
+++ b/lib/migrations/Migrator.php
@@ -38,16 +38,14 @@
  *
  * (\d+)_([a-z_]+).php   // (index)_(name).php
  *
- * 20180524110400_my_first_migration.php
- * 20180812152300_another_migration.php
- * 20181110100900_and_one_last.php
+ * 001_my_first_migration.php
+ * 002_another_migration.php
+ * 003_and_one_last.php
  *
- * Those numbers are used to order your migrations. Use the current time to
- * define the chronological order of migrations. Gaps are allowed. In previous
- * versions of the migration system, the numbers were naturally ordered starting
- * with 1 but that proved to be rather unflexible regarding bug fixes that
- * needed a migration to be executed. Thus, every executed migration number is
- * stored and you may add a migration lateron between two other migrations.
+ * Those numbers are used to order your migrations. The first migration has
+ * to be a 1 (but you can use leading 0). Every following migration has to be
+ * the successor to the previous migration. No gaps are allowed. Just use
+ * natural numbers starting with 1.
  *
  * When migrating those numbers are used to determine the migrations needed to
  * fulfill the target version.
@@ -102,7 +100,7 @@
  *   $migrator = new Migrator($path, $version, $verbose);
  *
  *   # now migrate to target version
- *   $migrator->migrateTo(20181128100139);
+ *   $migrator->migrateTo(5);
  *
  * If you want to migrate to the highest migration, you can just use NULL as
  * parameter:
@@ -116,8 +114,6 @@
  */
 class Migrator
 {
-    const FILE_REGEXP = '/\b(\d+)([_-][_a-z0-9]+)+\.php$/';
-
     /**
      * Direction of migration, either "up" or "down"
      *
@@ -135,9 +131,9 @@ class Migrator
     /**
      * Specifies the target version, may be NULL (alias for "highest migration")
      *
-     * @var int
+     * @var array
      */
-    private $target_version;
+    private $target_versions;
 
     /**
      * How verbose shall the migrator be?
@@ -192,92 +188,97 @@ class Migrator
      * the current schema version (provided by the SchemaVersion object) and a
      * target version calling the methods #up and #down in sequence.
      *
-     * @param mixed  the target version as an integer or NULL thus migrating to
-     *               the top migration
+     * @param mixed  the target version as an integer, array or NULL thus
+     *               migrating to the top migrations
      */
     public function migrateTo($target_version)
     {
         $migrations = $this->relevantMigrations($target_version);
+        $target_branch = $this->schema_version->getBranch();
 
         # you're on the right version
         if (empty($migrations)) {
-            $this->log("You are already at %d.\n", $this->schema_version->get());
+            $this->log('You are already at %d.', $this->schema_version->get($target_branch));
             return;
         }
 
         $this->log(
-            "Currently at version %d. Now migrating %s to %d.\n",
-            $this->schema_version->get(),
+            'Currently at version %d. Now migrating %s to %d.',
+            $this->schema_version->get($target_branch),
             $this->direction,
-            $this->target_version
+            max($this->target_versions)
         );
 
-        foreach ($migrations as $version => $migration) {
-            $this->execute($version, $this->direction, $migration);
+        foreach ($migrations as $number => $migration) {
+            list($branch, $version) = $this->migrationBranchAndVersion($number);
+
+            $action = $this->isUp() ? 'Migrating' : 'Reverting';
+            $migration->announce("{$action} %s", $number);
+
+            if ($migration->description()) {
+                $this->log($migration->description());
+                $this->log(str_repeat('-', 79));
+            }
+
+            $time_start = microtime(true);
+            $migration->migrate($this->direction);
+
+            $action = $this->isUp() ? 'Migrated' : 'Reverted';
+            $this->log('');
+            $migration->announce("{$action} in %ss", round(microtime(true) - $time_start, 3));
+            $this->log('');
+
+            $this->schema_version->set($this->isDown() ? $version - 1 : $version, $branch);
+
+            $action = $this->isUp() ? 'MIGRATE_UP' : 'MIGRATE_DOWN';
+            StudipLog::log($action, $number, $this->schema_version->getDomain());
         }
     }
 
     /**
-     * Executes a migration's up or down method
+     * Calculate the selected target versions for all relevant branches. If a
+     * single branch is selected for migration, only that branch and all its
+     * children are considered relevant.
      *
-     * @param  string $version      Version to execute
-     * @param  string $direction    Up or down
-     * @param  Migration $migration Migration to execute (optional, will be
-     *                              loaded if missing)
+     * @param mixed  the target version as an integer, array or NULL thus
+     *               migrating to the top migrations
+     *
+     * @return array an associative array, whose keys are the branch names
+     *               and whose values are the target versions
      */
-    public function execute($version, $direction, Migration $migration = null)
+    public function targetVersions($target_version)
     {
-        if ($this->isUp($direction) && $this->schema_version->contains($version)) {
-            $this->log("Version {$version} is already present.\n");
-            return;
+        $top_versions = $this->topVersion(true);
+        $target_branch = $this->schema_version->getBranch();
+  
+        if (is_array($target_version)) {
+            return $target_version;
         }
-
-        if ($this->isDown($direction) && !$this->schema_version->contains($version)) {
-            $this->log("Version {$version} is not present.\n");
-            return;
-        }
-
-        if ($migration === null) {
-            $migrations = $this->migrationClasses();
-            if (!isset($migrations[$version])) {
-                throw new Exception("Version {$version} is invalid");
+  
+        $max_version = $target_branch ? $target_branch . '.' . $target_version : $target_version;
+  
+        foreach ($top_versions as $branch => $version) {
+            if ($branch == $target_branch) {
+                if (isset($target_version)) {
+                    $top_versions[$branch] = $target_version;
+                }
+            } else if ($target_branch && strpos($branch, $target_branch . '.') !== 0) {
+                unset($top_versions[$branch]);
+            } else if (isset($target_version) && version_compare($branch, $max_version) >= 0) {
+                $top_versions[$branch] = 0;
             }
-            list($file, $class) = $migrations[$version];
-            $migration = $this->loadMigration($file, $class);
-        }
-
-        $action = $this->isUp($direction) ? 'Migrating' : 'Reverting';
-
-        $this->announce("{$action} %d", $version);
-        if ($migration->description()) {
-            $this->log($migration->description());
-            $this->log(self::mark('', '-'));
-        }
-
-        $time_start = microtime(true);
-        $migration->migrate($direction);
-
-        $action = $this->isUp($direction) ? 'Migrated' : 'Reverted';
-        $this->log('');
-        $this->announce("{$action} in %ss", round(microtime(true) - $time_start, 3));
-        $this->log('');
-
-        // Update schema version
-        if ($this->isDown($direction)) {
-            $this->schema_version->remove($version);
-        } else {
-            $this->schema_version->add($version);
         }
-
+  
+        return $top_versions;
     }
-
+  
     /**
      * Invoking this method will return a list of migrations with an index between
      * the current schema version (provided by the SchemaVersion object) and a
      * target version calling the methods #up and #down in sequence.
      *
-     * @param mixed  the target version as an integer or NULL thus migrating to
-     *               the top migration
+     * @param mixed  the target version as an integer, array or NULL thus
+     *               migrating to the top migrations
      *
      * @return array an associative array, whose keys are the migration's
      *               version and whose values are the migration objects
@@ -287,35 +288,29 @@ class Migrator
         // Load migrations
         $migrations = $this->migrationClasses();
 
-        // Determine correct target version
-        $this->target_version = $target_version === null
-                              ? $this->topVersion()
-                              : (int) $target_version;
-
-        // Determine max version (might differ from max schema version in db in
-        // development systems)
-        $max_version = min($this->topVersion(), $this->schema_version->get());
+        // Determine correct target versions
+        $this->target_versions = $this->targetVersions($target_version);
 
         // Determine migration direction
-        if ($this->target_version > 0 && $this->target_version >= $max_version) {
-            $this->direction = 'up';
-        } else {
-            $this->direction = 'down';
+        foreach ($this->target_versions as $branch => $version) {
+            if ($this->schema_version->get($branch) < $version) {
+                $this->direction = 'up';
+                break;
+            } else if ($version < $this->schema_version->get($branch)) {
+                $this->direction = 'down';
+                break;
+            }
         }
 
         // Sort migrations in correct order
-        uksort($migrations, function ($a, $b) {
-            if (mb_strlen($a) > 8 && mb_strlen($b) > 8) {
-                return $a - $b;
-            }
-            return mb_substr($a, 0, 8) - mb_substr($b, 0, 8);
-        });
+        uksort($migrations, 'version_compare');
 
         if (!$this->isUp()) {
             $migrations = array_reverse($migrations, true);
         }
 
         $result = [];
+
         foreach ($migrations as $version => $migration_file_and_class) {
             if (!$this->relevantMigration($version)) {
                 continue;
@@ -323,10 +318,15 @@ class Migrator
 
             list($file, $class) = $migration_file_and_class;
 
-            try {
-                $result[$version] = $this->loadMigration($file, $class);
-            } catch (Exception $e) {
+            $migration = require_once $file;
+
+            if (!$migration instanceof Migration) {
+                $migration = new $class($this->verbose);
+            } else {
+                $migration->setVerbose($this->verbose);
             }
+
+            $result[$version] = $migration;
         }
 
         return $result;
@@ -342,47 +342,30 @@ class Migrator
      */
     private function relevantMigration($version)
     {
-        if ($this->isUp()) {
-            return !$this->schema_version->contains($version)
-                && $version <= $this->target_version;
-        } elseif ($this->isDown()) {
-            return $this->schema_version->contains($version)
-                && $version > $this->target_version;
+        list($branch, $version) = $this->migrationBranchAndVersion($version);
+        $current_version = $this->schema_version->get($branch);
+
+        if (!isset($this->target_versions[$branch])) {
+            return false;
+        } else if ($this->isUp()) {
+            return $current_version < $version
+                && $version <= $this->target_versions[$branch];
+        } else if ($this->isDown()) {
+            return $current_version >= $version
+                && $version > $this->target_versions[$branch];
         }
 
         return false;
     }
 
-    /**
-     * Loads a migration from the given file and creates and instance of it.
-     *
-     * @param string $file  File name of migration to load
-     * @param string $class Class name to expect to be loaded from the file
-     * @return Migration instance
-     */
-    private function loadMigration($file, $class)
-    {
-        if (class_exists($class)) {
-            $migration = new $class($this->verbose);
-        } else {
-            $migration = require $file;
-            if (!$migration instanceof Migration) {
-                $migration = new $class($this->verbose);
-            } else {
-                $migration->setVerbose($this->verbose);
-            }
-        }
-        return $migration;
-    }
-
     /**
      * Am I migrating up?
      *
      * @return bool  TRUE if migrating up, FALSE otherwise
      */
-    private function isUp($direction = null)
+    private function isUp()
     {
-        return ($direction ?: $this->direction) === 'up';
+        return $this->direction === 'up';
     }
 
     /**
@@ -390,9 +373,9 @@ class Migrator
      *
      * @return bool  TRUE if migrating down, FALSE otherwise
      */
-    private function isDown($direction = null)
+    private function isDown()
     {
-        return ($direction ?: $this->direction) === 'down';
+        return $this->direction === 'down';
     }
 
     /**
@@ -433,10 +416,7 @@ class Migrator
      */
     protected function migrationFiles()
     {
-        $files = glob($this->migrations_path . '/*.php');
-        $files = array_filter($files, function ($file) {
-            return preg_match(self::FILE_REGEXP, $file);
-        });
+        $files = glob($this->migrations_path . '/[0-9]*_*.php');
         return $files;
     }
 
@@ -450,19 +430,43 @@ class Migrator
     protected function migrationVersionAndName($migration_file)
     {
         $matches = [];
-        preg_match(self::FILE_REGEXP, $migration_file, $matches);
-        return [(int) $matches[1], $matches[2]];
+        preg_match('/\b([0-9.]+)_([_a-z0-9]*)\.php$/', $migration_file, $matches);
+        return [$matches[1], $matches[2]];
+    }
+
+    /**
+     * Split a migration version into its branch and version parts.
+     *
+     * @param string  a migration version
+     * @return array  an array of two elements containing the migration's branch
+     *                and version on this branch.
+     */
+    public function migrationBranchAndVersion($version)
+    {
+        if (preg_match('/^(.*)\.([0-9]+)$/', $version, $matches)) {
+            $branch = preg_replace('/\b0+/', '', $matches[1]);
+            $version = (int) $matches[2];
+        } else {
+            $branch = '0';
+            $version = (int) $version;
+        }
+        return [$branch, $version];
     }
 
     /**
      * Returns the top migration's version.
      *
+     * @param bool  return top version for all branches, not just default one
      * @return int  the top migration's version.
      */
-    public function topVersion()
+    public function topVersion($all_branches = false)
     {
-        $versions = array_keys($this->migrationClasses());
-        return $versions ? max($versions) : 0;
+        $versions = [0];
+        foreach (array_keys($this->migrationClasses()) as $version) {
+            list($branch, $version) = $this->migrationBranchAndVersion($version);
+            $versions[$branch] = max($versions[$branch], $version);
+        }
+        return $all_branches ? $versions : $versions[$this->schema_version->getBranch()];
     }
 
     /**
@@ -479,39 +483,6 @@ class Migrator
         }
 
         $args = func_get_args();
-        vprintf(trim(array_shift($args)) . "\n", $args);
-    }
-
-
-    /**
-     * Overridable method used to return a textual representation of a stronger
-     * ouput of what's going on in me. You can use me as you would use printf.
-     *
-     * @param string $format just a dummy value, instead use this method as you
-     *                       would use printf & co.
-     */
-    protected function announce($format)
-    {
-        # format message
-        $args = func_get_args();
-        $message = vsprintf(array_shift($args), $args);
-
-        return $this->log(self::mark($message));
-    }
-
-    /**
-     * Pads and highlights a given text to a specific length with the given
-     * sign.
-     *
-     * @param string $text
-     * @param string $sign
-     */
-    public static function mark($text, $sign = '=')
-    {
-        $text = trim($text);
-        if ($text) {
-            $text = " {$text} ";
-        }
-        return str_pad("{$sign}{$sign}{$text}", 79, $sign, STR_PAD_RIGHT);
+        vprintf(array_shift($args) . "\n", $args);
     }
 }
diff --git a/lib/migrations/SchemaVersion.php b/lib/migrations/SchemaVersion.php
index bbb2a6b26a7337dc320ae21c2e818dea4844b8fe..d2cf7662d12a1cf4706aafe9a3295d571e6d80cd 100644
--- a/lib/migrations/SchemaVersion.php
+++ b/lib/migrations/SchemaVersion.php
@@ -14,32 +14,32 @@
 interface SchemaVersion
 {
     /**
-     * Returns current schema version (as maximum number).
+     * Retrieve the branch of this schema.
      *
-     * @return int schema version
+     * @return string schema branch
      */
-    public function get();
+    public function getBranch();
 
     /**
-     * Returns whether the given version is already present for the given
-     * domain.
+     * Retrieve all branches of this schema.
      *
-     * @param  int $version Version number
-     * @return bool
+     * @return array all schema branches
      */
-    public function contains($version);
+    public function getAllBranches();
 
     /**
-     * Adds a schema version.
+     * Returns current schema version.
      *
-     * @param int $version schema version
+     * @param string $branch schema branch (optional)
+     * @return int schema version
      */
-    public function add($version);
+    public function get($branch = 0);
 
     /**
-     * Removes a schema version.
+     * Sets the new schema version.
      *
-     * @param int $version schema version
+     * @param int $version new schema version
+     * @param string $branch schema branch (optional)
      */
-    public function remove($version);
+    public function set($version, $branch = 0);
 }
diff --git a/lib/models/StudipNews.class.php b/lib/models/StudipNews.class.php
index d164a4a0694d2c276850fcd8efcca5c22220477d..b528604bfbf8b27ca374ef0aaca1686e54cb5498 100644
--- a/lib/models/StudipNews.class.php
+++ b/lib/models/StudipNews.class.php
@@ -109,9 +109,6 @@ class StudipNews extends SimpleORMap implements PrivacyObject
 
     public static function CountUnread($range_id = 'studip', $user_id = false)
     {
-        if (!DBSchemaVersion::exists('studip', '20210201')) {
-            return 0;
-        }
         $query = "SELECT SUM(nw.chdate > IFNULL(b.visitdate, :threshold) AND nw.user_id != :user_id)
                   FROM news_range a
                   LEFT JOIN news nw ON (a.news_id = nw.news_id AND UNIX_TIMESTAMP() BETWEEN date AND date + expire)
diff --git a/lib/plugins/engine/PluginManager.class.php b/lib/plugins/engine/PluginManager.class.php
index 13973592e0b4c83031249f5e17bbfb71facee4dc..5f67c40aad264653e56b0113d2ea6cf81d88bf92 100644
--- a/lib/plugins/engine/PluginManager.class.php
+++ b/lib/plugins/engine/PluginManager.class.php
@@ -192,10 +192,6 @@ class PluginManager
      */
     public function isPluginActivated ($id, $context)
     {
-        if (!DBSchemaVersion::exists('studip', '20210201')) {
-            return null;
-        }
-
         if (!$context) {
             return null;
         }
diff --git a/resources/vue/components/courseware/CoursewareStructuralElement.vue b/resources/vue/components/courseware/CoursewareStructuralElement.vue
index 92eec3a7898b1c7b197c428edc7a73c13a39d5cf..00a24fb315878c38ed22734a5447373256b61e75 100755
--- a/resources/vue/components/courseware/CoursewareStructuralElement.vue
+++ b/resources/vue/components/courseware/CoursewareStructuralElement.vue
@@ -389,6 +389,7 @@
 
 <script>
 import ContainerComponents from './container-components.js';
+import CoursewarePluginComponents from './plugin-components.js'
 import CoursewareStructuralElementPermissions from './CoursewareStructuralElementPermissions.vue';
 import CoursewareAccordionContainer from './CoursewareAccordionContainer.vue';
 import CoursewareCompanionBox from './CoursewareCompanionBox.vue';
@@ -965,6 +966,6 @@ export default {
         this.pluginManager.registerComponentsLocally(this);
     },
     // this line provides all the components to courseware plugins
-    provide: () => ({ containerComponents: ContainerComponents }),
+    provide: () => ({ containerComponents: ContainerComponents, coursewarePluginComponents: CoursewarePluginComponents }),
 };
 </script>
diff --git a/resources/vue/components/courseware/plugin-components.js b/resources/vue/components/courseware/plugin-components.js
new file mode 100644
index 0000000000000000000000000000000000000000..e3f236ee2ca23c5582692abf89745c8c34186298
--- /dev/null
+++ b/resources/vue/components/courseware/plugin-components.js
@@ -0,0 +1,21 @@
+import CoursewareBlockAdderArea from './CoursewareBlockAdderArea.vue';
+import CoursewareCollapsibleBox from './CoursewareCollapsibleBox.vue';
+import CoursewareCompanionBox from './CoursewareCompanionBox.vue';
+import CoursewareDefaultBlock from './CoursewareDefaultBlock.vue';
+import CoursewareDefaultContainer from './CoursewareDefaultContainer.vue';
+import CoursewareFileChooser from './CoursewareFileChooser.vue';
+import CoursewareTabs from './CoursewareTabs.vue';
+import CoursewareTab from './CoursewareTab.vue';
+
+const CoursewarePluginComponents = {
+    CoursewareBlockAdderArea,
+    CoursewareCollapsibleBox,
+    CoursewareCompanionBox,
+    CoursewareDefaultBlock,
+    CoursewareDefaultContainer,
+    CoursewareFileChooser,
+    CoursewareTabs,
+    CoursewareTab,
+}
+
+export default CoursewarePluginComponents;
\ No newline at end of file
diff --git a/tests/unit/lib/classes/test-migrations/10_test_migration_ten.php b/tests/_data/migrations/10_test_migration_ten.php
similarity index 100%
rename from tests/unit/lib/classes/test-migrations/10_test_migration_ten.php
rename to tests/_data/migrations/10_test_migration_ten.php
diff --git a/tests/unit/lib/classes/test-migrations/1_test_migration_one.php b/tests/_data/migrations/1_test_migration_one.php
similarity index 100%
rename from tests/unit/lib/classes/test-migrations/1_test_migration_one.php
rename to tests/_data/migrations/1_test_migration_one.php
diff --git a/tests/_data/migrations/2.1_test_migration_two_one.php b/tests/_data/migrations/2.1_test_migration_two_one.php
new file mode 100644
index 0000000000000000000000000000000000000000..13126d41ec0dd7ccd06182de7fdfc4b334a8b812
--- /dev/null
+++ b/tests/_data/migrations/2.1_test_migration_two_one.php
@@ -0,0 +1,4 @@
+<?php
+class TestMigrationTwoOne extends Migration
+{
+}
diff --git a/tests/unit/lib/classes/test-migrations/2_test_migration_two.php b/tests/_data/migrations/2_test_migration_two.php
similarity index 100%
rename from tests/unit/lib/classes/test-migrations/2_test_migration_two.php
rename to tests/_data/migrations/2_test_migration_two.php
diff --git a/tests/unit/lib/classes/MigrationTest.php b/tests/unit/lib/classes/MigrationTest.php
index 7085c9ee9d7f09456bc9f3192412029e36f116c2..0aabbcd42c6824c0ac9ea06acb42f4bfc5de4d47 100644
--- a/tests/unit/lib/classes/MigrationTest.php
+++ b/tests/unit/lib/classes/MigrationTest.php
@@ -13,9 +13,7 @@ class MigrationTest extends \Codeception\Test\Unit
 
     public function setUp(): void
     {
-        $this->before = isset($GLOBALS['CACHING_ENABLE'])
-                      ? $GLOBALS['CACHING_ENABLE']
-                      : null;
+        $this->before = $GLOBALS['CACHING_ENABLE'] ?? null;
         $GLOBALS['CACHING_ENABLE'] = false;
 
         require_once 'lib/classes/StudipCache.class.php';
@@ -41,30 +39,31 @@ class MigrationTest extends \Codeception\Test\Unit
     {
         return new class() implements SchemaVersion
         {
-            private $versions = [];
+            private $versions = [0];
 
-            public function get()
+            public function getDomain()
             {
-                return count($this->versions) > 0 ? max($this->versions) : 0;
+                return 'test';
             }
 
-            public function contains($version)
+            public function getBranch()
             {
-                return in_array($version, $this->versions);
+                return 0;
             }
 
-            public function add($version)
+            public function getAllBranches()
             {
-                if (!$this->contains($version)) {
-                    $this->versions[] = $version;
-                }
+                return array_keys($this->versions);
             }
 
-            public function remove($version)
+            public function get($branch = 0)
             {
-                if ($this->contains($version)) {
-                    $this->versions = array_diff($this->versions, [$version]);
-                }
+                return $this->versions[$branch];
+            }
+
+            public function set($version, $branch = 0)
+            {
+                $this->versions[$branch] = (int) $version;
             }
         };
     }
@@ -72,29 +71,11 @@ class MigrationTest extends \Codeception\Test\Unit
     private function getMigrator($schema_version = null)
     {
         return new Migrator(
-            __DIR__ . '/test-migrations',
+            TEST_FIXTURES_PATH . 'migrations',
             $schema_version ?: $this->getSchemaVersion()
         );
     }
 
-    public function testSchemaVersion()
-    {
-        $schema_version = $this->getSchemaVersion();
-        $this->assertSame(0, $schema_version->get());
-
-        $schema_version->add(1);
-        $this->assertTrue($schema_version->contains(1));
-        $this->assertSame(1, $schema_version->get());
-
-        $schema_version->add(2);
-        $this->assertTrue($schema_version->contains(2));
-        $this->assertSame(2, $schema_version->get());
-
-        $schema_version->remove(1);
-        $this->assertFalse($schema_version->contains(1));
-        $this->assertSame(2, $schema_version->get());
-    }
-
     public function testRelevance()
     {
         $migrator = $this->getMigrator();
@@ -102,10 +83,10 @@ class MigrationTest extends \Codeception\Test\Unit
         $relevant = $migrator->relevantMigrations(null);
         $this->assertSame(4, count($relevant));
 
-        $migrator->migrateTo(10);
+        $migrator->migrateTo(2);
 
         $relevant = $migrator->relevantMigrations(null);
-        $this->assertSame(1, count($relevant));
+        $this->assertSame(2, count($relevant));
     }
 
     public function testMigrationUp()
@@ -113,7 +94,7 @@ class MigrationTest extends \Codeception\Test\Unit
         $schema_version = $this->getSchemaVersion();
         $migrator = $this->getMigrator($schema_version);
         $migrator->migrateTo(null);
-        $this->assertSame(20190417, $schema_version->get());
+        $this->assertSame(10, $schema_version->get());
         $this->assertSame(0, count($migrator->relevantMigrations(null)));
 
         return $schema_version;
@@ -133,13 +114,12 @@ class MigrationTest extends \Codeception\Test\Unit
     public function testGaps()
     {
         $schema_version = $this->getSchemaVersion();
-        $schema_version->add(2);
-        $schema_version->add(10);
+        $schema_version->set(10);
 
         $migrator = $this->getMigrator($schema_version);
 
         $relevant = $migrator->relevantMigrations(null);
-        $this->assertSame(2, count($relevant));
-        $this->assertEquals([1, 20190417], array_keys($relevant));
+        $this->assertSame(1, count($relevant));
+        $this->assertEquals(['2.1'], array_keys($relevant));
     }
 }
diff --git a/tests/unit/lib/classes/test-migrations/20190417_returns_instance.php b/tests/unit/lib/classes/test-migrations/20190417_returns_instance.php
deleted file mode 100644
index c55e214167994d386e47fbb530e5b05ec836e9f7..0000000000000000000000000000000000000000
--- a/tests/unit/lib/classes/test-migrations/20190417_returns_instance.php
+++ /dev/null
@@ -1,5 +0,0 @@
-<?php
-return new class() extends Migration
-{
-
-};