diff --git a/controllers/solutions.php b/controllers/solutions.php
index 9e03ced2474b16bfe5e69e3d9156e111f230f6ed..54e97650ba9bb11e8b6da5256d2fba1175886d82 100644
--- a/controllers/solutions.php
+++ b/controllers/solutions.php
@@ -303,6 +303,16 @@ class SolutionsController extends StudipController
             $solvers[$solver_id]['extra_info'] = $extra_info;
         }
 
+        $confirm = [];
+
+        if (!$released) {
+            $num_corrected = $assignment->assignment_attempts->filter(function($a) { return $a->options['corrected']; })->count();
+
+            if ($num_corrected > 0 && $num_corrected < count($assignment->assignment_attempts)) {
+                $confirm = ['data-confirm' => _vips('Es sind noch nicht alle Ergebnisse bestätigt. Wollen Sie trotzdem alle Ergebnisse freigeben?')];
+            }
+        }
+
         $this->assignment                    = $assignment;
         $this->assignment_id                 = $assignment_id;
         $this->view                          = $view;
@@ -420,16 +430,16 @@ class SolutionsController extends StudipController
                 $released == 0);
             $widget->addRadioButton(_vips('vergebene Punkte'),
                 $this->link_for('solutions/change_released', ['assignment_id' => $assignment_id, 'released' => 1]),
-                $released == 1);
+                $released == 1, $confirm);
             $widget->addRadioButton(_vips('Punkte und Kommentare'),
                 $this->link_for('solutions/change_released', ['assignment_id' => $assignment_id, 'released' => 2]),
-                $released == 2);
+                $released == 2, $confirm);
             $widget->addRadioButton(_vips('… zusätzlich Aufgaben und Korrektur'),
                 $this->link_for('solutions/change_released', ['assignment_id' => $assignment_id, 'released' => 3]),
-                $released == 3);
+                $released == 3, $confirm);
             $widget->addRadioButton(_vips('… zusätzlich Musterlösungen'),
                 $this->link_for('solutions/change_released', ['assignment_id' => $assignment_id, 'released' => 4]),
-                $released == 4);
+                $released == 4, $confirm);
             Sidebar::get()->addWidget($widget);
 
             $widget = new SidebarWidget();
@@ -1147,6 +1157,96 @@ class SolutionsController extends StudipController
         $this->redirect($this->url_for('solutions/assignment_solutions', compact('assignment_id', 'view')));
     }
 
+    /**
+     * Add assignment level feedback to an assignment attempt.
+     */
+    public function edit_feedback_dialog_action()
+    {
+        $assignment_id = Request::int('assignment_id');
+        $assignment    = VipsAssignment::find($assignment_id);
+        $user_ids      = Request::optionArray('user_ids');
+        $view          = Request::option('view');
+
+        vips_require_edit_permission($assignment);
+
+        $this->assignment = $assignment;
+        $this->user_ids   = $user_ids;
+        $this->view       = $view;
+        $this->feedback   = null;
+        $this->corrected  = null;
+
+        foreach ($user_ids as $user_id) {
+            $assignment_attempt = $assignment->getAssignmentAttempt($user_id);
+            $feedback = $assignment->getUserFeedback($user_id) ?? '';
+            $corrected = $assignment_attempt->options['corrected'] ?? 0;
+
+            if (!isset($this->feedback) || $this->feedback === $feedback) {
+                $this->feedback = $feedback;
+            } else {
+                $this->feedback = '';
+            }
+
+            if (!isset($this->corrected) || $this->corrected === $corrected) {
+                $this->corrected = $corrected;
+            } else {
+                $this->corrected = 0;
+            }
+        }
+    }
+
+    /**
+     * Store feedback and status for an assignment attempt.
+     */
+    public function edit_feedback_action()
+    {
+        CSRFProtection::verifyUnsafeRequest();
+
+        $assignment_id = Request::int('assignment_id');
+        $assignment    = VipsAssignment::find($assignment_id);
+        $user_ids      = Request::optionArray('user_ids');
+        $view          = Request::option('view');
+
+        $feedback      = trim(Request::get('feedback'));
+        $feedback      = Studip\Markup::purifyHtml($feedback);
+        $corrected     = Request::int('corrected');
+
+        vips_require_edit_permission($assignment);
+
+        foreach ($user_ids as $user_id) {
+            $assignment_attempt = $assignment->getAssignmentAttempt($user_id);
+
+            if ($assignment_attempt) {
+                $options = $assignment_attempt->options;
+
+                if ($corrected && !$options['corrected']) {
+                    $feedback .= Studip\Markup::markAsHtml('<p>' . htmlReady(sprintf(_('Bewertung wurde am %s von %s bestätigt.'), date('d.m.Y'), get_fullname())) . '</p>');
+                }
+
+                unset($options['feedback']);
+                unset($options['corrected']);
+
+                if ($feedback !== '') {
+                    $options['feedback'] = $feedback;
+                }
+
+                if ($corrected) {
+                    $options['corrected'] = $corrected;
+                }
+
+                $assignment_attempt->options = $options;
+                $assignment_attempt->store();
+            }
+        }
+
+        if ($feedback === '') {
+            PageLayout::postSuccess(_vips('Der Kommentar zur Bewertung wurde entfernt.'));
+        } else {
+            PageLayout::postSuccess(_vips('Der Kommentar zur Bewertung wurde gespeichert.'));
+        }
+
+        $this->redirect($this->url_for('solutions/assignment_solutions', compact('assignment_id', 'view')));
+    }
+
     /**
      * Write a message to selected members for an assignment.
      */
diff --git a/lib/VipsAssignment.php b/lib/VipsAssignment.php
index a91e17e0b76a1303e0ae754ffd4131ef4bc3f692..823946e29c6072c65c1130d18816ceaee171cc23 100644
--- a/lib/VipsAssignment.php
+++ b/lib/VipsAssignment.php
@@ -1133,6 +1133,12 @@ class VipsAssignment extends SimpleORMap
      */
     public function getUserFeedback($user_id)
     {
+        $assignment_attempt = $this->getAssignmentAttempt($user_id);
+
+        if ($assignment_attempt && isset($assignment_attempt->options['feedback'])) {
+            return $assignment_attempt->options['feedback'];
+        }
+
         if (isset($this->options['feedback'])) {
             $user_points = $this->getUserPoints($user_id);
             $max_points = $this->test->getTotalPoints();
diff --git a/views/solutions/assignment_solutions.php b/views/solutions/assignment_solutions.php
index beea89e0da97930dad118cf83b4ff6610f31b4b3..33233e6c893417c3e6039539e465d27dc753f123 100644
--- a/views/solutions/assignment_solutions.php
+++ b/views/solutions/assignment_solutions.php
@@ -199,6 +199,9 @@
                             <? $menu->addLink($controller->url_for('solutions/show_assignment_log', ['assignment_id' => $assignment_id, 'solver_id' => $solver['user_id']]),
                                    _vips('Abgabeprotokoll anzeigen'), Icon::create('log'), ['data-dialog' => 'size=auto']
                                ) ?>
+                            <? $menu->addLink($controller->url_for('solutions/edit_feedback_dialog', ['assignment_id' => $assignment_id, 'user_ids[]' => $solver['user_id'], 'view' => $view]),
+                                   _vips('Kommentar zur Bewertung'), Icon::create('feedback'), ['data-dialog' => 'size=800x440']
+                               ) ?>
                         <? endif ?>
                         <? if ($uploaded_files > 0): ?>
                             <? $menu->addLink($controller->url_for('solutions/download_uploads', ['assignment_id' => $assignment_id, 'solver_id' => $solver['user_id']]),
@@ -298,6 +301,12 @@
                                 'formmethod' => 'post',
                                 'formtarget' => '_blank'
                             ]) ?>
+                        <?= Studip\Button::create(_vips('Kommentar zur Bewertung'), 'feedback', [
+                                'class' => 'batch_action',
+                                'formaction' => $controller->url_for('solutions/edit_feedback_dialog'),
+                                'formmethod' => 'post',
+                                'data-dialog' => 'size=800x440'
+                            ]) ?>
                         <?= Studip\Button::create(_vips('Nachricht schreiben'), 'message', [
                                 'class' => 'batch_action',
                                 'formaction' => $controller->url_for('solutions/write_message'),
diff --git a/views/solutions/edit_feedback_dialog.php b/views/solutions/edit_feedback_dialog.php
new file mode 100644
index 0000000000000000000000000000000000000000..e54cb3a09e34e599d91e0c83099712e86306d845
--- /dev/null
+++ b/views/solutions/edit_feedback_dialog.php
@@ -0,0 +1,22 @@
+<form class="default" action="<?= $controller->link_for('solutions/edit_feedback') ?>" method="POST">
+    <?= CSRFProtection::tokenTag() ?>
+    <input type="hidden" name="assignment_id" value="<?= $assignment->id ?>">
+    <? foreach ($user_ids as $user_id): ?>
+        <input type="hidden" name="user_ids[]" value="<?= $user_id ?>">
+    <? endforeach ?>
+    <input type="hidden" name="view" value="<?= $view ?>">
+
+    <label>
+        <?= _vips('Kommentar zur Bewertung') ?>
+        <textarea name="feedback" class="size-l wysiwyg" data-editor="toolbar=small"><?= wysiwygReady($feedback) ?></textarea>
+    </label>
+
+    <label>
+        <input type="checkbox" name="corrected" value="1" <?= $corrected ? 'checked' : '' ?>>
+        <?= _vips('Bestätigungsvermerk hinzufügen') ?>
+    </label>
+
+    <footer data-dialog-button>
+        <?= Studip\Button::createAccept(_vips('Speichern'), 'save') ?>
+    </footer>
+</form>