diff --git a/app/controllers/course/courseware.php b/app/controllers/course/courseware.php
index f8e721e0b2eeec6d68fd160e79465c68fb98b5e5..af7d0e912fd6cfa8a90eaffd55ee95d4bcffe1fb 100644
--- a/app/controllers/course/courseware.php
+++ b/app/controllers/course/courseware.php
@@ -36,6 +36,12 @@ class Course_CoursewareController extends CoursewareController
         $this->licenses = $this->getLicenses();
         $this->oer_enabled = Config::get()->OERCAMPUS_ENABLED && $GLOBALS['perm']->have_perm(Config::get()->OER_PUBLIC_STATUS);
         $this->unitsNotFound = Unit::countBySql('range_id = ?', [Context::getId()]) === 0;
+
+        $this->feedback_settings = json_encode([
+            'activated' => \Feedback::isActivated(),
+            'adminPerm' => \Feedback::hasAdminPerm(Context::getId()),
+            'createPerm' => \Feedback::hasCreatePerm(Context::getId()),
+        ]);
     }
 
     public function index_action(): void
diff --git a/app/controllers/course/feedback.php b/app/controllers/course/feedback.php
index dec20009bbbab084cbe3c35ffb241717f4bc5bb9..c6eb9771be60c71dee66a7d196905d06b75f90d0 100644
--- a/app/controllers/course/feedback.php
+++ b/app/controllers/course/feedback.php
@@ -49,7 +49,7 @@ class Course_FeedbackController extends AuthenticatedController
                 $widget->addLink(
                     _('Neues Feedback-Element'),
                     $this->url_for('course/feedback/create_form'),
-                    Icon::create('star')
+                    Icon::create('add')
                 )->asDialog();
             }
         }
@@ -72,6 +72,8 @@ class Course_FeedbackController extends AuthenticatedController
             'results_visible'   => 1,
             'commentable'       => 1,
             'mode'              => FeedbackElement::MODE_5STAR_RATING,
+            'mode'              => 1,
+            'anonymous_entries' => 1,
         ]);
     }
 
@@ -99,6 +101,7 @@ class Course_FeedbackController extends AuthenticatedController
                 'description'       =>  Studip\Markup::purifyHtml(Request::get('description')),
                 'results_visible'   => intval(Request::get('results_visible')),
                 'commentable'       => $commentable,
+                'anonymous_entries' => intval(Request::get('anonymous_entries')),
                 'mode'              => $mode
             ]);
             $feedback->store();
@@ -232,11 +235,13 @@ class Course_FeedbackController extends AuthenticatedController
             if ($rating == 0) {
                 $rating = 1;
             }
+            $anonymous = intval(Request::get('anonymous'));
             $entry =  FeedbackEntry::build([
                 'feedback_id'   => $this->feedback->id,
                 'user_id'       => $GLOBALS['user']->id,
                 'rating'        => $rating,
-                'comment'       => trim(Request::get('comment'))
+                'comment'       => trim(Request::get('comment')),
+                'anonymous'     => $anonymous,
             ]);
             $entry->store();
             PageLayout::postSuccess(_('Feedback gespeichert'));
@@ -268,6 +273,7 @@ class Course_FeedbackController extends AuthenticatedController
             }
         $entry->comment = trim(Request::get('comment'));
         $entry->rating  = $rating;
+        $entry->anonymous = Request::int('anonymous', 0);
         $entry->store();
         PageLayout::postSuccess(_('Änderungen gespeichert'));
         $this->redirect($entry->feedback->getRange()->getRangeUrl());
diff --git a/app/views/course/courseware/courseware.php b/app/views/course/courseware/courseware.php
index 2dadc86a667ec66b19d73b344841cede8d019628..9de924d18f190db7adcdae636fcf3b352b82dcd5 100644
--- a/app/views/course/courseware/courseware.php
+++ b/app/views/course/courseware/courseware.php
@@ -6,6 +6,7 @@
         entry-id="<?= htmlReady(Context::getId()) ?>"
         unit-id="<?= htmlReady($unit_id) ?>"
         licenses='<?= htmlReady($licenses) ?>'
+        feedback-settings='<?= htmlReady($feedback_settings) ?>'
         >
     </div>
 <? endif; ?>
diff --git a/app/views/course/courseware/index.php b/app/views/course/courseware/index.php
index 81296cbb149d1cd1ba7fd01baa2ec1acfe645158..eea40635c425b26ded94d3d63c1a93cf3488a3fc 100644
--- a/app/views/course/courseware/index.php
+++ b/app/views/course/courseware/index.php
@@ -3,4 +3,5 @@
     entry-type="courses"
     entry-id="<?= Context::getId() ?>"
     licenses='<?= $licenses ?>'
+    feedback-settings='<?= htmlReady($feedback_settings) ?>'
 ></div>
diff --git a/app/views/course/feedback/_add_edit_entry_form.php b/app/views/course/feedback/_add_edit_entry_form.php
index b117b782e9db296c3fff8e064da4c01a5b56de59..1481f57fb747054ca10ce20acead54ed5f777fe3 100644
--- a/app/views/course/feedback/_add_edit_entry_form.php
+++ b/app/views/course/feedback/_add_edit_entry_form.php
@@ -29,6 +29,12 @@
     <textarea name="comment"><?= htmlReady(isset($entry) ? $entry->comment : '') ?></textarea>
 </label>
 <? endif; ?>
+<? if ($feedback->anonymous_entries) : ?>
+<label>
+    <input type="checkbox" name="anonymous" value="1" <?= $entry->anonymous ? 'checked' : '' ?> >
+    <?= _('Kommentar anonym abgeben') ?>
+</label>
+<? endif; ?>
 <div>
     <?= Studip\Button::createAccept(_('Absenden'), 'add', ['class' => 'feedback-entry-submit']) ?>
     <?= Studip\Button::createCancel(_('Abbrechen'), 'cancel', ['class' => 'feedback-entry-cancel']) ?>
diff --git a/app/views/course/feedback/_entry.php b/app/views/course/feedback/_entry.php
index 29f35fbd967c8a938c311596cf8c6b258222138d..b7473d120451ca5ba9ae2c22a42b699f82e008b7 100644
--- a/app/views/course/feedback/_entry.php
+++ b/app/views/course/feedback/_entry.php
@@ -1,10 +1,15 @@
 <article class="studip feedback-entry" data-id="<?= $entry->id ?>">
     <header>
         <h1>
+            <? if (!$entry->anonymous): ?>
             <a href="<?= URLHelper::getLink('dispatch.php/profile?username=' . $entry->user->username) ?>">
                 <?= Avatar::getAvatar($entry->user_id)->getImageTag(Avatar::SMALL) ?>
                 <?= htmlReady($entry->user->getFullName()) ?>
             </a>
+            <? else: ?>
+                <?= Avatar::getNobody()->getImageTag(Avatar::SMALL) ?>
+                <?= _('Anonym') ?>
+            <? endif; ?>
         </h1>
         <nav>
             <? if ($entry->isEditable()) : ?>
diff --git a/app/views/course/feedback/_new_edit_feedback_form.php b/app/views/course/feedback/_new_edit_feedback_form.php
index 224cd11e3f25cdc028f0e6a2e438078f81f5bd55..df6b8212aab16f08f74bce69a9a35c2e5367607d 100644
--- a/app/views/course/feedback/_new_edit_feedback_form.php
+++ b/app/views/course/feedback/_new_edit_feedback_form.php
@@ -14,7 +14,17 @@
     </label>
     <label>
         <input type="checkbox" name="results_visible" value="1" <?= $feedback->results_visible == 1 ? 'checked' : '' ?>>
-        <?= _('Feedback Ergebnisse nach Antwort sichtbar') ?>
+        <?= _('Feedback-Ergebnisse nach Antwort sichtbar') ?>
+    </label>
+    <label>
+        <input
+            type="checkbox"
+            name="anonymous_entries"
+            value="1"
+            <?= $this->current_action === 'edit_form' ? 'disabled' : '' ?>
+            <?= $feedback->anonymous_entries ? 'checked' : '' ?>
+        > 
+        <?= _('Feedback kann anonym abgegeben werden')?>
     </label>
     <label>
         <input id="comment-activated" type="checkbox" name="commentable" value="1" <? if ($this->current_action ==
diff --git a/app/views/course/feedback/index.php b/app/views/course/feedback/index.php
index 602d445a33f62aaca82b4c570bca2f18195d3b31..87a2449fc867cee3d6071b324197a0a68f8a2fdc 100644
--- a/app/views/course/feedback/index.php
+++ b/app/views/course/feedback/index.php
@@ -102,13 +102,13 @@
                         $actionMenu = ActionMenu::get()->setContext($feedback->question);
                         $actionMenu->addLink(
                             $controller->link_for('course/feedback/edit_form/' . $feedback->id),
-                            _('Feedback-Element bearbeiten'),
+                            _('Bearbeiten'),
                             Icon::create('edit', Icon::ROLE_CLICKABLE, ['size' => 20]),
                             ['data-dialog' => '']
                         );
                         $actionMenu->addLink(
                             $controller->link_for('course/feedback/delete/' . $feedback->id),
-                            _('Feedback-Element löschen'),
+                            _('Löschen'),
                             Icon::create('trash', Icon::ROLE_CLICKABLE, ['size' => 20]),
                             ['onclick' => "return STUDIP.Dialog.confirmAsPost('" . _('Feedback-Element und dazugehörige Einträge löschen?') . "', this.href);"]
                         );
diff --git a/db/migrations/5.5.22_add_feedback_anonymous_entries.php b/db/migrations/5.5.22_add_feedback_anonymous_entries.php
new file mode 100644
index 0000000000000000000000000000000000000000..2e557b532e79c1c28d611aeb64d41005c97428a3
--- /dev/null
+++ b/db/migrations/5.5.22_add_feedback_anonymous_entries.php
@@ -0,0 +1,27 @@
+<?php
+final class AddFeedbackAnonymousEntries extends Migration
+{
+    public function description()
+    {
+        return 'Extend feedback tables for anonymous entries';
+    }
+
+    public function up()
+    {
+        \DBManager::get()->exec("ALTER TABLE `feedback` 
+            ADD `anonymous_entries` TINYINT(1) NOT NULL DEFAULT 0 
+            AFTER `commentable`
+        ");
+
+        \DBManager::get()->exec("ALTER TABLE `feedback_entries` 
+            ADD `anonymous` TINYINT(1) NOT NULL DEFAULT 0
+            AFTER `rating`
+        ");
+    }
+
+    public function down()
+    {
+        \DBManager::get()->exec("ALTER TABLE `feedback` DROP `anonymous_entries`");
+        \DBManager::get()->exec("ALTER TABLE `feedback_entries` DROP `anonymous`");
+    }
+}
diff --git a/lib/classes/FeedbackRange.interface.php b/lib/classes/FeedbackRange.interface.php
index f5eaaeff743f622619105796e93d2ad858eadb61..863c197ba64f4424e8be500144c7ac74948b851c 100644
--- a/lib/classes/FeedbackRange.interface.php
+++ b/lib/classes/FeedbackRange.interface.php
@@ -11,6 +11,13 @@
 
 interface FeedbackRange
 {
+    /**
+     * Returns the ID of this range.
+     *
+     * @return string|integer The ID of the range.
+     */
+    public function getId();
+
     /**
      * Returns a human-friendly representation of the FeedbackRange object instance's name.
      *
diff --git a/lib/classes/JsonApi/RouteMap.php b/lib/classes/JsonApi/RouteMap.php
index e86617bcfbffc42feb740ab97343ebef2acd783b..d4d5bbb21210e48392e1f8f75e08ce8025a4f4f8 100644
--- a/lib/classes/JsonApi/RouteMap.php
+++ b/lib/classes/JsonApi/RouteMap.php
@@ -239,12 +239,20 @@ class RouteMap
     private function addAuthenticatedFeedbackRoutes(RouteCollectorProxy $group): void
     {
         $group->get('/feedback-elements/{id}', Routes\Feedback\FeedbackElementsShow::class);
-        $group->get('/feedback-elements/{id}/entries', Routes\Feedback\FeedbackEntriesIndex::class);
         $group->get('/courses/{id}/feedback-elements', Routes\Feedback\FeedbackElementsByCourseIndex::class);
         $group->get('/file-refs/{id}/feedback-elements', Routes\Feedback\FeedbackElementsByFileRefIndex::class);
         $group->get('/folders/{id}/feedback-elements', Routes\Feedback\FeedbackElementsByFolderIndex::class);
 
+        $group->post('/feedback-elements', Routes\Feedback\FeedbackElementsCreate::class);
+        $group->patch('/feedback-elements/{id}', Routes\Feedback\FeedbackElementsUpdate::class);
+        $group->delete('/feedback-elements/{id}', Routes\Feedback\FeedbackElementsDelete::class);
+
+        $group->get('/feedback-elements/{id}/entries', Routes\Feedback\FeedbackEntriesIndex::class);
+        $group->post('/feedback-entries', Routes\Feedback\FeedbackEntriesCreate::class);
+
         $group->get('/feedback-entries/{id}', Routes\Feedback\FeedbackEntriesShow::class);
+        $group->patch('/feedback-entries/{id}', Routes\Feedback\FeedbackEntriesUpdate::class);
+        $group->delete('/feedback-entries/{id}', Routes\Feedback\FeedbackEntriesDelete::class);
     }
 
     private function addAuthenticatedInstitutesRoutes(RouteCollectorProxy $group): void
diff --git a/lib/classes/JsonApi/Routes/Courseware/CoursesUnitsIndex.php b/lib/classes/JsonApi/Routes/Courseware/CoursesUnitsIndex.php
index dde67bcb18eb12e55ac84432fe61e7ac1ef83421..b09d0c8b5c11228c10730504dfb80cf64afd22d0 100644
--- a/lib/classes/JsonApi/Routes/Courseware/CoursesUnitsIndex.php
+++ b/lib/classes/JsonApi/Routes/Courseware/CoursesUnitsIndex.php
@@ -19,6 +19,7 @@ class CoursesUnitsIndex extends JsonApiController
     protected $allowedIncludePaths = [
         'structural-element',
         'creator',
+        'feedback-element',
     ];
 
     protected $allowedPagingParameters = ['offset', 'limit'];
diff --git a/lib/classes/JsonApi/Routes/Courseware/CoursewareInstancesUpdate.php b/lib/classes/JsonApi/Routes/Courseware/CoursewareInstancesUpdate.php
index 8bb01968b7f21ac70a36ba9f5997e3be22ee1001..5f0269089541d36935bf067c3f3ee339fca8ff18 100644
--- a/lib/classes/JsonApi/Routes/Courseware/CoursewareInstancesUpdate.php
+++ b/lib/classes/JsonApi/Routes/Courseware/CoursewareInstancesUpdate.php
@@ -67,10 +67,12 @@ class CoursewareInstancesUpdate extends JsonApiController
                     return 'Attribute `favorite-block-types` contains an invalid block type.';
                 }
             }
-        } elseif (self::arrayHas($json, 'data.attributes.sequential-progression')) {
+        } 
+        
+        if (self::arrayHas($json, 'data.attributes.sequential-progression')) {
             $sequentialProgression = self::arrayGet($json, 'data.attributes.sequential-progression');
-            if (!is_bool($sequentialProgression)) {
-                return 'Attribute `sequential-progression` must be a bool.';
+            if (!in_array($sequentialProgression, [0, 1])) {
+                return 'Attribute `sequential-progression` must be 0 or 1.';
             }
         }
 
@@ -94,6 +96,20 @@ class CoursewareInstancesUpdate extends JsonApiController
             }
         }
 
+        if (self::arrayHas($json, 'data.attributes.show-feedback-popup')) {
+            $showFeedbackPopup = self::arrayGet($json, 'data.attributes.show-feedback-popup');
+            if (!in_array($showFeedbackPopup, [0,1])) {
+                return 'Attribute `show-feedback-popup` must be 0 or 1.';
+            }
+        }
+
+        if (self::arrayHas($json, 'data.attributes.show-feedback-in-contentbar')) {
+            $showFeedbackInContentbar = self::arrayGet($json, 'data.attributes.show-feedback-in-contentbar');
+            if (!in_array($showFeedbackInContentbar, [0,1])) {
+                return 'Attribute `show-feedback-in-contentbar` must be 0 or 1.';
+            }
+        }
+
         if (self::arrayHas($json, 'data.attributes.certificate-settings')) {
             $certificateSettings = self::arrayGet($json, 'data.attributes.certificate-settings');
 
@@ -137,6 +153,12 @@ class CoursewareInstancesUpdate extends JsonApiController
         $editingPermissionLevel = $get('data.attributes.editing-permission-level');
         $instance->setEditingPermissionLevel($editingPermissionLevel);
 
+        $showFeedbackPopup = $get('data.attributes.show-feedback-popup');
+        $instance->setShowFeedbackPopup($showFeedbackPopup);
+
+        $showFeedbackInContentbar = $get('data.attributes.show-feedback-in-contentbar');
+        $instance->setShowFeedbackInContentbar($showFeedbackInContentbar);
+
         $certificateSettings = $get('data.attributes.certificate-settings');
         $instance->setCertificateSettings($certificateSettings);
 
diff --git a/lib/classes/JsonApi/Routes/Feedback/Authority.php b/lib/classes/JsonApi/Routes/Feedback/Authority.php
index 44397817fa063b867b91ae19a4657dce56b3a28b..5437a0789d103572801b301287d9ea31ec6d648d 100644
--- a/lib/classes/JsonApi/Routes/Feedback/Authority.php
+++ b/lib/classes/JsonApi/Routes/Feedback/Authority.php
@@ -2,45 +2,88 @@
 
 namespace JsonApi\Routes\Feedback;
 
+use Feedback;
+use FeedbackElement;
+use FeedbackEntry;
+use FeedbackRange;
+use SimpleORMap;
 use User;
 
+/**
+ * @SuppressWarnings(PHPMD.StaticAccess)
+ * @SuppressWarnings(PHPMD.TooManyPublicMethods)
+ */
 class Authority
 {
-    public static function canShowFeedbackElement(User $user, \FeedbackElement $resource)
+    public static function canShowFeedbackElement(User $user, FeedbackElement $resource): bool
     {
-        return \Feedback::hasRangeAccess($resource->range_id, $resource->range_type, $user->id);
+        return Feedback::hasRangeAccess($resource->range_id, $resource->range_type, $user->getId());
     }
 
-    public static function canIndexFeedbackEntries(User $user, \FeedbackElement $resource)
+    public static function canIndexFeedbackEntries(User $user, FeedbackElement $resource): bool
     {
         return self::canShowFeedbackElement($user, $resource);
     }
 
-    public static function canSeeResultsOfFeedbackElement(User $user, \FeedbackElement $resource)
+    public static function canSeeResultsOfFeedbackElement(User $user, FeedbackElement $resource): bool
     {
         return self::canIndexFeedbackEntries($user, $resource) &&
-            ($resource['results_visible'] || \Feedback::hasAdminPerm($resource['course_id'], $user->id));
+            ($resource['results_visible'] || \Feedback::hasAdminPerm($resource['course_id'], $user->getId()));
     }
 
-    public static function canIndexFeedbackElementsOfCourse(User $user, \Course $course)
+    public static function canIndexFeedbackElementsOfCourse(User $user, \Course $course): bool
     {
-        return \Feedback::hasRangeAccess($course->id, \Course::class, $user->id);
+        return \Feedback::hasRangeAccess($course->getId(), \Course::class, $user->getId());
     }
 
-    public static function canIndexFeedbackElementsOfFileRef(User $user, \FileRef $fileRef)
+    public static function canIndexFeedbackElementsOfFileRef(User $user, \FileRef $fileRef): bool
     {
-        return \Feedback::hasRangeAccess($fileRef->id, \FileRef::class, $user->id);
+        return \Feedback::hasRangeAccess($fileRef->getId(), \FileRef::class, $user->getId());
     }
 
-    public static function canIndexFeedbackElementsOfFolder(User $user, \Folder $folder)
+    public static function canIndexFeedbackElementsOfFolder(User $user, \Folder $folder): bool
     {
-        return \Feedback::hasRangeAccess($folder->id, \Folder::class, $user->id);
+        return \Feedback::hasRangeAccess($folder->getId(), \Folder::class, $user->getId());
     }
 
-    public static function canShowFeedbackEntry(User $user, \FeedbackEntry $resource)
+    public static function canShowFeedbackEntry(User $user, \FeedbackEntry $resource): bool
     {
         $feedbackElement = $resource->feedback;
 
         return self::canShowFeedbackElement($user, $feedbackElement);
     }
+
+    public static function canCreateFeedbackEntry(User $user, FeedbackElement $element): bool
+    {
+        return $element->isFeedbackable($user->id);
+    }
+
+    public static function canUpdateFeedbackEntry(User $user, FeedbackEntry $entry): bool
+    {
+        return $entry->isEditable($user->id);
+    }
+
+    public static function canDeleteFeedbackEntry(User $user, FeedbackEntry $entry): bool
+    {
+        return $entry->isDeletable($user->id);
+    }
+
+    public static function canCreateFeedbackElement(User $user, FeedbackRange $range): bool
+    {
+        return $range->isRangeAccessible($user->id)
+            && Feedback::hasCreatePerm($range->getRangeCourseId(), $user->id);
+    }
+
+    public static function canUpdateFeedbackElement(User $user, FeedbackElement $element): bool
+    {
+        $range = $element->getRange();
+
+        return $range->isRangeAccessible($user->id)
+            && Feedback::hasAdminPerm($range->getRangeCourseId(), $user->id);
+    }
+
+    public static function canDeleteFeedbackElement(User $user, FeedbackElement $element): bool
+    {
+        return self::canUpdateFeedbackElement($user, $element);
+    }
 }
diff --git a/lib/classes/JsonApi/Routes/Feedback/FeedbackElementsCreate.php b/lib/classes/JsonApi/Routes/Feedback/FeedbackElementsCreate.php
new file mode 100644
index 0000000000000000000000000000000000000000..1269d8c8a273ee999e45f6257a153ddd9533f130
--- /dev/null
+++ b/lib/classes/JsonApi/Routes/Feedback/FeedbackElementsCreate.php
@@ -0,0 +1,114 @@
+<?php
+
+namespace JsonApi\Routes\Feedback;
+
+use FeedbackElement;
+use FeedbackRange;
+use User;
+use JsonApi\Errors\AuthorizationFailedException;
+use JsonApi\JsonApiController;
+use JsonApi\Routes\ValidationTrait;
+use JsonApi\Schemas\FeedbackElement as FeedbackElementSchema;
+use Psr\Http\Message\ResponseInterface as Response;
+use Psr\Http\Message\ServerRequestInterface as Request;
+
+/**
+ * Create a FeedbackElement.
+ *
+ * @SuppressWarnings(PHPMD.StaticAccess)
+ */
+class FeedbackElementsCreate extends JsonApiController
+{
+    use RangeTypeAware;
+    use ValidationTrait;
+
+    /**
+     * @SuppressWarnings(PHPMD.UnusedFormalParameter)
+     *
+     * @param array $args
+     *
+     * @return Response
+     */
+    public function __invoke(Request $request, Response $response, $args)
+    {
+        $this->preparePossibleRangeTypes();
+
+        $json = $this->validate($request);
+        $range = $this->getRangeFromJson($json);
+        $user = $this->getUser($request);
+
+        if (!Authority::canCreateFeedbackElement($user, $range)) {
+            throw new AuthorizationFailedException();
+        }
+
+        $feedbackElement = $this->create($user, $json);
+
+        return $this->getCreatedResponse($feedbackElement);
+    }
+
+    /**
+     * @SuppressWarnings(PHPMD.UnusedFormalParameters)
+     *
+     * @param array $json
+     * @param mixed $data
+     *
+     * @return string|void
+     */
+    protected function validateResourceDocument($json, $data)
+    {
+        if (!self::arrayHas($json, 'data')) {
+            return 'Missing `data` member at document´s top level.';
+        }
+        if (FeedbackElementSchema::TYPE !== self::arrayGet($json, 'data.type')) {
+            return 'Invalid `type` of document´s `data`.';
+        }
+        if (self::arrayHas($json, 'data.id')) {
+            return 'New document must not have an `id`.';
+        }
+
+        $required = ['question', 'description', 'mode', 'results-visible', 'is-commentable', 'anonymous-entries'];
+        foreach ($required as $attribute) {
+            if (!self::arrayHas($json, 'data.attributes.' . $attribute)) {
+                return 'Missing `' . $attribute . '` attribute.';
+            }
+        }
+
+        if (!self::arrayHas($json, 'data.relationships.range')) {
+            return 'Missing `range` relationship.';
+        }
+        if (!$this->getRangeFromJson($json)) {
+            return 'Invalid `range` relationship.';
+        }
+    }
+
+    private function getRangeFromJson(array $json): ?FeedbackRange
+    {
+        $rangeType = self::arrayGet($json, 'data.relationships.range.data.type');
+        $rangeId = self::arrayGet($json, 'data.relationships.range.data.id');
+
+        if (!isset($this->possibleRangeTypes[$rangeType])) {
+            return null;
+        }
+        $rangeClass = $this->possibleRangeTypes[$rangeType];
+
+        return $rangeClass::find($rangeId);
+    }
+
+    private function create(User $user, array $json): FeedbackElement
+    {
+        $range = $this->getRangeFromJson($json);
+        return \FeedbackElement::create([
+            'range_id' => $range->getId(),
+            'range_type' => get_class($range),
+            'user_id' => $user->id,
+            'question' => self::arrayGet($json, 'data.attributes.question'),
+            'description' => self::arrayGet($json, 'data.attributes.description'),
+            'mode' => self::arrayGet($json, 'data.attributes.mode'),
+            'results_visible' => (int) self::arrayGet($json, 'data.attributes.results-visible'),
+            'commentable' => (int) self::arrayGet($json, 'data.attributes.is-commentable'),
+            'anonymous_entries' => (int) self::arrayGet($json, 'data.attributes.anonymous-entries'), 
+            // TODO:
+            'course_id' => $range->getRangeCourseId(),
+        ]);
+    }
+}
diff --git a/lib/classes/JsonApi/Routes/Feedback/FeedbackElementsDelete.php b/lib/classes/JsonApi/Routes/Feedback/FeedbackElementsDelete.php
new file mode 100644
index 0000000000000000000000000000000000000000..874a172e7082cdaa337888e57126288409720081
--- /dev/null
+++ b/lib/classes/JsonApi/Routes/Feedback/FeedbackElementsDelete.php
@@ -0,0 +1,39 @@
+<?php
+
+namespace JsonApi\Routes\Feedback;
+
+use Psr\Http\Message\ServerRequestInterface as Request;
+use Psr\Http\Message\ResponseInterface as Response;
+use JsonApi\Errors\AuthorizationFailedException;
+use JsonApi\Errors\RecordNotFoundException;
+use JsonApi\JsonApiController;
+
+/**
+ * Deletes a feedback element.
+ *
+ * @SuppressWarnings(PHPMD.StaticAccess)
+ */
+class FeedbackElementsDelete extends JsonApiController
+{
+    /**
+     * @SuppressWarnings(PHPMD.UnusedFormalParameter)
+     *
+     * @param array $args
+     *
+     * @return Response
+     */
+    public function __invoke(Request $request, Response $response, $args)
+    {
+        $resource = \FeedbackElement::find($args['id']);
+        if (!$resource) {
+            throw new RecordNotFoundException();
+        }
+
+        if (!Authority::canDeleteFeedbackElement($this->getUser($request), $resource)) {
+            throw new AuthorizationFailedException();
+        }
+        $resource->delete();
+
+        return $this->getCodeResponse(204);
+    }
+}
diff --git a/lib/classes/JsonApi/Routes/Feedback/FeedbackElementsShow.php b/lib/classes/JsonApi/Routes/Feedback/FeedbackElementsShow.php
index 5ecc5932c83f5548d14aabea816251d06e253831..849e58a7ea9f2beed3627b81d3a7e240ed241345 100644
--- a/lib/classes/JsonApi/Routes/Feedback/FeedbackElementsShow.php
+++ b/lib/classes/JsonApi/Routes/Feedback/FeedbackElementsShow.php
@@ -7,20 +7,33 @@ use Psr\Http\Message\ResponseInterface as Response;
 use JsonApi\Errors\AuthorizationFailedException;
 use JsonApi\Errors\RecordNotFoundException;
 use JsonApi\JsonApiController;
+use JsonApi\Schemas\FeedbackElement as FeedbackElementSchema;
 
 /**
  * Displays a certain feedback element.
+ *
+ * @SuppressWarnings(PHPMD.StaticAccess)
  */
 class FeedbackElementsShow extends JsonApiController
 {
-    protected $allowedIncludePaths = ['author', 'course', 'entries', 'range'];
+    protected $allowedIncludePaths = [
+        FeedbackElementSchema::REL_AUTHOR,
+        FeedbackElementSchema::REL_COURSE,
+        FeedbackElementSchema::REL_ENTRIES,
+        FeedbackElementSchema::REL_RANGE,
+    ];
 
     /**
      * @SuppressWarnings(PHPMD.UnusedFormalParameter)
+     *
+     * @param array $args
+     *
+     * @return Response
      */
     public function __invoke(Request $request, Response $response, $args)
     {
-        if (!$resource = \FeedbackElement::find($args['id'])) {
+        $resource = \FeedbackElement::find($args['id']);
+        if (!$resource) {
             throw new RecordNotFoundException();
         }
 
diff --git a/lib/classes/JsonApi/Routes/Feedback/FeedbackElementsUpdate.php b/lib/classes/JsonApi/Routes/Feedback/FeedbackElementsUpdate.php
new file mode 100644
index 0000000000000000000000000000000000000000..2c02352929551f4ab2092788ccb9fbe1e0194bf2
--- /dev/null
+++ b/lib/classes/JsonApi/Routes/Feedback/FeedbackElementsUpdate.php
@@ -0,0 +1,94 @@
+<?php
+
+namespace JsonApi\Routes\Feedback;
+
+use FeedbackElement;
+use FeedbackRange;
+use User;
+use JsonApi\Errors\AuthorizationFailedException;
+use JsonApi\Errors\RecordNotFoundException;
+use JsonApi\JsonApiController;
+use JsonApi\Routes\ValidationTrait;
+use JsonApi\Schemas\FeedbackElement as FeedbackElementSchema;
+use Psr\Http\Message\ResponseInterface as Response;
+use Psr\Http\Message\ServerRequestInterface as Request;
+
+/**
+ * Update a FeedbackElement.
+ *
+ * @SuppressWarnings(PHPMD.StaticAccess)
+ */
+class FeedbackElementsUpdate extends JsonApiController
+{
+    use RangeTypeAware;
+    use ValidationTrait;
+
+    /**
+     * @SuppressWarnings(PHPMD.UnusedFormalParameter)
+     *
+     * @param array $args
+     *
+     * @return Response
+     */
+    public function __invoke(Request $request, Response $response, $args)
+    {
+        $this->preparePossibleRangeTypes();
+        $resource = \FeedbackElement::find($args['id']);
+        if (!$resource) {
+            throw new RecordNotFoundException();
+        }
+
+        $json = $this->validate($request);
+        $user = $this->getUser($request);
+
+        if (!Authority::canUpdateFeedbackElement($user, $resource)) {
+            throw new AuthorizationFailedException();
+        }
+
+        $feedbackElement = $this->update($resource, $json);
+
+        return $this->getContentResponse($feedbackElement);
+    }
+
+    /**
+     * @SuppressWarnings(PHPMD.UnusedFormalParameters)
+     *
+     * @param array $json
+     * @param mixed $data
+     *
+     * @return string|void
+     */
+    protected function validateResourceDocument($json, $data)
+    {
+        if (!self::arrayHas($json, 'data')) {
+            return 'Missing `data` member at document´s top level.';
+        }
+        if (FeedbackElementSchema::TYPE !== self::arrayGet($json, 'data.type')) {
+            return 'Invalid `type` of document´s `data`.';
+        }
+        if (!self::arrayHas($json, 'data.id')) {
+            return 'An existing document must have an `id`.';
+        }
+
+        $required = ['question', 'description'];
+        foreach ($required as $attribute) {
+            if (!self::arrayHas($json, 'data.attributes.' . $attribute)) {
+                return 'Missing `' . $attribute . '` attribute.';
+            }
+        }
+    }
+
+    private function update(FeedbackElement $feedbackElement, array $json): FeedbackElement
+    {
+        $strAttrs = ['question', 'description'];
+        foreach ($strAttrs as $attribute) {
+            if (self::arrayHas($json, 'data.attributes.' . $attribute)) {
+                $feedbackElement[$attribute] = self::arrayGet($json, 'data.attributes.' . $attribute);
+            }
+        }
+
+        $feedbackElement->store();
+
+        return $feedbackElement;
+    }
+}
diff --git a/lib/classes/JsonApi/Routes/Feedback/FeedbackEntriesCreate.php b/lib/classes/JsonApi/Routes/Feedback/FeedbackEntriesCreate.php
new file mode 100644
index 0000000000000000000000000000000000000000..41efd01eb5a6286e6d52f1d16bf5be957894a83c
--- /dev/null
+++ b/lib/classes/JsonApi/Routes/Feedback/FeedbackEntriesCreate.php
@@ -0,0 +1,115 @@
+<?php
+
+namespace JsonApi\Routes\Feedback;
+
+use FeedbackElement;
+use FeedbackEntry;
+use InvalidArgumentException;
+use User;
+use JsonApi\Errors\AuthorizationFailedException;
+use JsonApi\JsonApiController;
+use JsonApi\Routes\ValidationTrait;
+use JsonApi\Schemas\FeedbackElement as FeedbackElementSchema;
+use JsonApi\Schemas\FeedbackEntry as FeedbackEntrySchema;
+use Psr\Http\Message\ResponseInterface as Response;
+use Psr\Http\Message\ServerRequestInterface as Request;
+
+/**
+ * Create a FeedbackEntry.
+ *
+ * @SuppressWarnings(PHPMD.StaticAccess)
+ */
+class FeedbackEntriesCreate extends JsonApiController
+{
+    use RatingHelper;
+    use ValidationTrait;
+
+    /**
+     * @SuppressWarnings(PHPMD.UnusedFormalParameter)
+     *
+     * @param array $args
+     *
+     * @return Response
+     */
+    public function __invoke(Request $request, Response $response, $args)
+    {
+        $json = $this->validate($request);
+        $element = $this->getElementFromJson($json);
+        $user = $this->getUser($request);
+
+        if (!Authority::canCreateFeedbackEntry($user, $element)) {
+            throw new AuthorizationFailedException();
+        }
+
+        $feedbackEntry = $this->create($user, $json);
+
+        return $this->getCreatedResponse($feedbackEntry);
+    }
+
+    /**
+     * @SuppressWarnings(PHPMD.UnusedFormalParameters)
+     *
+     * @param array $json
+     * @param mixed $data
+     *
+     * @return string|void
+     */
+    protected function validateResourceDocument($json, $data)
+    {
+        if (!self::arrayHas($json, 'data')) {
+            return 'Missing `data` member at document´s top level.';
+        }
+        if (FeedbackEntrySchema::TYPE !== self::arrayGet($json, 'data.type')) {
+            return 'Invalid `type` of document´s `data`.';
+        }
+        if (self::arrayHas($json, 'data.id')) {
+            return 'New document must not have an `id`.';
+        }
+
+        if (!self::arrayHas($json, 'data.relationships.feedback-element')) {
+            return 'Missing `feedback-element` relationship.';
+        }
+        if (!$this->getElementFromJson($json)) {
+            return 'Invalid `feedback-element` relationship.';
+        }
+
+        $required = ['rating'];
+        foreach ($required as $attribute) {
+            if (!self::arrayHas($json, 'data.attributes.' . $attribute)) {
+                return 'Missing `' . $attribute . '` attribute.';
+            }
+        }
+    }
+
+    private function getElementFromJson(array $json): ?FeedbackElement
+    {
+        $relationship = FeedbackEntrySchema::REL_FEEDBACK;
+        if (!$this->validateResourceObject($json, 'data.relationships.' . $relationship, FeedbackElementSchema::TYPE)) {
+            return null;
+        }
+        $resourceId = self::arrayGet($json, 'data.relationships.' . $relationship . '.data.id');
+
+        return FeedbackElement::find($resourceId);
+    }
+
+    private function create(User $user, array $json): FeedbackEntry
+    {
+        $element = $this->getElementFromJson($json);
+        $entry = \FeedbackEntry::build([
+            'feedback_id' => $element->getId(),
+            'user_id' => $user->id,
+            'rating' => $this->getRating($element, (int) self::arrayGet($json, 'data.attributes.rating')),
+        ]);
+
+        if ($element['commentable']) {
+            $entry['comment'] = self::arrayGet($json, 'data.attributes.comment', '');
+        }
+        if ($element['anonymous_entries']) {
+            $entry['anonymous'] = (int) self::arrayGet($json, 'data.attributes.anonymous', '0');
+        }
+
+        $entry->store();
+
+        return $entry;
+    }
+}
diff --git a/lib/classes/JsonApi/Routes/Feedback/FeedbackEntriesDelete.php b/lib/classes/JsonApi/Routes/Feedback/FeedbackEntriesDelete.php
new file mode 100644
index 0000000000000000000000000000000000000000..7afcdb12ea143ee9316375bab7bb5f2c93f6f182
--- /dev/null
+++ b/lib/classes/JsonApi/Routes/Feedback/FeedbackEntriesDelete.php
@@ -0,0 +1,40 @@
+<?php
+
+namespace JsonApi\Routes\Feedback;
+
+use FeedbackEntry;
+use Psr\Http\Message\ServerRequestInterface as Request;
+use Psr\Http\Message\ResponseInterface as Response;
+use JsonApi\Errors\AuthorizationFailedException;
+use JsonApi\Errors\RecordNotFoundException;
+use JsonApi\JsonApiController;
+
+/**
+ * Deletes a feedback entry.
+ *
+ * @SuppressWarnings(PHPMD.StaticAccess)
+ */
+class FeedbackEntriesDelete extends JsonApiController
+{
+    /**
+     * @SuppressWarnings(PHPMD.UnusedFormalParameter)
+     *
+     * @param array $args
+     *
+     * @return Response
+     */
+    public function __invoke(Request $request, Response $response, $args)
+    {
+        $resource = FeedbackEntry::find($args['id']);
+        if (!$resource) {
+            throw new RecordNotFoundException();
+        }
+
+        if (!Authority::canDeleteFeedbackEntry($this->getUser($request), $resource)) {
+            throw new AuthorizationFailedException();
+        }
+        $resource->delete();
+
+        return $this->getCodeResponse(204);
+    }
+}
diff --git a/lib/classes/JsonApi/Routes/Feedback/FeedbackEntriesShow.php b/lib/classes/JsonApi/Routes/Feedback/FeedbackEntriesShow.php
index b591db3e1351e5ea1cf20660d078909686a7e6e4..4a85c69cdbe1a53635f439fb38e787c349ab69d7 100644
--- a/lib/classes/JsonApi/Routes/Feedback/FeedbackEntriesShow.php
+++ b/lib/classes/JsonApi/Routes/Feedback/FeedbackEntriesShow.php
@@ -7,19 +7,27 @@ use Psr\Http\Message\ResponseInterface as Response;
 use JsonApi\Errors\AuthorizationFailedException;
 use JsonApi\Errors\RecordNotFoundException;
 use JsonApi\JsonApiController;
+use JsonApi\Schemas\FeedbackEntry as FeedbackEntrySchema;
 
 /**
  * Displays a certain feedback entry.
  */
 class FeedbackEntriesShow extends JsonApiController
 {
-    protected $allowedIncludePaths = ['author', 'feedback-element'];
+    protected $allowedIncludePaths = [FeedbackEntrySchema::REL_AUTHOR, FeedbackEntrySchema::REL_FEEDBACK];
+
     /**
      * @SuppressWarnings(PHPMD.UnusedFormalParameter)
+     * @SuppressWarnings(PHPMD.StaticAccess)
+     *
+     * @param array $args
+     *
+     * @return Response
      */
     public function __invoke(Request $request, Response $response, $args)
     {
-        if (!$resource = \FeedbackEntry::find($args['id'])) {
+        $resource = \FeedbackEntry::find($args['id']);
+        if (!$resource) {
             throw new RecordNotFoundException();
         }
 
diff --git a/lib/classes/JsonApi/Routes/Feedback/FeedbackEntriesUpdate.php b/lib/classes/JsonApi/Routes/Feedback/FeedbackEntriesUpdate.php
new file mode 100644
index 0000000000000000000000000000000000000000..ff64f57e6d01efbf399d87b23d09661aaa9c0e7c
--- /dev/null
+++ b/lib/classes/JsonApi/Routes/Feedback/FeedbackEntriesUpdate.php
@@ -0,0 +1,95 @@
+<?php
+
+namespace JsonApi\Routes\Feedback;
+
+use FeedbackElement;
+use FeedbackEntry;
+use User;
+use JsonApi\Errors\AuthorizationFailedException;
+use JsonApi\Errors\RecordNotFoundException;
+use JsonApi\JsonApiController;
+use JsonApi\Routes\ValidationTrait;
+use JsonApi\Schemas\FeedbackElement as FeedbackElementSchema;
+use JsonApi\Schemas\FeedbackEntry as FeedbackEntrySchema;
+use Psr\Http\Message\ResponseInterface as Response;
+use Psr\Http\Message\ServerRequestInterface as Request;
+
+/**
+ * Update a FeedbackEntry.
+ *
+ * @SuppressWarnings(PHPMD.StaticAccess)
+ */
+class FeedbackEntriesUpdate extends JsonApiController
+{
+    use RatingHelper;
+    use ValidationTrait;
+
+    /**
+     * @SuppressWarnings(PHPMD.UnusedFormalParameter)
+     *
+     * @param array $args
+     *
+     * @return Response
+     */
+    public function __invoke(Request $request, Response $response, $args)
+    {
+        $resource = \FeedbackEntry::find($args['id']);
+        if (!$resource) {
+            throw new RecordNotFoundException();
+        }
+
+        $json = $this->validate($request);
+        $user = $this->getUser($request);
+
+        if (!Authority::canUpdateFeedbackEntry($user, $resource)) {
+            throw new AuthorizationFailedException();
+        }
+
+        $feedbackEntry = $this->update($resource, $json);
+
+        return $this->getContentResponse($feedbackEntry);
+    }
+
+    /**
+     * @SuppressWarnings(PHPMD.UnusedFormalParameters)
+     *
+     * @param array $json
+     * @param mixed $data
+     *
+     * @return string|void
+     */
+    protected function validateResourceDocument($json, $data)
+    {
+        if (!self::arrayHas($json, 'data')) {
+            return 'Missing `data` member at document´s top level.';
+        }
+        if (FeedbackEntrySchema::TYPE !== self::arrayGet($json, 'data.type')) {
+            return 'Invalid `type` of document´s `data`.';
+        }
+        if (!self::arrayHas($json, 'data.id')) {
+            return 'An existing document must have an `id`.';
+        }
+
+        $required = ['rating'];
+        foreach ($required as $attribute) {
+            if (!self::arrayHas($json, 'data.attributes.' . $attribute)) {
+                return 'Missing `' . $attribute . '` attribute.';
+            }
+        }
+    }
+
+    private function update(FeedbackEntry $feedbackEntry, array $json): FeedbackEntry
+    {
+        $feedbackEntry->rating = $this->getRating(
+            $feedbackEntry->feedback,
+            (int) self::arrayGet($json, 'data.attributes.rating')
+        );
+        if ($feedbackEntry->feedback->commentable && self::arrayHas($json, 'data.attributes.comment')) {
+            $feedbackEntry->comment = self::arrayGet($json, 'data.attributes.comment');
+        }
+        $feedbackEntry->anonymous = (int) self::arrayGet($json, 'data.attributes.anonymous');
+        $feedbackEntry->store();
+
+        return $feedbackEntry;
+    }
+}
diff --git a/lib/classes/JsonApi/Routes/Feedback/RangeTypeAware.php b/lib/classes/JsonApi/Routes/Feedback/RangeTypeAware.php
new file mode 100644
index 0000000000000000000000000000000000000000..88fd1b164b6d7f32d15b2b0ba83f62ea01050cfe
--- /dev/null
+++ b/lib/classes/JsonApi/Routes/Feedback/RangeTypeAware.php
@@ -0,0 +1,20 @@
+<?php
+
+namespace JsonApi\Routes\Feedback;
+
+use FeedbackRange;
+use SimpleORMap;
+
+trait RangeTypeAware
+{
+    protected $possibleRangeTypes = null;
+
+    protected function preparePossibleRangeTypes(): void
+    {
+        foreach (app('json-api-integration-schemas') as $class => $schema) {
+            if (is_subclass_of($class, FeedbackRange::class) && is_subclass_of($class, SimpleORMap::class)) {
+                $this->possibleRangeTypes[$schema::TYPE] = $class;
+            }
+        }
+    }
+}
diff --git a/lib/classes/JsonApi/Routes/Feedback/RatingHelper.php b/lib/classes/JsonApi/Routes/Feedback/RatingHelper.php
new file mode 100644
index 0000000000000000000000000000000000000000..849cba73a7eb33c38b0728a7ab4bcae4e2291a30
--- /dev/null
+++ b/lib/classes/JsonApi/Routes/Feedback/RatingHelper.php
@@ -0,0 +1,31 @@
+<?php
+
+namespace JsonApi\Routes\Feedback;
+
+use FeedbackElement;
+
+trait RatingHelper
+{
+    private function getRating(FeedbackElement $element, int $rating): int
+    {
+        $mode = intval($element['mode']);
+
+        if ($mode === 0) {
+            return 0;
+        }
+
+        if ($rating === 0) {
+            return 1;
+        }
+
+        if ($mode === 1) {
+            return min(5, $rating);
+        }
+
+        if ($mode === 2) {
+            return min(10, $rating);
+        }
+
+        throw new InvalidArgumentException("Invalid mode {$mode}");
+    }
+}
diff --git a/lib/classes/JsonApi/Schemas/Courseware/Instance.php b/lib/classes/JsonApi/Schemas/Courseware/Instance.php
index 7df0cf6d719f1ca5fd33c2fac981bd85234b54d2..114467a61b48143703d4d99009eda4db089f6ed0 100644
--- a/lib/classes/JsonApi/Schemas/Courseware/Instance.php
+++ b/lib/classes/JsonApi/Schemas/Courseware/Instance.php
@@ -40,6 +40,8 @@ class Instance extends SchemaProvider
             'root-layout' => $resource->getRootLayout(),
             'sequential-progression' => $resource->getSequentialProgression(),
             'editing-permission-level' => $resource->getEditingPermissionLevel(),
+            'show-feedback-popup' => $resource->getShowFeedbackPopup(),
+            'show-feedback-in-contentbar' => $resource->getShowFeedbackInContentbar(),
             'certificate-settings' => $resource->getCertificateSettings(),
             'reminder-settings' => $resource->getReminderSettings(),
             'reset-progress-settings' => $resource->getResetProgressSettings(),
diff --git a/lib/classes/JsonApi/Schemas/Courseware/StructuralElement.php b/lib/classes/JsonApi/Schemas/Courseware/StructuralElement.php
index ab1dd0f504717f7d8badd459c7225f16bffeb5b4..e6ccafa2f88979c01ee9c586f1e86f837f6b59e1 100644
--- a/lib/classes/JsonApi/Schemas/Courseware/StructuralElement.php
+++ b/lib/classes/JsonApi/Schemas/Courseware/StructuralElement.php
@@ -24,6 +24,7 @@ class StructuralElement extends SchemaProvider
     const REL_USER = 'user';
     const REL_TASK = 'task';
     const REL_UNIT = 'unit';
+    const REL_FEEDBACKELEMENT = 'feedback-element';
 
     /**
      * {@inheritdoc}
@@ -140,6 +141,12 @@ class StructuralElement extends SchemaProvider
             $this->shouldInclude($context, self::REL_UNIT)
         );
 
+        $relationships = $this->addFeedbackElementRelationship(
+            $relationships,
+            $resource,
+            $this->shouldInclude($context, self::REL_FEEDBACKELEMENT)
+        );
+
         return $relationships;
     }
 
@@ -380,6 +387,22 @@ class StructuralElement extends SchemaProvider
         return $relationships;
     }
 
+    private function addFeedbackElementRelationship(array $relationships, $resource, $includeData): array
+    {
+        $relation = [
+            self::RELATIONSHIP_LINKS => [
+                Link::RELATED => $this->getRelationshipRelatedLink($resource, self::REL_FEEDBACKELEMENT),
+            ],
+        ];
+
+        $feedback = $resource->getFeedbackElement();
+        $relation[self::RELATIONSHIP_DATA] = $feedback;
+        $relationships[self::REL_FEEDBACKELEMENT] = $relation;
+
+
+        return $relationships;
+    }
+
     private static $memo = [];
 
     private function createLinkToCourse($rangeId)
diff --git a/lib/classes/JsonApi/Schemas/Courseware/Unit.php b/lib/classes/JsonApi/Schemas/Courseware/Unit.php
index 84c6ca21e2de4ca5b5e687b4bf0e10b0e3a89022..901f2f0d882dd86d43b1c511ffc2d76d24cee307 100644
--- a/lib/classes/JsonApi/Schemas/Courseware/Unit.php
+++ b/lib/classes/JsonApi/Schemas/Courseware/Unit.php
@@ -13,6 +13,7 @@ class Unit extends SchemaProvider
     const REL_CREATOR= 'creator';
     const REL_RANGE = 'range';
     const REL_STRUCTURAL_ELEMENT = 'structural-element';
+    const REL_FEEDBACK_ELEMENT = 'feedback-element';
 
     /**
      * {@inheritdoc}
@@ -75,6 +76,16 @@ class Unit extends SchemaProvider
             ]
             : [self::RELATIONSHIP_DATA => null];
 
+        $feedback = $resource->getFeedbackElement();
+        $relationships[self::REL_FEEDBACK_ELEMENT] = $feedback
+            ? [
+                self::RELATIONSHIP_LINKS => [
+                    Link::RELATED => $this->createLinkToResource($feedback),
+                ],
+                self::RELATIONSHIP_DATA => $feedback,
+            ]
+            : [self::RELATIONSHIP_DATA => null];
+
         return $relationships;
     }
 }
diff --git a/lib/classes/JsonApi/Schemas/FeedbackElement.php b/lib/classes/JsonApi/Schemas/FeedbackElement.php
index 143bb0c1cf47776d38da16e3d6a4a0f26ebf28e3..7b9e8cb372d5f388f2ed24817f4fd9e01fe6642d 100644
--- a/lib/classes/JsonApi/Schemas/FeedbackElement.php
+++ b/lib/classes/JsonApi/Schemas/FeedbackElement.php
@@ -2,24 +2,28 @@
 
 namespace JsonApi\Schemas;
 
+use JsonApi\Errors\InternalServerError;
 use Neomerx\JsonApi\Contracts\Schema\ContextInterface;
 use Neomerx\JsonApi\Schema\Link;
 
 class FeedbackElement extends SchemaProvider
 {
-    const TYPE = 'feedback-elements';
-    const REL_AUTHOR = 'author';
-    const REL_COURSE = 'course';
-    const REL_ENTRIES = 'entries';
-    const REL_RANGE = 'range';
+    public const TYPE = 'feedback-elements';
+    public const REL_AUTHOR = 'author';
+    public const REL_COURSE = 'course';
+    public const REL_ENTRIES = 'entries';
+    public const REL_RANGE = 'range';
 
 
 
     public function getId($resource): ?string
     {
-        return (int) $resource->id;
+        return (string) $resource->id;
     }
 
+    /**
+     * @SuppressWarnings(PHPMD.UnusedFormalParameter)
+     */
     public function getAttributes($resource, ContextInterface $context): iterable
     {
         $attributes = [
@@ -28,6 +32,9 @@ class FeedbackElement extends SchemaProvider
             'mode' => (int) $resource['mode'],
             'results-visible' => (bool) $resource['results_visible'],
             'is-commentable' => (bool) $resource['commentable'],
+            'anonymous-entries' => (bool) $resource['anonymous_entries'],
+            'average-rating' => $resource->getAverageRating(),
+            'has-entries' => $resource->hasEntries(),
 
             'mkdate' => date('c', $resource['mkdate']),
             'chdate' => date('c', $resource['chdate'])
@@ -76,7 +83,7 @@ class FeedbackElement extends SchemaProvider
         return $relationships;
     }
 
-    private function getAuthorRelationship(array $relationships, \FeedbackElement $resource, $includeData): array
+    private function getAuthorRelationship(array $relationships, \FeedbackElement $resource, bool $includeData): array
     {
         $userId = $resource['user_id'];
         $related = $includeData ? \User::find($userId) : \User::build(['id' => $userId], false);
@@ -90,7 +97,7 @@ class FeedbackElement extends SchemaProvider
         return $relationships;
     }
 
-    private function getCourseRelationship(array $relationships, \FeedbackElement $resource, $includeData): array
+    private function getCourseRelationship(array $relationships, \FeedbackElement $resource, bool $includeData): array
     {
         if ($courseId = $resource['course_id']) {
             $related = $includeData ? \Course::find($courseId) : \Course::build(['id' => $courseId], false);
@@ -119,25 +126,19 @@ class FeedbackElement extends SchemaProvider
 
     private function getRangeRelationship(array $relationships, \FeedbackElement $resource, bool $includeData): array
     {
-        $rangeType = $resource['range_type'];
-        $link = null;
-
+        $range = $resource->getRange();
         try {
-            $link = $this->createLinkToResource($rangeType);
-            if (
-                is_subclass_of($rangeType, \FeedbackRange::class) &&
-                is_subclass_of($rangeType, \SimpleORMap::class)
-            ) {
-                if ($range = $rangeType::find($resource['range_id'])) {
-                    $relationships[self::REL_RANGE] = [
-                        self::RELATIONSHIP_LINKS => [Link::RELATED => $link],
-                        self::RELATIONSHIP_DATA => $range
-                    ];
-                }
-            }
+            $link = $this->createLinkToResource($range);
+            $relationships[self::REL_RANGE] = [
+                self::RELATIONSHIP_LINKS => [Link::RELATED => $link],
+                self::RELATIONSHIP_DATA => $range
+            ];
         } catch (\InvalidArgumentException $e) {
+            // don't show this relation
+        } catch (InternalServerError $ise) {
+            // don't show this relation
         }
 
         return $relationships;
     }
-}
+}
\ No newline at end of file
diff --git a/lib/classes/JsonApi/Schemas/FeedbackEntry.php b/lib/classes/JsonApi/Schemas/FeedbackEntry.php
index b84bf7709e444f7f0acd7dc857c9e26d1794628b..37e275e614a4ee3ac1dec1753699fc8cf04c657c 100644
--- a/lib/classes/JsonApi/Schemas/FeedbackEntry.php
+++ b/lib/classes/JsonApi/Schemas/FeedbackEntry.php
@@ -21,6 +21,7 @@ class FeedbackEntry extends SchemaProvider
         $attributes = [
             'comment' => (string) $resource['comment'],
             'rating' => 0 === $resource->feedback->mode ? null : $resource['rating'],
+            'anonymous' => (bool) $resource['anonymous'],
             'mkdate' => date('c', $resource['mkdate']),
             'chdate' => date('c', $resource['chdate']),
         ];
diff --git a/lib/models/Courseware/Instance.php b/lib/models/Courseware/Instance.php
index 1084ed55d6175ba67e75dde68a15b77885108b30..5f6c3436d866bcdda3b34706984e81d26d5dcab3 100644
--- a/lib/models/Courseware/Instance.php
+++ b/lib/models/Courseware/Instance.php
@@ -174,6 +174,14 @@ class Instance
         \UserConfig::get($user->id)->store('COURSEWARE_FAVORITE_BLOCK_TYPES', $favorites);
     }
 
+
+
+    /* 
+     *
+     *  GENERAL SETTINGS
+     *
+     */
+
     /**
      * Returns which layout is set for root node of this coursware instance
      *
@@ -287,6 +295,43 @@ class Instance
     }
 
 
+    /* 
+     *
+     *  FEEDBACK
+     *
+     */
+
+    public function getShowFeedbackPopup(): bool
+    {
+        $showFeedbackPopup = $this->unit->config['show_feedback_popup'] ?? false;
+
+        return (bool) $showFeedbackPopup;
+    }
+
+    public function setShowFeedbackPopup(bool $showFeedbackPopup): void
+    {
+        $this->unit->config['show_feedback_popup'] = $showFeedbackPopup ? 1 : 0;
+    }
+    
+    public function getShowFeedbackInContentbar(): bool
+    {
+        $showFeedbackInContentbar = $this->unit->config['show_feedback__in_contentbar'] ?? false;
+
+        return (bool) $showFeedbackInContentbar;
+    }
+
+    public function setShowFeedbackInContentbar(bool $showFeedbackInContentbar): void
+    {
+        $this->unit->config['show_feedback__in_contentbar'] = $showFeedbackInContentbar ? 1 : 0;
+    }
+
+    /* 
+     *
+     *  CERTIFICATE
+     *
+     */
+
+
     /**
      * Returns the certificate creation settings.
      *
diff --git a/lib/models/Courseware/StructuralElement.php b/lib/models/Courseware/StructuralElement.php
index a63c73f7f9fbf6bdf2a9e2e47d85864be6032e1e..35a0184563bc06284324dd9d537b17cc9d19967e 100644
--- a/lib/models/Courseware/StructuralElement.php
+++ b/lib/models/Courseware/StructuralElement.php
@@ -53,7 +53,7 @@ use User;
  * @property Task $task has_one Task
  * @property mixed $image additional field
  */
-class StructuralElement extends \SimpleORMap implements \PrivacyObject
+class StructuralElement extends \SimpleORMap implements \PrivacyObject, \FeedbackRange
 {
     protected static function configure($config = [])
     {
@@ -151,6 +151,7 @@ class StructuralElement extends \SimpleORMap implements \PrivacyObject
         if (is_a($image, \FileRef::class)) {
             $image->delete();
         }
+        \FeedbackElement::deleteBySQL('range_id = ? AND range_type = ?', [$this->id, self::class]);
     }
 
     /**
@@ -1195,4 +1196,48 @@ SQL;
             ]);
         }
     }
+
+    public function getRangeCourseId(): string
+    {
+        return $this->range_id;
+    }
+
+    public function getRangeName(): string
+    {
+        return $this->title;
+    }
+
+    public function getRangeIcon($role): string
+    {
+        return \Icon::create('courseware', $role);
+    }
+
+    public function getRangeUrl(): string
+    {
+        $unit = $this->findUnit();
+
+        if ($this->range_type === 'user') {
+            return 'contents/courseware/courseware/' . $unit->id . '#/structural_element/' . $this->id;
+        }
+ 
+        return 'course/courseware/courseware/' . $unit->id . '?cid=' . $this->range_id . '#/structural_element/' . $this->id;
+    }
+
+    public function isRangeAccessible(string $user_id = null): bool
+    {
+        $user =  \User::find($user_id);
+        if ($user) {
+            return $this->canRead($user);
+        }
+
+        return false;
+    }
+
+    public function getFeedbackElement()
+    {
+        return \FeedbackElement::findOneBySQL(
+            'range_id = ? AND range_type = ?',
+            [$this->id, self::class]
+        );
+    }
 }
diff --git a/lib/models/Courseware/Unit.php b/lib/models/Courseware/Unit.php
index bf083281887865b7e9dea4aa43d4ba4518858bd8..2a38a291d40742cd1aa0bcd705a063c8495d7136 100644
--- a/lib/models/Courseware/Unit.php
+++ b/lib/models/Courseware/Unit.php
@@ -31,7 +31,7 @@ use User;
  * @property StructuralElement $structural_element has_one StructuralElement
  */
 
-class Unit extends \SimpleORMap implements \PrivacyObject
+class Unit extends \SimpleORMap implements \PrivacyObject, \FeedbackRange
 {
     protected static function configure($config = [])
     {
@@ -60,10 +60,16 @@ class Unit extends \SimpleORMap implements \PrivacyObject
         ];
 
         $config['registered_callbacks']['after_delete'][] = 'updatePositionsAfterDelete';
+        $config['registered_callbacks']['before_delete'][] = 'cbBeforeDelete';
 
         parent::configure($config);
     }
 
+    public function cbBeforeDelete()
+    {
+        \FeedbackElement::deleteBySQL('range_id = ? AND range_type = ?', [$this->id, self::class]);
+    }
+
     public static function findCoursesUnits(\Course $course): array
     {
         return self::findBySQL('range_id = ? AND range_type = ?', [$course->id, 'course']);
@@ -201,4 +207,46 @@ class Unit extends \SimpleORMap implements \PrivacyObject
 
         return $struct;
     }
+
+    public function getRangeCourseId(): string
+    {
+        return $this->range_id;
+    }
+
+    public function getRangeName(): string
+    {
+        return $this->structural_element->title;
+    }
+
+    public function getRangeIcon($role): string
+    {
+        return \Icon::create('content2', $role);
+    }
+
+    public function getRangeUrl(): string
+    {
+        if ($this->structural_element->range_type === 'user') {
+            return 'contents/courseware/';   
+        }
+
+        return 'course/courseware/' . '?cid=' . $this->range_id;
+    }
+
+    public function isRangeAccessible(string $user_id = null): bool
+    {
+        $user =  \User::find($user_id);
+        if ($user) {
+            return $this->canRead($user);
+        }
+
+        return false;
+    }
+
+    public function getFeedbackElement()
+    {
+        return \FeedbackElement::findOneBySQL(
+            'range_id = ? AND range_type = ?',
+            [$this->id, self::class]
+        );
+    }
 }
diff --git a/lib/models/FeedbackElement.php b/lib/models/FeedbackElement.php
index 63a2186a04659ade881dd5ec4dabca6415234f76..468f146e60be0221da7ac08337cb7d06e367098b 100644
--- a/lib/models/FeedbackElement.php
+++ b/lib/models/FeedbackElement.php
@@ -3,6 +3,7 @@
 /**
  *
  * @author Nils Gehrke <nils.gehrke@uni-goettingen.de>
+ * @author Ron Lucke <lucke@elan-ev.de>
  *
  * The column "range_type" represents the name of a class that implements
  * FeedbackRange.
@@ -17,6 +18,7 @@
  * @property int $mode database column
  * @property int $results_visible database column
  * @property int $commentable database column
+ * @property int $anonymous_entries database column
  * @property int $mkdate database column
  * @property int $chdate database column
  * @property SimpleORMapCollection|FeedbackEntry[] $entries has_many FeedbackEntry
@@ -156,6 +158,22 @@ class FeedbackElement extends SimpleORMap
         }
     }
 
+    public function getAverageRating(): float
+    {
+        $ratings = $this->getRatings();
+
+        if (empty($ratings)) {
+            return 0;
+        }
+
+        return array_sum($ratings) / count($ratings);
+    }
+
+    public function hasEntries(): bool
+    {
+        return count($this->getRatings()) > 0;
+    }
+
     public function getRange()
     {
         return $this->range_type::find($this->range_id);
diff --git a/lib/models/FeedbackEntry.php b/lib/models/FeedbackEntry.php
index 4b4ea7c35642fc0984ad680336571a70134aa88c..9c6ab4178fae8aab66b155982208b3eade312cb9 100644
--- a/lib/models/FeedbackEntry.php
+++ b/lib/models/FeedbackEntry.php
@@ -3,12 +3,14 @@
 /**
  *
  * @author Nils Gehrke <nils.gehrke@uni-goettingen.de>
+ * @author Ron Lucke <lucke@elan-ev.de>
  *
  * @property int $id database column
  * @property int $feedback_id database column
  * @property string $user_id database column
  * @property string $comment database column
  * @property int $rating database column
+ * @property int $anonymous database column
  * @property int $mkdate database column
  * @property int $chdate database column
  * @property FeedbackElement $feedback belongs_to FeedbackElement
diff --git a/lib/modules/CoursewareModule.class.php b/lib/modules/CoursewareModule.class.php
index 6766f8ce387f4aa4cb0230e1b767c998240c588b..8deeda769686fcaed3819ecc271ba6dba626dfdb 100644
--- a/lib/modules/CoursewareModule.class.php
+++ b/lib/modules/CoursewareModule.class.php
@@ -73,7 +73,7 @@ class CoursewareModule extends CorePlugin implements SystemPlugin, StudipModule
         );
         $navigation->addSubNavigation(
             'comments',
-            new Navigation(_('Kommentare und Feedback'), 'dispatch.php/course/courseware/comments_overview?cid=' . $courseId)
+            new Navigation(_('Kommentare und Anmerkungen'), 'dispatch.php/course/courseware/comments_overview?cid=' . $courseId)
         );
 
         return ['courseware' => $navigation];
diff --git a/public/assets/images/icons/black/feedback.svg b/public/assets/images/icons/black/feedback.svg
new file mode 100644
index 0000000000000000000000000000000000000000..00ab1a46020c81bd8a7086f4cfe231a841c0e7d0
--- /dev/null
+++ b/public/assets/images/icons/black/feedback.svg
@@ -0,0 +1 @@
+<svg data-name="Ebene 2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64"><path fill="none" d="M0 0h64v64H0z" data-name="Viewbox 64x64"/><path d="M53.48 7.54H10.53c-3.59 0-6.52 2.94-6.52 6.52v25.45c0 3.59 2.94 6.52 6.52 6.52h8.65l.03 10.5c6.46-.02 11.93-4.5 13.5-10.5h20.78c3.59 0 6.52-2.94 6.52-6.52V14.06c0-3.59-2.94-6.52-6.52-6.52Zm3.02 31.98c0 1.67-1.36 3.02-3.02 3.02H29.67c0 4.55-2.92 8.44-6.98 9.89l-.02-9.89H10.53c-1.67 0-3.02-1.36-3.02-3.02V14.06c0-1.67 1.36-3.02 3.02-3.02h42.95c1.67 0 3.02 1.36 3.02 3.02v25.45Z"/><path d="M37.35 29.74 46 23.45H35.3L32 13.26l-3.3 10.19H18l8.65 6.29-3.3 10.19 8.65-6.3 8.65 6.3-3.3-10.19z"/></svg>
\ No newline at end of file
diff --git a/public/assets/images/icons/blue/feedback.svg b/public/assets/images/icons/blue/feedback.svg
new file mode 100644
index 0000000000000000000000000000000000000000..11c43a4accff1b03cf32b6640687a51c690384f4
--- /dev/null
+++ b/public/assets/images/icons/blue/feedback.svg
@@ -0,0 +1 @@
+<svg data-name="Ebene 2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64"><path fill="none" d="M0 0h64v64H0z" data-name="Viewbox 64x64"/><g fill="#28497c"><path d="M53.48 7.54H10.53c-3.59 0-6.52 2.94-6.52 6.52v25.45c0 3.59 2.94 6.52 6.52 6.52h8.65l.03 10.5c6.46-.02 11.93-4.5 13.5-10.5h20.78c3.59 0 6.52-2.94 6.52-6.52V14.06c0-3.59-2.94-6.52-6.52-6.52Zm3.02 31.98c0 1.67-1.36 3.02-3.02 3.02H29.67c0 4.55-2.92 8.44-6.98 9.89l-.02-9.89H10.53c-1.67 0-3.02-1.36-3.02-3.02V14.06c0-1.67 1.36-3.02 3.02-3.02h42.95c1.67 0 3.02 1.36 3.02 3.02v25.45Z"/><path d="M37.35 29.74 46 23.45H35.3L32 13.26l-3.3 10.19H18l8.65 6.29-3.3 10.19 8.65-6.3 8.65 6.3-3.3-10.19z"/></g></svg>
\ No newline at end of file
diff --git a/public/assets/images/icons/green/feedback.svg b/public/assets/images/icons/green/feedback.svg
new file mode 100644
index 0000000000000000000000000000000000000000..a4e93bce8f05867105faea68fce09aea42a9f062
--- /dev/null
+++ b/public/assets/images/icons/green/feedback.svg
@@ -0,0 +1 @@
+<svg data-name="Ebene 2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64"><path fill="none" d="M0 0h64v64H0z" data-name="Viewbox 64x64"/><g fill="#00962d"><path d="M53.48 7.54H10.53c-3.59 0-6.52 2.94-6.52 6.52v25.45c0 3.59 2.94 6.52 6.52 6.52h8.65l.03 10.5c6.46-.02 11.93-4.5 13.5-10.5h20.78c3.59 0 6.52-2.94 6.52-6.52V14.06c0-3.59-2.94-6.52-6.52-6.52Zm3.02 31.98c0 1.67-1.36 3.02-3.02 3.02H29.67c0 4.55-2.92 8.44-6.98 9.89l-.02-9.89H10.53c-1.67 0-3.02-1.36-3.02-3.02V14.06c0-1.67 1.36-3.02 3.02-3.02h42.95c1.67 0 3.02 1.36 3.02 3.02v25.45Z"/><path d="M37.35 29.74 46 23.45H35.3L32 13.26l-3.3 10.19H18l8.65 6.29-3.3 10.19 8.65-6.3 8.65 6.3-3.3-10.19z"/></g></svg>
\ No newline at end of file
diff --git a/public/assets/images/icons/grey/feedback.svg b/public/assets/images/icons/grey/feedback.svg
new file mode 100644
index 0000000000000000000000000000000000000000..bc66f121d7df95eef4ee80c9805299c9d35cc7df
--- /dev/null
+++ b/public/assets/images/icons/grey/feedback.svg
@@ -0,0 +1 @@
+<svg data-name="Ebene 2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64"><path fill="none" d="M0 0h64v64H0z" data-name="Viewbox 64x64"/><g fill="#6e6e6e"><path d="M53.48 7.54H10.53c-3.59 0-6.52 2.94-6.52 6.52v25.45c0 3.59 2.94 6.52 6.52 6.52h8.65l.03 10.5c6.46-.02 11.93-4.5 13.5-10.5h20.78c3.59 0 6.52-2.94 6.52-6.52V14.06c0-3.59-2.94-6.52-6.52-6.52Zm3.02 31.98c0 1.67-1.36 3.02-3.02 3.02H29.67c0 4.55-2.92 8.44-6.98 9.89l-.02-9.89H10.53c-1.67 0-3.02-1.36-3.02-3.02V14.06c0-1.67 1.36-3.02 3.02-3.02h42.95c1.67 0 3.02 1.36 3.02 3.02v25.45Z"/><path d="M37.35 29.74 46 23.45H35.3L32 13.26l-3.3 10.19H18l8.65 6.29-3.3 10.19 8.65-6.3 8.65 6.3-3.3-10.19z"/></g></svg>
\ No newline at end of file
diff --git a/public/assets/images/icons/red/feedback.svg b/public/assets/images/icons/red/feedback.svg
new file mode 100644
index 0000000000000000000000000000000000000000..b927cad894f931a3a9e030c40dc07b7988096f63
--- /dev/null
+++ b/public/assets/images/icons/red/feedback.svg
@@ -0,0 +1 @@
+<svg data-name="Ebene 2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64"><path fill="none" d="M0 0h64v64H0z" data-name="Viewbox 64x64"/><g fill="#cb1800"><path d="M53.48 7.54H10.53c-3.59 0-6.52 2.94-6.52 6.52v25.45c0 3.59 2.94 6.52 6.52 6.52h8.65l.03 10.5c6.46-.02 11.93-4.5 13.5-10.5h20.78c3.59 0 6.52-2.94 6.52-6.52V14.06c0-3.59-2.94-6.52-6.52-6.52Zm3.02 31.98c0 1.67-1.36 3.02-3.02 3.02H29.67c0 4.55-2.92 8.44-6.98 9.89l-.02-9.89H10.53c-1.67 0-3.02-1.36-3.02-3.02V14.06c0-1.67 1.36-3.02 3.02-3.02h42.95c1.67 0 3.02 1.36 3.02 3.02v25.45Z"/><path d="M37.35 29.74 46 23.45H35.3L32 13.26l-3.3 10.19H18l8.65 6.29-3.3 10.19 8.65-6.3 8.65 6.3-3.3-10.19z"/></g></svg>
\ No newline at end of file
diff --git a/public/assets/images/icons/white/feedback.svg b/public/assets/images/icons/white/feedback.svg
new file mode 100644
index 0000000000000000000000000000000000000000..cf01a79c8b31b678d4eb15809a72c328c4cb91a7
--- /dev/null
+++ b/public/assets/images/icons/white/feedback.svg
@@ -0,0 +1 @@
+<svg data-name="Ebene 2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64"><path fill="none" d="M0 0h64v64H0z" data-name="Viewbox 64x64"/><g fill="#fff"><path d="M53.48 7.54H10.53c-3.59 0-6.52 2.94-6.52 6.52v25.45c0 3.59 2.94 6.52 6.52 6.52h8.65l.03 10.5c6.46-.02 11.93-4.5 13.5-10.5h20.78c3.59 0 6.52-2.94 6.52-6.52V14.06c0-3.59-2.94-6.52-6.52-6.52Zm3.02 31.98c0 1.67-1.36 3.02-3.02 3.02H29.67c0 4.55-2.92 8.44-6.98 9.89l-.02-9.89H10.53c-1.67 0-3.02-1.36-3.02-3.02V14.06c0-1.67 1.36-3.02 3.02-3.02h42.95c1.67 0 3.02 1.36 3.02 3.02v25.45Z"/><path d="M37.35 29.74 46 23.45H35.3L32 13.26l-3.3 10.19H18l8.65 6.29-3.3 10.19 8.65-6.3 8.65 6.3-3.3-10.19z"/></g></svg>
\ No newline at end of file
diff --git a/public/assets/images/icons/yellow/feedback.svg b/public/assets/images/icons/yellow/feedback.svg
new file mode 100644
index 0000000000000000000000000000000000000000..de8e124c753fe3db955e2e60195ccd5e3d8f8a1c
--- /dev/null
+++ b/public/assets/images/icons/yellow/feedback.svg
@@ -0,0 +1 @@
+<svg data-name="Ebene 2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64"><path fill="none" d="M0 0h64v64H0z" data-name="Viewbox 64x64"/><g fill="#ffad00"><path d="M53.48 7.54H10.53c-3.59 0-6.52 2.94-6.52 6.52v25.45c0 3.59 2.94 6.52 6.52 6.52h8.65l.03 10.5c6.46-.02 11.93-4.5 13.5-10.5h20.78c3.59 0 6.52-2.94 6.52-6.52V14.06c0-3.59-2.94-6.52-6.52-6.52Zm3.02 31.98c0 1.67-1.36 3.02-3.02 3.02H29.67c0 4.55-2.92 8.44-6.98 9.89l-.02-9.89H10.53c-1.67 0-3.02-1.36-3.02-3.02V14.06c0-1.67 1.36-3.02 3.02-3.02h42.95c1.67 0 3.02 1.36 3.02 3.02v25.45Z"/><path d="M37.35 29.74 46 23.45H35.3L32 13.26l-3.3 10.19H18l8.65 6.29-3.3 10.19 8.65-6.3 8.65 6.3-3.3-10.19z"/></g></svg>
\ No newline at end of file
diff --git a/resources/assets/stylesheets/scss/courseware.scss b/resources/assets/stylesheets/scss/courseware.scss
index d83a23e0313582e7a6ff190402faa468d87a5b63..dba99a776f833ebf0ff04f2c7a64de39c00dc057 100644
--- a/resources/assets/stylesheets/scss/courseware.scss
+++ b/resources/assets/stylesheets/scss/courseware.scss
@@ -30,4 +30,4 @@
 @import './courseware/layouts/tabs.scss';
 @import './courseware/layouts/talk-bubble.scss';
 @import './courseware/layouts/tile.scss';
-@import './courseware/layouts/tree.scss';
+@import './courseware/layouts/tree.scss';
\ No newline at end of file
diff --git a/resources/assets/stylesheets/scss/courseware/layouts/ribbon.scss b/resources/assets/stylesheets/scss/courseware/layouts/ribbon.scss
index f05a518c9a6ccac70e73c788b56fbb66a2db804f..7f5b5fdb6bb26a693ce28befc8a5c1e9df696799 100644
--- a/resources/assets/stylesheets/scss/courseware/layouts/ribbon.scss
+++ b/resources/assets/stylesheets/scss/courseware/layouts/ribbon.scss
@@ -129,6 +129,10 @@ $consum_ribbon_width: calc(100% - 58px);
                         vertical-align: text-top;
                     }
 
+                    .studip-five-stars {
+                        display: inline-block;
+                    }
+
                     &.cw-ribbon-breadcrumb-item-current {
                         flex-shrink: 1;
                     }
diff --git a/resources/assets/stylesheets/scss/courseware/layouts/tile.scss b/resources/assets/stylesheets/scss/courseware/layouts/tile.scss
index 4fd93bda5bdc8fb135a6c74e809c2deda52cec4d..a5632442a3305989bb3bf78630d0fe9605e55021 100644
--- a/resources/assets/stylesheets/scss/courseware/layouts/tile.scss
+++ b/resources/assets/stylesheets/scss/courseware/layouts/tile.scss
@@ -100,7 +100,7 @@
 
         .progress-wrapper {
             width: 100%;
-            padding: 1em 0;
+            padding: 8px 0;
             border: none;
             background: none;
 
@@ -126,10 +126,9 @@
 
         .description-text-wrapper {
             overflow: hidden;
-            height: 8em;
-            margin-top: 0.5em;
+            height: 10em;
+            margin-top: 4px;
             display: -webkit-box;
-            margin-bottom: 1em;
             -webkit-line-clamp: 7;
             -webkit-box-orient: vertical;
             p {
@@ -139,10 +138,14 @@
 
         footer {
             width: 242px;
+            margin-top: 8px;
             color: var(--white);
             white-space: nowrap;
             overflow: hidden;
             text-overflow: ellipsis;
+            display: flex;
+            align-items: center;
+            justify-content: space-between;
 
             img {
                 vertical-align: text-bottom;
diff --git a/resources/assets/stylesheets/scss/feedback.scss b/resources/assets/stylesheets/scss/feedback.scss
index a9c34e6000dca283754d366a22dca677a9645aeb..5e11197ea48445e63bc40484eb81bf9985ec532f 100644
--- a/resources/assets/stylesheets/scss/feedback.scss
+++ b/resources/assets/stylesheets/scss/feedback.scss
@@ -15,7 +15,8 @@ article.studip.feedback-stream {
             font-weight: normal;
             white-space: nowrap;
         }
-        > img:not(:first-child), > .feedback-star-rating{
+        > img:not(:first-child),
+        > .feedback-star-rating {
             margin-left: 8px;
         }
     }
@@ -25,11 +26,13 @@ article.studip.feedback-stream {
 }
 .feedback-entry-add {
     .rating {
-        label.checked img, label.hover img {
+        label.checked img,
+        label.hover img {
             opacity: 1;
         }
-        label img, label.out img {
-            opacity: .2;
+        label img,
+        label.out img {
+            opacity: 0.2;
         }
         label {
             font-size: 0;
@@ -57,7 +60,8 @@ article.studip.feedback-stream {
                 > span {
                     font-weight: bold;
                 }
-                .avatar-small, span {
+                .avatar-small,
+                span {
                     margin-right: 5px;
                 }
             }
@@ -66,7 +70,7 @@ article.studip.feedback-stream {
             white-space: nowrap;
             font-size: 0;
             .inactive {
-                opacity: .2;
+                opacity: 0.2;
             }
         }
         .date {
@@ -102,3 +106,138 @@ table.feedback {
     background-color: var(--base-color);
     min-width: 20px;
 }
+
+/* * * * * * * * * * * *
+vue feedback components
+* * * * * * * * * * * */
+
+.five-stars-histogram {
+    display: flex;
+    max-width: 420px;
+    flex-wrap: wrap;
+
+    .five-stars-histogram-average {
+        padding: 0 2em 0 0;
+        margin: auto;
+        text-align: center;
+        .fraction {
+            margin: -10px 0;
+            .average {
+                font-size: 3em;
+                font-weight: 700;
+                margin-bottom: -8px;
+            }
+        }
+        .total {
+            font-size: 0.8em;
+            margin-top: -4px;
+        }
+    }
+    .five-stars-histogram-chart {
+        min-width: 260px;
+        span {
+            display: inline-block;
+            width: 2em;
+        }
+        img {
+            vertical-align: text-bottom;
+            margin-left: -2px;
+        }
+        .percentage {
+            display: inline-block;
+            background-color: var(--content-color-10);
+            width: calc(100% - 6em);
+            margin: 2px 10px;
+            .percentage-bar {
+                background-color: var(--yellow);
+                color: transparent;
+                min-width: 0px;
+                padding: 0;
+                margin: 0;
+            }
+        }
+    }
+
+    &.vertical {
+        width: 260px;
+        height: 230px;
+        margin-bottom: 1em;
+        .five-stars-histogram-average {
+            padding: 0;
+        }
+    }
+}
+
+.five-stars-input {
+    margin: 8px auto;
+
+    button {
+        border: none;
+        background: transparent;
+        padding: 0 14px;
+        cursor: pointer;
+    }
+}
+
+.feedback-dialog {
+    display: flex;
+    flex-wrap: wrap;
+
+    .feedback-dialog-content {
+        width: 540px;
+        padding-left: 3em;
+        h2 {
+            display: inline-block;
+            width: calc(100% - 40px);
+            margin: 0;
+        }
+        ul {
+            list-style: none;
+            padding: 0;
+        }
+        .feedback-dialog-content-header {
+            border-bottom: solid thin var(--content-color-40);
+            padding-bottom: 4px;
+        }
+    }
+}
+
+.feedback-element-update,
+.feedback-entry-create {
+    background-color: var(--content-color-10);
+    padding: 1em;
+    margin: 8px 0 16px 0;
+
+    h3 {
+        margin: 0 0 1em 0;
+    }
+    textarea {
+        width: calc(100% - 8px);
+        height: 6em;
+        resize: none;
+    }
+    .button-wrapper {
+        display: flex;
+        flex-direction: row;
+        justify-content: flex-end;
+        button.button {
+            margin: 8px 0 0 5px;
+        }
+    }
+}
+
+.feedback-entry-box {
+    display: flex;
+    margin-bottom: 1em;
+    padding: 8px;
+    border: solid thin var(--content-color-40);
+    .feedback-entry-box-avatar {
+        margin-right: 1em;
+    }
+    .feedback-entry-box-content {
+        flex-grow: 1;
+        h4 {
+            margin: 0 0 2px 0;
+        }
+    }
+}
\ No newline at end of file
diff --git a/resources/vue/components/courseware/CoursewareBlockCommentsOverview.vue b/resources/vue/components/courseware/CoursewareBlockCommentsOverview.vue
index 32ba86e6318c4aa8cc9f9e3a9b620880e682712d..d3fd69c376002214aadb5d25e791ecbcab184bf3 100644
--- a/resources/vue/components/courseware/CoursewareBlockCommentsOverview.vue
+++ b/resources/vue/components/courseware/CoursewareBlockCommentsOverview.vue
@@ -27,7 +27,7 @@
                         <a href="#">{{ $gettext('Kommentare') }}</a>
                     </th>
                     <th class="responsive-hidden" :class="getSortClass('feedback')" @click="sort('feedback')">
-                        <a href="#">{{ $gettext('Feedback') }}</a>
+                        <a href="#">{{ $gettext('Anmerkungen') }}</a>
                     </th>
                     <th class="actions">
                         {{ $gettext('Aktionen') }}
@@ -60,7 +60,7 @@
                             @click.prevent="enableFeedbackDialog(block)"
                             >
                             {{ $gettextInterpolate(
-                                $ngettext('%{length} Feedback', '%{length} Feedbacks', block.feedbacks.length),
+                                $ngettext('%{length} Anmerkung', '%{length} Anmerkungen', block.feedbacks.length),
                                 {length: block.feedbacks.length}
                             ) }}
                         </a>
@@ -81,7 +81,7 @@
             <tbody v-else>
                 <tr class="empty">
                     <td colspan="6">
-                        {{ $gettext('Es wurden keine Kommentare oder Feedback gefunden') }}
+                        {{ $gettext('Es wurden keine Kommentare oder Anmerkungen gefunden') }}
                     </td>
                 </tr>
             </tbody>
@@ -204,7 +204,7 @@ export default {
             let menuItems = [];
             menuItems.push({ id: 1, label: this.$gettext('Kommentare anzeigen'), icon: 'comment2', emit: 'showComments' });
             if (block.element.attributes['can-edit']) {
-                menuItems.push({ id: 2, label: this.$gettext('Feedback anzeigen'), icon: 'comment2', emit: 'showFeedback' });
+                menuItems.push({ id: 2, label: this.$gettext('Anmerkungen anzeigen'), icon: 'comment2', emit: 'showFeedback' });
             }
 
             return menuItems;
diff --git a/resources/vue/components/courseware/CoursewareCommentsOverviewDialog.vue b/resources/vue/components/courseware/CoursewareCommentsOverviewDialog.vue
index 825ee8fb3cc29df63b5c002dc74192ec9451a806..d157f5b42b96b68e94cddfb7bdb5675c2b92c2a1 100644
--- a/resources/vue/components/courseware/CoursewareCommentsOverviewDialog.vue
+++ b/resources/vue/components/courseware/CoursewareCommentsOverviewDialog.vue
@@ -75,7 +75,7 @@ export default {
                 return this.$gettext('Kommentare');
             }
             if (this.isFeedback) {
-                return this.$gettext('Feedback');
+                return this.$gettext('Anmerkungen');
             }
 
             return '';
diff --git a/resources/vue/components/courseware/CoursewareDashboardStudents.vue b/resources/vue/components/courseware/CoursewareDashboardStudents.vue
index ff59669729a601d3373bf5705a56475595de1cc1..bac31a6f0fc93541a1e3d9d55a815bc5c3b2b2f3 100644
--- a/resources/vue/components/courseware/CoursewareDashboardStudents.vue
+++ b/resources/vue/components/courseware/CoursewareDashboardStudents.vue
@@ -24,7 +24,7 @@
                     </th>
                     <th>{{ $gettext('Abgabe') }}</th>
                     <th class="responsive-hidden renewal">{{ $gettext('Verlängerungsanfrage') }}</th>
-                    <th class="responsive-hidden feedback">{{ $gettext('Feedback') }}</th>
+                    <th class="responsive-hidden feedback">{{ $gettext('Anmerkungen') }}</th>
                 </tr>
             </thead>
             <tbody>
@@ -106,15 +106,15 @@
                         <span
                             v-if="feedback"
                             :title="
-                                $gettext('Feedback geschrieben am:') +
+                                $gettext('Anmerkung geschrieben am:') +
                                 ' ' +
                                 getReadableDate(feedback.attributes['chdate'])
                             "
                         >
                             <studip-icon shape="accept" role="status-green" />
-                            {{ $gettext('Feedback gegeben') }}
+                            {{ $gettext('Anmerkung gegeben') }}
                             <studip-icon
-                                :title="$gettext('Feedback bearbeiten')"
+                                :title="$gettext('Anmerkung bearbeiten')"
                                 class="edit"
                                 shape="edit"
                                 role="clickable"
@@ -127,7 +127,7 @@
                             class="button"
                             @click="addFeedback(task)"
                         >
-                            {{ $gettext('Feedback geben') }}
+                            {{ $gettext('Anmerkung geben') }}
                         </button>
                     </td>
                 </tr>
@@ -193,12 +193,12 @@
                     v-if="currentDialogFeedback.attributes.content === ''"
                     mood="pointing"
                     :msgCompanion="
-                        $gettext('Sie haben kein Feedback geschrieben, beim Speichern wird dieses Feedback gelöscht!')
+                        $gettext('Sie haben keine Anmerkungen geschrieben, beim Speichern wird diese Anmerkung gelöscht!')
                     "
                 />
                 <form class="default" @submit.prevent="">
                     <label>
-                        {{ $gettext('Feedback') }}
+                        {{ $gettext('Anmerkung') }}
                         <textarea v-model="currentDialogFeedback.attributes.content" />
                     </label>
                 </form>
@@ -220,7 +220,7 @@
             <template v-slot:dialogContent>
                 <form class="default" @submit.prevent="">
                     <label>
-                        {{ $gettext('Feedback') }}
+                        {{ $gettext('Anmerkung') }}
                         <textarea v-model="currentDialogFeedback.attributes.content" />
                     </label>
                 </form>
@@ -264,12 +264,12 @@ export default {
                     close: this.$gettext('Schließen'),
                 },
                 editFeedbackDialog: {
-                    title: this.$gettext('Feedback zur Aufgabe ändern'),
+                    title: this.$gettext('Anmerkung zur Aufgabe ändern'),
                     confirm: this.$gettext('Speichern'),
                     close: this.$gettext('Schließen'),
                 },
                 addFeedbackDialog: {
-                    title: this.$gettext('Feedback zur Aufgabe geben'),
+                    title: this.$gettext('Anmerkung zur Aufgabe erstellen'),
                     confirm: this.$gettext('Speichern'),
                     close: this.$gettext('Schließen'),
                 },
@@ -350,7 +350,7 @@ export default {
         createFeedback() {
             if (this.currentDialogFeedback.attributes.content === '') {
                 this.companionError({
-                    info: this.$gettext('Bitte schreiben Sie ein Feedback.'),
+                    info: this.$gettext('Bitte schreiben Sie eine Anmerkung.'),
                 });
                 return false;
             }
@@ -373,7 +373,7 @@ export default {
                     taskFeedbackId: this.currentDialogFeedback.id,
                 });
                 this.companionSuccess({
-                    info: this.$gettext('Feedback wurde gelöscht.'),
+                    info: this.$gettext('Anmerkung wurde gelöscht.'),
                 });
             } else {
                 await this.updateTaskFeedback({
@@ -381,7 +381,7 @@ export default {
                     taskFeedbackId: this.currentDialogFeedback.id,
                 });
                 this.companionSuccess({
-                    info: this.$gettext('Feedback wurde gespeichert.'),
+                    info: this.$gettext('Anmerkung wurde gespeichert.'),
                 });
             }
 
diff --git a/resources/vue/components/courseware/CoursewareDashboardTasks.vue b/resources/vue/components/courseware/CoursewareDashboardTasks.vue
index caaea0bb1b6f654782b34cc392a2bd95e2d0649c..6de9c13882b6bdf3aa1a39c84cd65df2c65a4487 100644
--- a/resources/vue/components/courseware/CoursewareDashboardTasks.vue
+++ b/resources/vue/components/courseware/CoursewareDashboardTasks.vue
@@ -12,7 +12,7 @@
                     <th>{{ $gettext('Abgabefrist') }}</th>
                     <th>{{ $gettext('Abgabe') }}</th>
                     <th class="responsive-hidden">{{ $gettext('Verlängerungsanfrage') }}</th>
-                    <th class="responsive-hidden">{{ $gettext('Feedback') }}</th>
+                    <th class="responsive-hidden">{{ $gettext('Anmerkung') }}</th>
                     <th class="actions">{{ $gettext('Aktionen') }}</th>
                 </tr>
             </thead>
@@ -57,7 +57,7 @@
                     <td class="responsive-hidden">
                         <studip-icon
                             v-if="feedback"
-                            :title="$gettext('Feedback anzeigen')"
+                            :title="$gettext('Anmerkung anzeigen')"
                             class="display-feedback"
                             shape="consultation"
                             role="clickable"
@@ -126,7 +126,7 @@ export default {
             currentTaskFeedback: '',
             text: {
                 feedbackDialog: {
-                    title: this.$gettext('Feedback'),
+                    title: this.$gettext('Anmerkung'),
                 },
                 submitDialog: {
                     title: this.$gettext('Aufgabe abgeben'),
diff --git a/resources/vue/components/courseware/CoursewareStructuralElementCommentsOverview.vue b/resources/vue/components/courseware/CoursewareStructuralElementCommentsOverview.vue
index a5ba72e74321077f74035ea8e9ccc6e63cbed674..f9b6f1a4f89b8b379a97857305859a058fd2e4c3 100644
--- a/resources/vue/components/courseware/CoursewareStructuralElementCommentsOverview.vue
+++ b/resources/vue/components/courseware/CoursewareStructuralElementCommentsOverview.vue
@@ -25,7 +25,7 @@
                         <a href="#">{{ $gettext('Kommentare') }}</a>
                     </th>
                     <th class="responsive-hidden" :class="getSortClass('feedback')" @click="sort('feedback')">
-                        <a href="#">{{ $gettext('Feedback') }}</a>
+                        <a href="#">{{ $gettext('Anmerkungen') }}</a>
                     </th>
                     <th class="actions">
                         {{ $gettext('Aktionen') }}
@@ -57,11 +57,11 @@
                         <a
                             v-if="element.attributes['can-edit'] && element.feedbacks.length > 0"
                             href="#"
-                            :title="$gettext('Feedback anzeigen')"
+                            :title="$gettext('Anmerkungen anzeigen')"
                             @click.prevent="enableFeedbackDialog(element)"
                         >
                             {{ $gettextInterpolate(
-                                $ngettext('%{length} Feedback', '%{length} Feedbacks', element.feedbacks.length),
+                                $ngettext('%{length} Anmerkung', '%{length} Anmerkungen', element.feedbacks.length),
                                 {length: element.feedbacks.length}
                             ) }}
                         </a>
@@ -82,7 +82,7 @@
             <tbody v-else>
                 <tr class="empty">
                     <td colspan="6">
-                        {{ $gettext('Es wurden keine Kommentare oder Feedback gefunden') }}
+                        {{ $gettext('Es wurden keine Kommentare oder Anmerkungen gefunden') }}
                     </td>
                 </tr>
             </tbody>
@@ -198,7 +198,7 @@ export default {
             let menuItems = [];
             menuItems.push({ id: 1, label: this.$gettext('Kommentare anzeigen'), icon: 'comment2', emit: 'showComments' });
             if (element.attributes['can-edit']) {
-                menuItems.push({ id: 2, label: this.$gettext('Feedback anzeigen'), icon: 'comment2', emit: 'showFeedback' });
+                menuItems.push({ id: 2, label: this.$gettext('Anmerkungen anzeigen'), icon: 'comment2', emit: 'showFeedback' });
             }
 
             return menuItems;
diff --git a/resources/vue/components/courseware/ShelfApp.vue b/resources/vue/components/courseware/ShelfApp.vue
index d5877550ec44cdd4c929efa1507e8f63b417ac17..173be71bce4430e7a93f342d314a92d8fd3a9d5e 100644
--- a/resources/vue/components/courseware/ShelfApp.vue
+++ b/resources/vue/components/courseware/ShelfApp.vue
@@ -41,6 +41,11 @@ export default {
         CoursewareSharedItems,
         CoursewareCompanionOverlay,
     },
+    data() {
+        return {
+            rate: 0
+        }
+    },
     computed: {
         ...mapGetters({
             showUnitAddDialog: 'showUnitAddDialog',
diff --git a/resources/vue/components/courseware/blocks/CoursewareBlockFeedback.vue b/resources/vue/components/courseware/blocks/CoursewareBlockFeedback.vue
index c13d500de7f905dcfd52ac9ca2628b6c76b92bf1..8bc05c32d3f7ee78ede4226803d7fa916d552b48 100644
--- a/resources/vue/components/courseware/blocks/CoursewareBlockFeedback.vue
+++ b/resources/vue/components/courseware/blocks/CoursewareBlockFeedback.vue
@@ -16,7 +16,7 @@
             </div>
             <courseware-companion-box
                 v-if="!userIsTeacher && feedback.length === 0"
-                :msgCompanion="$gettext('Es wurde noch keine Anmerkungen abgegeben.')"
+                :msgCompanion="$gettext('Es wurde noch keine Anmerkung hinzugefügt.')"
                 mood="pointing"
             />
             <div v-if="userIsTeacher" class="cw-block-feedback-create">
@@ -105,7 +105,7 @@ export default {
             });
         },
         async postFeedback() {
-            this.updateSrMessage(this.$gettext('Feedback gesendet'));
+            this.updateSrMessage(this.$gettext('Anmerkung gesendet'));
             const data = {
                 attributes: {
                     feedback: this.feedbackText,
diff --git a/resources/vue/components/courseware/structural-element/CoursewareFeedbackPopup.vue b/resources/vue/components/courseware/structural-element/CoursewareFeedbackPopup.vue
new file mode 100644
index 0000000000000000000000000000000000000000..687ca3ca7bd2c14f7592a429e6fa835565292b3b
--- /dev/null
+++ b/resources/vue/components/courseware/structural-element/CoursewareFeedbackPopup.vue
@@ -0,0 +1,109 @@
+<template>
+    <studip-dialog
+        height="430"
+        width="600"
+        :title="$gettext('Feedback')"
+        :confirmText="$gettext('Feedback abgeben')"
+        confirmClass="accept"
+        :closeText="$gettext('Schließen')"
+        closeClass="cancel"
+        @close="$emit('close')"
+        @confirm="submitEntry"
+    >
+        <template v-slot:dialogContent>
+            <h2>{{ $gettextInterpolate($gettext('Bewertung für %{title}'),  { title: structuralElement.attributes.title }) }}</h2>
+
+            <div class="feedback-entry-create">
+                <studip-five-stars-input v-model="rating" />
+                <label v-if="isCommentable">
+                    {{ $gettext('Kommentar') }}
+                    <textarea v-model="comment"></textarea>
+                </label>
+                <label v-if="anonymousEntriesEnabled">
+                    <input type="checkbox" v-model="anonymous" />
+                    {{ $gettext('Feedback anonym abgeben') }}
+                </label>
+            </div>
+        </template>
+    </studip-dialog>
+</template>
+<script>
+import StudipFiveStarsInput from '../../feedback/StudipFiveStarsInput.vue';
+
+import { mapActions, mapGetters } from 'vuex';
+
+export default {
+    name: 'courseware-feedback-popup',
+    components: {
+        StudipFiveStarsInput,
+    },
+    props: {
+        feedbackElement: {
+            type: Object,
+            required: true,
+        },
+    },
+    data() {
+        return {
+            rating: 0,
+            comment: '',
+            anonymous: false
+        };
+    },
+    computed: {
+        ...mapGetters({
+            currentUser: 'currentUser',
+            structuralElementById: 'courseware-structural-elements/byId',
+        }),
+        structuralElement() {
+            return this.structuralElementById({ id: this.feedbackElement.relationships.range.data.id });
+        },
+        anonymousEntriesEnabled() {
+            return this.feedbackElement.attributes['anonymous-entries'];
+        },
+        isCommentable() {
+            return this.feedbackElement.attributes['is-commentable'];
+        }
+    },
+    methods: {
+        ...mapActions({
+            createFeedbackEntries: 'feedback-entries/create',
+        }),
+        submitEntry() {
+            let data = {
+                attributes: {
+                    rating: this.rating,
+                },
+                relationships: {
+                    'feedback-element': {
+                        data: {
+                            type: 'feedback-elements',
+                            id: this.feedbackElement.id,
+                        },
+                    },
+                    author: {
+                        data: {
+                            id: this.currentUser.id,
+                            type: 'users',
+                        },
+                    },
+                },
+            };
+            if (this.isCommentable) {
+                data.attributes.comment = this.comment
+            }
+            if (this.anonymousEntriesEnabled) {
+                data.attributes.anonymous = this.anonymous;
+            }
+            this.createFeedbackEntries(data);
+            this.$emit('submit');
+        },
+    },
+};
+</script>
+<style scoped>
+h2 {
+    margin-top: 0;
+    margin-bottom: 20px;
+}
+</style>
diff --git a/resources/vue/components/courseware/structural-element/CoursewareStructuralElement.vue b/resources/vue/components/courseware/structural-element/CoursewareStructuralElement.vue
index 143a03ae258426433887a66fcd2a0da4a890b6bf..34d3145f5d1f3c44f988c72f1270c83c0a9e817a 100644
--- a/resources/vue/components/courseware/structural-element/CoursewareStructuralElement.vue
+++ b/resources/vue/components/courseware/structural-element/CoursewareStructuralElement.vue
@@ -52,6 +52,20 @@
                                         ({{ elementProgress }} %)
                                     </span>
                                 </template>
+                                <studip-five-stars
+                                    v-if="showFeedbackInContentbar && hasFeedbackElement"
+                                    :amount="hasFeedbackAverage ? feedbackAverage : 5"
+                                    :size="16"
+                                    :role="hasFeedbackAverage ? 'status-yellow' : 'inactive'"
+                                    :title="
+                                    hasFeedbackAverage ?
+                                        $gettextInterpolate($gettext('Seite wurde mit %{avg} Sternen bewertet'), {
+                                            avg: feedbackAverage,
+                                        }) :
+                                        $gettext('Seite wurde noch nicht bewertet')
+                                    "
+                                    @click="menuAction('showFeedback')"
+                                />
                             </li>
                         </template>
                         <template #breadcrumbFallback>
@@ -80,6 +94,7 @@
                                 @activateComments="menuAction('activateComments')"
                                 @deactivateComments="menuAction('deactivateComments')"
                                 @showFeedback="menuAction('showFeedback')"
+                                @showFeedbackCreate="menuAction('showFeedbackCreate')"
                             />
                         </template>
                     </courseware-ribbon>
@@ -593,6 +608,27 @@
                 <courseware-structural-element-dialog-export v-if="showExportDialog" :structuralElement="currentElement" />
                 <courseware-structural-element-dialog-export-pdf v-if="showPdfExportDialog" :structuralElement="currentElement" />
                 <courseware-structural-element-dialog-add-chooser v-if="showAddChooserDialog" />
+                <feedback-dialog
+                    v-if="showFeedbackDialog"
+                    :feedbackElementId="parseInt(feedbackElementId)"
+                    :currentUser="currentUser"
+                    @deleted="loadStructuralElement(currentId)"
+                    @close="showStructuralElementFeedbackDialog(false)"
+                />
+                <feedback-create-dialog
+                    v-if="showFeedbackCreateDialog"
+                    :defaultQuestion="$gettext('Bewerten Sie die Seite')"
+                    rangeType="courseware-structural-elements"
+                    :rangeId="currentElement.id"
+                    @created="loadStructuralElement(currentElement.id)"
+                    @close="showStructuralElementFeedbackCreateDialog(false)"
+                />
+                <courseware-feedback-popup
+                    v-if="showRatingPopup"
+                    :feedbackElement="ratingPopupFeedbackElement"
+                    @close="showRatingPopup = false"
+                    @submit="submitFeedback"
+                />
             </div>
             <div v-else>
                 <courseware-companion-box
@@ -618,6 +654,7 @@ import CoursewareRootContent from './CoursewareRootContent.vue';
 
 import CoursewareStructuralElementComments from './CoursewareStructuralElementComments.vue';
 import CoursewareStructuralElementFeedback from './CoursewareStructuralElementFeedback.vue';
+import CoursewareFeedbackPopup from './CoursewareFeedbackPopup.vue';
 import CoursewareStructuralElementDialogAdd from './CoursewareStructuralElementDialogAdd.vue';
 import CoursewareStructuralElementDialogAddChooser from './CoursewareStructuralElementDialogAddChooser.vue';
 import CoursewareStructuralElementDialogCopy from './CoursewareStructuralElementDialogCopy.vue';
@@ -638,6 +675,11 @@ import CoursewareCallToActionBox from '../layouts/CoursewareCallToActionBox.vue'
 import CoursewareDateInput from '../layouts/CoursewareDateInput.vue';
 import StockImageSelector from '../../stock-images/SelectorDialog.vue';
 import StudipDialog from '../../StudipDialog.vue';
+import { FocusTrap } from 'focus-trap-vue';
+import IsoDate from '../layouts/IsoDate.vue';
+import FeedbackDialog from '../../feedback/FeedbackDialog.vue'
+import FeedbackCreateDialog from '../../feedback/FeedbackCreateDialog.vue';
+import StudipFiveStars from '../../feedback/StudipFiveStars.vue';
 import draggable from 'vuedraggable';
 import containerMixin from '@/vue/mixins/courseware/container.js';
 import { mapActions, mapGetters } from 'vuex';
@@ -662,6 +704,12 @@ export default {
         CoursewareWelcomeScreen,
         CoursewareCallToActionBox,
         CoursewareDateInput,
+        CoursewareFeedbackPopup,
+        FeedbackDialog,
+        FeedbackCreateDialog,
+        StudipFiveStars,
+        FocusTrap,
+        IsoDate,
         StockImageSelector,
         StudipDialog,
         draggable,
@@ -729,12 +777,16 @@ export default {
             showStockImageSelector: false,
             selectedStockImage: null,
             displayFeedback: false,
+
+            showRatingPopup: false,
+            ratingPopupFeedbackElement: null
         };
     },
 
     computed: {
         ...mapGetters({
             courseware: 'courseware',
+            rootId: 'rootId',
             context: 'context',
             consumeMode: 'consumeMode',
             containerById: 'courseware-containers/byId',
@@ -762,6 +814,8 @@ export default {
             showSuggestOerDialog: 'showSuggestOerDialog',
             showPublicLinkDialog: 'showStructuralElementPublicLinkDialog',
             showRemoveLockDialog: 'showStructuralElementRemoveLockDialog',
+            showFeedbackDialog: 'showStructuralElementFeedbackDialog',
+            showFeedbackCreateDialog: 'showStructuralElementFeedbackCreateDialog',
             oerCampusEnabled: 'oerCampusEnabled',
             oerEnableSuggestions: 'oerEnableSuggestions',
             licenses: 'licenses',
@@ -785,7 +839,13 @@ export default {
             childrenById: 'courseware-structure/children',
 
             rootLayout: 'rootLayout',
-            toolbarActive: 'toolbarActive'
+            toolbarActive: 'toolbarActive',
+            isFeedbackActivated: 'isFeedbackActivated',
+            canCreateFeedbackElement: 'canCreateFeedbackElement',
+            getFeedbackElementById: 'feedback-elements/byId',
+            feedbackEntries: 'feedback-entries/all',
+
+            currentUser: 'currentUser'
         }),
 
         currentId() {
@@ -1042,22 +1102,59 @@ export default {
             return this.editor?.attributes['formatted-name'] ?? '?';
         },
 
+        feedbackElementId() {
+            return this.currentElement?.relationships?.['feedback-element']?.data?.id;
+        },
+        hasFeedbackElement() {
+            return this.feedbackElementId !== undefined;
+        },
+        showFeedbackInContentbar() {
+            return this.courseware.attributes['show-feedback-in-contentbar'];
+        },
+        feedbackElement() {
+            return this.getFeedbackElementById({ id: this.feedbackElementId });
+        },
+        feedbackAverage() {
+            return this.feedbackElement?.attributes?.['average-rating'] ?? 0;
+        },
+        hasFeedbackAverage() {
+            return this.feedbackAverage > 0;
+        },
+
         menuItems() {
             let menu = [
                 { id: 4, label: this.$gettext('Informationen anzeigen'), icon: 'info', emit: 'showInfo' },
                 { id: 5, label: this.$gettext('Lesezeichen setzen'), icon: 'star', emit: 'setBookmark' },
             ];
+            if (this.isFeedbackActivated) {
+                if (this.canCreateFeedbackElement && !this.hasFeedbackElement) {
+                    menu.push({
+                        id: 6,
+                        label: this.$gettext('Feedback aktivieren'),
+                        icon: 'feedback',
+                        emit: 'showFeedbackCreate',
+                    });
+                }
+                if (this.hasFeedbackElement) {
+                    menu.push({
+                        id: 6,
+                        label: this.$gettext('Feedback anzeigen'),
+                        icon: 'feedback',
+                        emit: 'showFeedback',
+                    });
+                }
+            }
 
             if (this.oerEnableSuggestions && this.inCourse && this.userId !== this.structuralElement.relationships.owner.data.id) {
                 menu.push(
-                    { id: 6, label: this.$gettext('Seite für OER Campus vorschlagen'), icon: 'oer-campus',
+                    { id: 7, label: this.$gettext('Seite für OER Campus vorschlagen'), icon: 'oer-campus',
                         emit: 'showSuggest' }
                 );
             }
 
             if (!document.documentElement.classList.contains('responsive-display')) {
                 menu.push(
-                    { id: 7, label: this.$gettext('Als Vollbild anzeigen'), icon: 'screen-full',
+                    { id: 8, label: this.$gettext('Als Vollbild anzeigen'), icon: 'screen-full',
                         emit: 'activateFullscreen'},
                 );
             }
@@ -1100,11 +1197,11 @@ export default {
                 menu.push({ id: 3, label: this.$gettext('Seite hinzufügen'), icon: 'add', emit: 'addElement' });
             }
             if (this.context.type === 'users') {
-                menu.push({ id: 8, label: this.$gettext('Öffentlichen Link erzeugen'), icon: 'group', emit: 'linkElement' });
+                menu.push({ id: 9, label: this.$gettext('Öffentlichen Link erzeugen'), icon: 'group', emit: 'linkElement' });
             }
             if (this.deletable && this.canEdit && !this.isTask && !this.blocked) {
                 menu.push({
-                    id: 8,
+                    id: 10,
                     label: this.$gettext('Seite löschen'),
                     icon: 'trash',
                     emit: 'deleteCurrentElement',
@@ -1319,9 +1416,9 @@ export default {
             companionInfo: 'companionInfo',
             companionWarning: 'companionWarning',
             companionError: 'companionError',
+            companionSuccess: 'companionSuccess',
             uploadImageForStructuralElement: 'uploadImageForStructuralElement',
             deleteImageForStructuralElement: 'deleteImageForStructuralElement',
-            companionSuccess: 'companionSuccess',
             setStockImageForStructuralElement: 'setStockImageForStructuralElement',
             showElementEditDialog: 'showElementEditDialog',
             showElementAddDialog: 'showElementAddDialog',
@@ -1334,6 +1431,8 @@ export default {
             showElementPublicLinkDialog: 'showElementPublicLinkDialog',
             showElementRemoveLockDialog: 'showElementRemoveLockDialog',
             updateShowSuggestOerDialog: 'updateShowSuggestOerDialog',
+            showStructuralElementFeedbackDialog: 'showStructuralElementFeedbackDialog',
+            showStructuralElementFeedbackCreateDialog: 'showStructuralElementFeedbackCreateDialog',
             updateContainer: 'updateContainer',
             createContainer: 'createContainer',
             sortContainersInStructualElements: 'sortContainersInStructualElements',
@@ -1345,6 +1444,8 @@ export default {
             activateStructuralElementComments: 'activateStructuralElementComments',
             deactivateStructuralElementComments: 'deactivateStructuralElementComments',
             loadRelatedFeedback: 'courseware-structural-element-feedback/loadRelated',
+            createFeedback: 'feedback-elements/create',
+            loadFeedbackElement: 'feedback-elements/loadById',
         }),
 
         initCurrent() {
@@ -1421,7 +1522,10 @@ export default {
                     this.deactivateStructuralElementComments({ element: this.currentElement });
                     break;
                 case 'showFeedback':
-                    this.displayFeedback = true;
+                    this.showStructuralElementFeedbackDialog(true);
+                    break;
+                case 'showFeedbackCreate':
+                    this.showStructuralElementFeedbackCreateDialog(true);
                     break;
             }
         },
@@ -1771,28 +1875,110 @@ export default {
             this.showStockImageSelector = false;
             this.deletingPreviewImage = false;
         },
+        activateFeedback() {
+            const data = {
+                attributes: {
+                    question: this.$gettext('Bewerten Sie das Lernmaterial'),
+                    description: '',
+                    mode: 1,
+                    'results-visible': true,
+                    'is-commentable': true,
+                    'anonymous-entries': true,
+                },
+                relationships: {
+                    range: {
+                        data: {
+                            type: 'courseware-structural-elements',
+                            id: this.currentElement.id,
+                        },
+                    },
+                },
+            };
+            this.createFeedback(data).then(() => {
+                this.loadStructuralElement(this.currentElement.id);
+            });
+        },
+        async showFeedbackPopup(to, from) {
+            let showRatingPopup = false;
+            let ratingPopupFeedbackElement = null;
+            const toId = to.params.id;
+            const toElem = this.structuralElementById({id: toId});
+            if (toId === this.nextElement?.id && toElem.relationships.parent.data.id === this.rootId) {
+                const firstLevelElement = await this.findFirstLevelParent(this.currentElement);
+                const feedbackElementId = firstLevelElement?.relationships?.['feedback-element']?.data?.id;
+                if (feedbackElementId) {
+                    await this.loadFeedbackElement({ id: feedbackElementId, options: { include: 'entries' }});
+                    ratingPopupFeedbackElement = this.getFeedbackElementById({ id: feedbackElementId });
+                    const hasUserEntry = this.feedbackEntries.filter(
+                        (entry) => 
+                            parseInt(entry.relationships?.['feedback-element']?.data?.id) == feedbackElementId &&
+                            this.currentUser.id === entry.relationships?.author?.data?.id
+                    ).length > 0;
+                    
+                    if (this.currentUser.id !== ratingPopupFeedbackElement?.relationships?.author?.data?.id && !hasUserEntry) {
+                        showRatingPopup = true;
+                    } else {
+                        ratingPopupFeedbackElement = null;
+                    }
+                }
+            }
+            this.showRatingPopup = showRatingPopup;
+            this.ratingPopupFeedbackElement = ratingPopupFeedbackElement;
+        },
+        async findFirstLevelParent(elem) {
+            const parentId = elem.relationships.parent.data.id;
+            if (!parentId) {
+                return null;
+            }
+            if (parentId == this.rootId) {
+                await this.loadStructuralElement(elem.id);
+                return this.structuralElementById({ id: elem.id });
+            }
+            const parent = this.structuralElementById({ id: parentId });
+            
+            return this.findFirstLevelParent(parent);
+        },
+        submitFeedback() {
+            this.showRatingPopup = false;
+            this.companionSuccess({ info: this.$gettext('Feedback wurde abgegeben.') });
+        }
     },
     created() {
         this.pluginManager.registerComponentsLocally(this);
     },
 
     watch: {
-        async structuralElement() {
-            this.setCurrentElementId(this.structuralElement.id);
-            this.initCurrent();
-            if (this.isTask) {
-                this.loadTask({
-                    taskId: this.structuralElement.relationships.task.data.id,
-                });
-            }
+        $route: {
+            handler(to, from) {
+                if (this.courseware.attributes['show-feedback-popup']) {
+                    this.showFeedbackPopup(to, from);
+                }
+            },
+            deep: true
+        },
+        structuralElement: {
+            async handler() {
+                this.setCurrentElementId(this.structuralElement.id);
+                this.initCurrent();
+                if (this.isTask) {
+                    this.loadTask({
+                        taskId: this.structuralElement.relationships.task.data.id,
+                    });
+                }
 
-            if (this.isLink) {
-                this.loadStructuralElement(this.structuralElement.attributes['target-id']);
-            }
+                if (this.isLink) {
+                    this.loadStructuralElement(this.structuralElement.attributes['target-id']);
+                }
 
-            if (this.inCourse && this.courseware.attributes['sequential-progression'] && !this.userIsTeacher) {
-               this.loadProgresses();
-            }
+                if (this.inCourse && this.courseware.attributes['sequential-progression'] && !this.userIsTeacher) {
+                    this.loadProgresses();
+                }
+
+                if (this.inCourse) {
+                    this.loadFeedbackElement({ id: this.feedbackElementId });
+                }
+            },
+            deep: true
         },
         containers() {
             this.containerList = this.containers;
@@ -1818,3 +2004,4 @@ export default {
     }),
 };
 </script>
+
diff --git a/resources/vue/components/courseware/structural-element/CoursewareStructuralElementDiscussion.vue b/resources/vue/components/courseware/structural-element/CoursewareStructuralElementDiscussion.vue
index 3428d83af748206eed955e81c1a7c4d2fdd26598..419b8b02d11f05cbe3bc9b8116e6fbdf82c678dc 100644
--- a/resources/vue/components/courseware/structural-element/CoursewareStructuralElementDiscussion.vue
+++ b/resources/vue/components/courseware/structural-element/CoursewareStructuralElementDiscussion.vue
@@ -47,7 +47,7 @@ export default {
             hasFeedback: false,
             text: {
                 comments: this.$gettext('Kommentare zur Seite'),
-                feedback: this.$gettext('Feedback zur Seite')
+                feedback: this.$gettext('Anmerkungen zur Seite')
             }
         }
     },
diff --git a/resources/vue/components/courseware/structural-element/CoursewareStructuralElementFeedback.vue b/resources/vue/components/courseware/structural-element/CoursewareStructuralElementFeedback.vue
index acecf63044862737aede6a5806f2440fbb180580..8d9f95863d98636ecb59d76af8a28708f6c333f9 100644
--- a/resources/vue/components/courseware/structural-element/CoursewareStructuralElementFeedback.vue
+++ b/resources/vue/components/courseware/structural-element/CoursewareStructuralElementFeedback.vue
@@ -14,10 +14,10 @@
             />
         </div>
         <courseware-companion-box
-            v-if="!userIsTeacher && feedback.length === 0"
-            :msgCompanion="$gettext('Es wurde noch keine Anmerkungen abgegeben.')"
-            mood="pointing"
-        />
+                v-if="!userIsTeacher && feedback.length === 0"
+                :msgCompanion="$gettext('Es wurde noch keine Anmerkung hinzugefügt.')"
+                mood="pointing"
+            />
         <div v-if="userIsTeacher" class="cw-structural-element-feedback-create">
             <textarea v-model="feedbackText" :placeholder="placeHolder" spellcheck="true"></textarea>
             <button class="button" @click="postFeedback">
@@ -106,7 +106,7 @@ export default {
             });
         },
         async postFeedback() {
-            this.updateSrMessage(this.$gettext('Anmerkung gesendet'));
+            this.updateSrMessage(this.$gettext('Anmerkung hinzugefügt'));
             const data = {
                 attributes: {
                     feedback: this.feedbackText,
@@ -135,5 +135,12 @@ export default {
     updated() {
         this.$refs.feedbacks.scrollTop = this.$refs.feedbacks.scrollHeight;
     },
+    watch: {
+        feedback() {
+            if (this.feedback && this.feedback.length > 0) {
+                this.$emit('hasFeedback');
+            }
+        }
+    }
 };
 </script>
diff --git a/resources/vue/components/courseware/unit/CoursewareShelfDialogTopics.vue b/resources/vue/components/courseware/unit/CoursewareShelfDialogTopics.vue
index eb6e88895fc34837251dd8d0a64c008baef1f09d..70745e815807a231311c3ef01755ad3cef835021 100644
--- a/resources/vue/components/courseware/unit/CoursewareShelfDialogTopics.vue
+++ b/resources/vue/components/courseware/unit/CoursewareShelfDialogTopics.vue
@@ -1,6 +1,6 @@
 <template>
     <studip-dialog
-        :title="$gettext('Lernmaterial aus Ablaufplan Themen erstellen')"
+        :title="$gettext('Lernmaterial aus Ablaufplan-Themen erstellen')"
         :confirmText="$gettext('Erstellen')"
         confirmClass="accept"
         :closeText="$gettext('Abbrechen')"
diff --git a/resources/vue/components/courseware/unit/CoursewareUnitItem.vue b/resources/vue/components/courseware/unit/CoursewareUnitItem.vue
index 6c0ec6e7fbbd7e399c7859afc5ca14a5377dae93..b1e82bfecc0945fdf90818c5228749e3a83360ef 100644
--- a/resources/vue/components/courseware/unit/CoursewareUnitItem.vue
+++ b/resources/vue/components/courseware/unit/CoursewareUnitItem.vue
@@ -25,22 +25,46 @@
                     @showSettings="openSettingsDialog"
                     @showLayout="openLayoutDialog"
                     @copyUnit="copy"
+                    @showFeedbackCreate="openFeedbackCreateDialog"
+                    @showFeedback="openFeedbackDialog"
                 />
             </template>
             <template #description>
                 {{ description }}
             </template>
-            <template #footer v-if="certificate">
-                <studip-icon shape="medal" :size="32" role="info_alt"></studip-icon>
+            <template #footer>
+                <template v-if="hasFeedbackElement">
+                    <studip-five-stars
+                        v-if="hasFeedbackEntries"
+                        :amount="feedbackAverage"
+                        :size="16"
+                        :title="
+                            $gettextInterpolate($gettext('Lernmaterial wurde mit %{avg} Sternen bewertet'), {
+                                avg: feedbackAverage,
+                            })
+                        "
+                    />
+                    <studip-five-stars
+                        v-else
+                        :amount="5"
+                        :size="16"
+                        role="inactive"
+                        :title="$gettext('Lernmaterial wurde noch nicht bewertet')"
+                    />
+                </template>
+                <template v-if="certificate">
+                    <studip-icon shape="medal" :size="16" role="info_alt" />
+                </template>
             </template>
         </courseware-tile>
         <studip-dialog
             v-if="showDeleteDialog"
             :title="$gettext('Lernmaterial löschen')"
-            :question="$gettextInterpolate(
-                        $gettext('Möchten Sie das Lernmaterial %{ unitTitle } wirklich löschen?'),
-                        { unitTitle: title }
-                    )"
+            :question="
+                $gettextInterpolate($gettext('Möchten Sie das Lernmaterial %{ unitTitle } wirklich löschen?'), {
+                    unitTitle: title,
+                })
+            "
             height="200"
             @confirm="executeDelete"
             @close="closeDeleteDialog"
@@ -56,13 +80,37 @@
             @close="closeProgressDialog"
         >
             <template v-slot:dialogContent>
-                <courseware-unit-progress :progressData="progresses" :unitId="unit.id" :rootId="parseInt(unitElement.id)"/>
+                <courseware-unit-progress
+                    :progressData="progresses"
+                    :unitId="unit.id"
+                    :rootId="parseInt(unitElement.id)"
+                />
             </template>
         </studip-dialog>
 
         <courseware-unit-item-dialog-export v-if="showExportDialog" :unit="unit" @close="showExportDialog = false" />
-        <courseware-unit-item-dialog-settings v-if="showSettingsDialog" :unit="unit" @close="closeSettingsDialog"/>
-        <courseware-unit-item-dialog-layout v-if="showLayoutDialog" :unit="unit" :unitElement="unitElement" @close="closeLayoutDialog"/>
+        <courseware-unit-item-dialog-settings v-if="showSettingsDialog" :unit="unit" @close="closeSettingsDialog" />
+        <courseware-unit-item-dialog-layout
+            v-if="showLayoutDialog"
+            :unit="unit"
+            :unitElement="unitElement"
+            @close="closeLayoutDialog"
+        />
+        <feedback-dialog
+            v-if="showFeedbackDialog"
+            :feedbackElementId="parseInt(feedbackElementId)"
+            :currentUser="currentUser"
+            @deleted="loadUnit({ id: unit.id })"
+            @close="closeFeedbackDialog"
+        />
+        <feedback-create-dialog
+            v-if="showFeedbackCreateDialog"
+            :defaultQuestion="$gettext('Bewerten Sie das Lernmaterial')"
+            rangeType="courseware-units"
+            :rangeId="unit.id"
+            @created="loadUnit({ id: unit.id })"
+            @close="closeFeedbackCreateDialog"
+        />
     </li>
 </template>
 
@@ -72,8 +120,12 @@ import CoursewareUnitItemDialogExport from './CoursewareUnitItemDialogExport.vue
 import CoursewareUnitItemDialogSettings from './CoursewareUnitItemDialogSettings.vue';
 import CoursewareUnitItemDialogLayout from './CoursewareUnitItemDialogLayout.vue';
 import CoursewareUnitProgress from './CoursewareUnitProgress.vue';
+import FeedbackDialog from '../../feedback/FeedbackDialog.vue';
+import FeedbackCreateDialog from '../../feedback/FeedbackCreateDialog.vue';
+import StudipFiveStars from '../../feedback/StudipFiveStars.vue';
 import axios from 'axios';
 
+
 import { mapActions, mapGetters } from 'vuex';
 
 export default {
@@ -84,6 +136,9 @@ export default {
         CoursewareUnitItemDialogLayout,
         CoursewareUnitItemDialogSettings,
         CoursewareUnitProgress,
+        FeedbackDialog,
+        FeedbackCreateDialog,
+        StudipFiveStars,
     },
     props: {
         unit: Object,
@@ -100,22 +155,49 @@ export default {
             showProgressDialog: false,
             showLayoutDialog: false,
             progresses: null,
-            certificate: null
-        }
+            certificate: null,
+            showFeedbackDialog: false,
+            showFeedbackCreateDialog: false,
+        };
     },
     computed: {
         ...mapGetters({
             context: 'context',
             structuralElementById: 'courseware-structural-elements/byId',
-            userIsTeacher: 'userIsTeacher'
+            userIsTeacher: 'userIsTeacher',
+            canCreateFeedbackElement: 'canCreateFeedbackElement',
+            isFeedbackActivated: 'isFeedbackActivated',
+            feedbackElementById: 'feedback-elements/byId',
+            currentUser: 'currentUser',
         }),
         menuItems() {
             let menu = [];
             if (this.inCourseContext) {
                 menu.push({ id: 1, label: this.$gettext('Fortschritt'), icon: 'progress', emit: 'showProgress' });
+                if (this.userIsTeacher) {
+                    menu.push({ id: 2, label: this.$gettext('Einstellungen'), icon: 'settings', emit: 'showSettings' });
+                }
+                if (this.isFeedbackActivated) {
+                    if (this.canCreateFeedbackElement && !this.hasFeedbackElement) {
+                        menu.push({
+                            id: 6,
+                            label: this.$gettext('Feedback aktivieren'),
+                            icon: 'feedback',
+                            emit: 'showFeedbackCreate',
+                        });
+                    }
+                    if (this.hasFeedbackElement) {
+                        menu.push({
+                            id: 6,
+                            label: this.$gettext('Feedback anzeigen'),
+                            icon: 'feedback',
+                            emit: 'showFeedback',
+                        });
+                    }
+                }
                 if (this.certificate) {
                     menu.push({
-                        id: 2,
+                        id: 3,
                         label: this.$gettext('Zertifikat'),
                         icon: 'medal',
                         url: STUDIP.URLHelper.getURL('sendfile.php', {
@@ -126,36 +208,54 @@ export default {
                     });
                 }
             }
-            if(this.userIsTeacher && this.inCourseContext) {
-                menu.push({ id: 2, label: this.$gettext('Einstellungen'), icon: 'settings', emit: 'showSettings' });
-            }
-            if(this.userIsTeacher || !this.inCourseContext) {
+
+            if (this.userIsTeacher || !this.inCourseContext) {
                 menu.push({ id: 4, label: this.$gettext('Darstellung'), icon: 'colorpicker', emit: 'showLayout' });
-                menu.push({ id: 4, label: this.$gettext('Duplizieren'), icon: 'copy', emit: 'copyUnit' });
-                menu.push({ id: 5, label: this.$gettext('Exportieren'), icon: 'export', emit: 'showExport' });
-                menu.push({ id: 6, label: this.$gettext('Löschen'), icon: 'trash', emit: 'showDelete' });
+                menu.push({ id: 5, label: this.$gettext('Duplizieren'), icon: 'copy', emit: 'copyUnit' });
+                menu.push({ id: 7, label: this.$gettext('Exportieren'), icon: 'export', emit: 'showExport' });
+                menu.push({ id: 8, label: this.$gettext('Löschen'), icon: 'trash', emit: 'showDelete' });
             }
 
+            menu.sort((a, b) => {
+                return a.id - b.id;
+            });
             return menu;
         },
         unitElement() {
-            return this.structuralElementById({id: this.unit.relationships['structural-element'].data.id}) ?? null;
+            return this.structuralElementById({ id: this.unit.relationships['structural-element'].data.id }) ?? null;
+        },
+        feedbackElementId() {
+            return this.unit.relationships['feedback-element']?.data?.id;
+        },
+        hasFeedbackElement() {
+            return this.feedbackElementId !== undefined;
+        },
+        hasFeedbackEntries() {
+            return this.feedbackElement?.attributes?.['has-entries'] ?? false;
+        },
+        feedbackAverage() {
+            return this.feedbackElement?.attributes?.['average-rating'] ?? 0;
+        },
+        feedbackElement() {
+            return this.feedbackElementById({ id: this.feedbackElementId });
         },
         color() {
             return this.unitElement?.attributes?.payload?.color ?? 'studip-blue';
         },
         title() {
-            return  this.unitElement?.attributes?.title ?? '';
+            return this.unitElement?.attributes?.title ?? '';
         },
         description() {
-            return  this.unitElement?.attributes?.payload?.description ?? '';
+            return this.unitElement?.attributes?.payload?.description ?? '';
         },
         imageUrl() {
             return this.unitElement?.relationships?.image?.meta?.['download-url'] ?? '';
         },
         url() {
             if (this.inCourseContext) {
-                return STUDIP.URLHelper.getURL('dispatch.php/course/courseware/courseware/' + this.unit.id , { cid: this.context.id });
+                return STUDIP.URLHelper.getURL('dispatch.php/course/courseware/courseware/' + this.unit.id, {
+                    cid: this.context.id,
+                });
             } else {
                 return STUDIP.URLHelper.getURL('dispatch.php/contents/courseware/courseware/' + this.unit.id);
             }
@@ -168,11 +268,11 @@ export default {
         },
         inCourseContext() {
             return this.context.type === 'courses';
-        }
+        },
     },
     async mounted() {
         if (this.inCourseContext) {
-            this.progresses = await this.loadUnitProgresses({unitId: this.unit.id});
+            this.progresses = await this.loadUnitProgresses({ unitId: this.unit.id });
             this.checkCertificate();
         }
     },
@@ -180,8 +280,11 @@ export default {
         ...mapActions({
             deleteUnit: 'deleteUnit',
             loadUnitProgresses: 'loadUnitProgresses',
+            loadUnit: 'courseware-units/loadById',
             copyUnit: 'copyUnit',
-            companionSuccess: 'companionSuccess'
+            companionSuccess: 'companionSuccess',
+            createFeedback: 'feedback-elements/create',
+            loadFeedbackElement: 'feedback-elements/loadById',
         }),
         async checkCertificate() {
             if (this.getStudipConfig('COURSEWARE_CERTIFICATES_ENABLE')) {
@@ -193,7 +296,7 @@ export default {
             }
         },
         executeDelete() {
-            this.deleteUnit({id: this.unit.id});
+            this.deleteUnit({ id: this.unit.id });
         },
         openDeleteDialog() {
             this.showDeleteDialog = true;
@@ -206,7 +309,7 @@ export default {
         },
         async openProgressDialog() {
             this.showProgressDialog = true;
-            this.progresses = await this.loadUnitProgresses({unitId: this.unit.id});
+            this.progresses = await this.loadUnitProgresses({ unitId: this.unit.id });
         },
         closeProgressDialog() {
             this.showProgressDialog = false;
@@ -223,8 +326,23 @@ export default {
         closeLayoutDialog() {
             this.showLayoutDialog = false;
         },
+        openFeedbackCreateDialog() {
+            this.showFeedbackCreateDialog = true;
+        },
+        closeFeedbackCreateDialog() {
+            this.showFeedbackCreateDialog = false;
+        },
+        openFeedbackDialog() {
+            if (this.feedbackElementId) {
+                this.showFeedbackDialog = true;
+            }
+        },
+        closeFeedbackDialog() {
+            this.showFeedbackDialog = false;
+            this.loadFeedbackElement({ id: this.feedbackElementId });
+        },
         async copy() {
-            await this.copyUnit({unitId: this.unit.id, modified: null});
+            await this.copyUnit({ unitId: this.unit.id, modified: null });
             this.companionSuccess({ info: this.$gettext('Lernmaterial kopiert.') });
         },
     }
diff --git a/resources/vue/components/courseware/unit/CoursewareUnitItemDialogSettings.vue b/resources/vue/components/courseware/unit/CoursewareUnitItemDialogSettings.vue
index ba2eaf4072805fd5893a597b8985cdde7922d0ad..d3534ee9670706c2670ba497aed32f754031b395 100644
--- a/resources/vue/components/courseware/unit/CoursewareUnitItemDialogSettings.vue
+++ b/resources/vue/components/courseware/unit/CoursewareUnitItemDialogSettings.vue
@@ -29,6 +29,23 @@
                         </select>
                     </label>
                 </fieldset>
+                <fieldset>
+                    <legend>{{ $gettext('Feedback') }}</legend>
+                    <label>
+                        {{ $gettext('Am Ende eines Kapitels einen Dialog für die Bewertung anzeigen') }}
+                        <select class="size-s" v-model="currentShowFeedbackPopup">
+                            <option value="0">{{ $gettext('Nein') }}</option>
+                            <option value="1">{{ $gettext('Ja') }}</option>
+                        </select>
+                        <label>
+                        {{ $gettext('Bewertung auf der Seite anzeigen') }}
+                        <select class="size-s" v-model="currentShowFeedbackInContentbar">
+                            <option value="0">{{ $gettext('Nein') }}</option>
+                            <option value="1">{{ $gettext('Ja') }}</option>
+                        </select>
+                    </label>
+                    </label>
+                </fieldset>
                 <fieldset v-if="certificatesRemindersEnabled">
                     <legend>{{ $gettext('Zertifikate') }}</legend>
                     <label>
@@ -211,6 +228,8 @@ export default {
             currentRootLayout: 'default',
             currentPermissionLevel: '',
             currentProgression: 0,
+            currentShowFeedbackPopup: 0,
+            currentShowFeedbackInContentbar: 1,
             makeCert: false,
             certThreshold: 0,
             certImage: '',
@@ -263,6 +282,8 @@ export default {
             this.currentRootLayout = this.currentInstance.attributes['root-layout'];
             this.currentPermissionLevel = this.currentInstance.attributes['editing-permission-level'];
             this.currentProgression = this.currentInstance.attributes['sequential-progression'] ? '1' : '0';
+            this.currentShowFeedbackPopup = this.currentInstance.attributes['show-feedback-popup'] ? '1' : '0';
+            this.currentShowFeedbackInContentbar = this.currentInstance.attributes['show-feedback-in-contentbar'] ? '1' : '0';
             this.certSettings = this.currentInstance.attributes['certificate-settings'];
             this.makeCert = typeof(this.certSettings) === 'object' &&
                 Object.keys(this.certSettings).length > 0;
@@ -290,6 +311,8 @@ export default {
             this.currentInstance.attributes['root-layout'] = this.currentRootLayout;
             this.currentInstance.attributes['editing-permission-level'] = this.currentPermissionLevel;
             this.currentInstance.attributes['sequential-progression'] = this.currentProgression;
+            this.currentInstance.attributes['show-feedback-popup'] = this.currentShowFeedbackPopup;
+            this.currentInstance.attributes['show-feedback-in-contentbar'] = this.currentShowFeedbackInContentbar;
             this.currentInstance.attributes['certificate-settings'] = this.generateCertificateSettings();
             this.currentInstance.attributes['reminder-settings'] = this.generateReminderSettings();
             this.currentInstance.attributes['reset-progress-settings'] = this.generateResetProgressSettings();
diff --git a/resources/vue/components/courseware/widgets/CoursewareActivitiesWidgetFilterType.vue b/resources/vue/components/courseware/widgets/CoursewareActivitiesWidgetFilterType.vue
index eb2c05f5184ff7645e41ab90d1d49826da1f09a6..09ced68f6c88661c9092b5a5a2ada9761b76e9a6 100644
--- a/resources/vue/components/courseware/widgets/CoursewareActivitiesWidgetFilterType.vue
+++ b/resources/vue/components/courseware/widgets/CoursewareActivitiesWidgetFilterType.vue
@@ -14,7 +14,7 @@
                             {{ $gettext('Erstellt') }}
                         </option>
                         <option value="answered">
-                            {{ $gettext('Feedback') }}
+                            {{ $gettext('Angemerkt') }}
                         </option>
                         <option value="interacted">
                             {{ $gettext('Kommentiert') }}
diff --git a/resources/vue/components/feedback/FeedbackCreateDialog.vue b/resources/vue/components/feedback/FeedbackCreateDialog.vue
new file mode 100644
index 0000000000000000000000000000000000000000..204254c395aef2129e149e02c2fc2d72de43f243
--- /dev/null
+++ b/resources/vue/components/feedback/FeedbackCreateDialog.vue
@@ -0,0 +1,117 @@
+<template>
+    <div>
+        <studip-dialog
+            :title="$gettext('Feedback erstellen')"
+            :confirmText="$gettext('Erstellen')"
+            :closeText="$gettext('Schließen')"
+            closeClass="cancel"
+            confirmClass="accept"
+            height="420"
+            width="500"
+            @confirm="createFeedback"
+            @close="$emit('close')"
+        >
+            <template v-slot:dialogContent>
+                <form class="default" @submit.prevent="">
+                    <label>
+                        {{ $gettext('Frage') }}
+                        <input type="text" v-model="question" >
+                    </label>
+                    <label>
+                        {{ $gettext('Beschreibung') }}
+                        <textarea v-model="description"></textarea>
+                    </label>
+                    <label>
+                        <input type="checkbox" v-model="anonymous" >
+                        {{ $gettext('Feedback kann anonym abgegeben werden') }}
+                    </label>
+                    <label>
+                        <input type="checkbox" v-model="commentable" >
+                        {{ $gettext('Abgegebenes Feedback kann einen Kommentar beinhalten') }}
+                    </label>
+
+                </form>
+            </template>
+        </studip-dialog>
+    </div>
+</template>
+
+<script>
+import { mapActions, mapGetters } from 'vuex';
+
+export default {
+    name: 'feedback-create-dialog',
+    props: {
+        defaultQuestion: {
+            type: String,
+            default: ''
+        },
+        defaultDescription: {
+            type: String,
+            default: ''
+        },
+        defaultCommentable: {
+            type: Boolean,
+            default: true
+        },
+        defaultAnonymous: {
+            type: Boolean,
+            default: false
+        },
+        rangeType: {
+            type: String,
+            required: true
+        },
+        rangeId: {
+            type: String,
+            required: true
+        }
+    },
+    data() {
+        return {
+            question: '',
+            description: '',
+            commentable: true,
+            anonymous: false
+        }
+    },
+    methods: {
+        ...mapActions({
+            createFeedbackElement: 'feedback-elements/create',
+        }),
+        createFeedback() {
+            const data = {
+                attributes: {
+                    question: this.question,
+                    description:this.description,
+                    mode: 1,
+                    'results-visible': true,
+                    'is-commentable': this.commentable,
+                    'anonymous-entries': this.anonymous,
+                },
+                relationships: {
+                    range: {
+                        data: {
+                            type: this.rangeType,
+                            id: this.rangeId,
+                        },
+                    },
+                },
+            };
+            this.createFeedbackElement(data).then(() => {
+                this.$emit('created');
+                this.$emit('close');
+            });
+        },
+        initData() {
+            this.question = this.defaultQuestion;
+            this.description = this.defaultDescription;
+            this.commentable = this.defaultCommentable;
+            this.anonymous = this.defaultAnonymous;
+        }
+    },
+    mounted() {
+        this.initData();
+    }
+};
+</script>
diff --git a/resources/vue/components/feedback/FeedbackDialog.vue b/resources/vue/components/feedback/FeedbackDialog.vue
new file mode 100644
index 0000000000000000000000000000000000000000..e432b1b4354d32e68d3bdc6dc25950e311db4dbf
--- /dev/null
+++ b/resources/vue/components/feedback/FeedbackDialog.vue
@@ -0,0 +1,223 @@
+<template>
+    <div>
+        <studip-dialog
+            :title="$gettext('Feedback')"
+            :closeText="$gettext('Schließen')"
+            closeClass="cancel"
+            :height="height"
+            :width="width"
+            @close="$emit('close')"
+        >
+            <template v-slot:dialogContent>
+                <div v-if="!loadingFeedbackElement" class="feedback-dialog">
+                    <feedback-five-stars-histogram :entries="entries" :vertical="true" />
+
+                    <div class="feedback-dialog-content">
+                        <template v-if="!editElement">
+                            <div class="feedback-dialog-content-header">
+                                <h2>
+                                    {{ feedbackElement?.attributes?.question }}
+                                </h2>
+                                <button class="as-link" @click="editElement = true">
+                                    <studip-icon shape="edit" />
+                                </button>
+                                <button class="as-link" @click="showDeleteFeedbackDialog = true">
+                                    <studip-icon shape="trash" />
+                                </button>
+                            </div>
+                            <div v-if="hasDescription">
+                                <h3>{{ $gettext('Beschreibung') }}</h3>
+                                <p v-html="description"></p>
+                            </div>
+                        </template>
+                        <feedback-element-update
+                            v-else
+                            :feedbackElementId="feedbackElementId"
+                            @cancel="editElement = false"
+                            @submit="updateFeedbackElement"
+                        />
+
+                        <template v-if="!currentUserIsAuthor">
+                            <h3>{{ $gettext('Meine Bewertung') }}</h3>
+                            <feedback-entry-create
+                                v-if="!hasCurrentUserEntry || editEntry"
+                                :feedbackElement="feedbackElement"
+                                :entry="currentUserEntry[0]"
+                                :currentUser="currentUser"
+                                @submit="editEntry = false"
+                                @cancel="editEntry = false"
+                            />
+                            <feedback-entry-box
+                                v-else
+                                class="current-user-entry"
+                                :entry="currentUserEntry[0]"
+                                :canEdit="true"
+                                :canDelete="true"
+                                @edit="editEntry = true"
+                                @delete="showDeleteEntry"
+                            />
+                        </template>
+
+                        <h3>{{ $gettext('Bewertungen') }}</h3>
+                        <ul>
+                            <li v-for="entry in otherUserEntries" :key="entry.id">
+                                <feedback-entry-box
+                                    :entry="entry"
+                                    :canDelete="canEditFeedbackElement"
+                                    @delete="showDeleteEntry"
+                                />
+                            </li>
+                        </ul>
+                        <p v-if="entries.length === 0">
+                            {{ $gettext('Es wurden noch keine Bewertungen abgegeben.') }}
+                        </p>
+                        <p v-if="otherUserEntries.length === 0 && entries.length > 0">
+                            {{ $gettext('Es wurden noch keine weiteren Bewertungen abgegeben.') }}
+                        </p>
+                    </div>
+                    <studip-dialog
+                        v-if="showDeleteEntryDialog"
+                        :title="$gettext('Feedback-Eintrag löschen')"
+                        :question="$gettext('Möchten Sie den Eintrag wirklich unwiderruflich löschen?')"
+                        height="200"
+                        @confirm="executeDeleteFeedbackEntry"
+                        @close="closeDeleteEntry"
+                    />
+                </div>
+                <studip-progress-indicator v-else :description="$gettext('Lade Bewertungen…')" />
+            </template>
+        </studip-dialog>
+        <studip-dialog
+            v-if="showDeleteFeedbackDialog"
+            :title="$gettext('Feedback-Element löschen')"
+            :question="
+                $gettext(
+                    'Möchten Sie das Feedback-Element wirklich unwiderruflich löschen? Alle Bewertungen werden ebenfalls gelöscht!'
+                )
+            "
+            height="200"
+            @confirm="executeDeleteFeedback"
+            @close="showDeleteFeedbackDialog = false"
+        ></studip-dialog>
+    </div>
+</template>
+<script>
+import FeedbackElementUpdate from './FeedbackElementUpdate.vue';
+import FeedbackEntryBox from './FeedbackEntryBox.vue';
+import FeedbackEntryCreate from './FeedbackEntryCreate.vue';
+import FeedbackFiveStarsHistogram from './FeedbackFiveStarsHistogram.vue';
+import StudipProgressIndicator from './../StudipProgressIndicator.vue';
+
+import { mapActions, mapGetters } from 'vuex';
+
+export default {
+    name: 'feedback-dialog',
+    components: {
+        FeedbackElementUpdate,
+        FeedbackEntryBox,
+        FeedbackEntryCreate,
+        FeedbackFiveStarsHistogram,
+        StudipProgressIndicator,
+    },
+    props: {
+        feedbackElementId: {
+            type: Number,
+            required: true,
+        },
+        currentUser: {
+            type: Object,
+            required: true,
+        },
+    },
+    data() {
+        return {
+            height: '0',
+            width: '0',
+            loadingFeedbackElement: false,
+            editEntry: false,
+            currentDeleteEntryId: null,
+            showDeleteEntryDialog: false,
+            showDeleteFeedbackDialog: false,
+            editElement: false,
+        };
+    },
+    computed: {
+        ...mapGetters({
+            feedbackElementById: 'feedback-elements/byId',
+            canEditFeedbackElement: 'canEditFeedbackElement',
+            feedbackEntries: 'feedback-entries/all',
+        }),
+        entries() {
+            return this.feedbackEntries.filter(
+                (entry) => parseInt(entry.relationships?.['feedback-element']?.data?.id) === this.feedbackElementId
+            );
+        },
+        feedbackElement() {
+            return this.feedbackElementById({ id: this.feedbackElementId }) ?? null;
+        },
+        currentUserIsAuthor() {
+            return this.currentUser.id === this.feedbackElement?.relationships?.author?.data?.id;
+        },
+        currentUserEntry() {
+            return this.entries.filter((entry) => this.isUserEntry(entry));
+        },
+        otherUserEntries() {
+            return this.entries.filter((entry) => !this.isUserEntry(entry));
+        },
+        hasCurrentUserEntry() {
+            return this.currentUserEntry.length > 0;
+        },
+        description() {
+            return this.feedbackElement?.attributes?.description;
+        },
+        hasDescription() {
+            return this.description !== '';
+        },
+    },
+    methods: {
+        ...mapActions({
+            loadFeedbackElement: 'feedback-elements/loadById',
+            deleteFeedbackEntries: 'feedback-entries/delete',
+            deleteFeedbackElement: 'feedback-elements/delete',
+        }),
+        setDimensions() {
+            this.height = (window.innerHeight * 0.8).toFixed(0);
+            this.width = Math.min((window.innerWidth * 0.8).toFixed(0), 890).toFixed(0);
+        },
+        isUserEntry(entry) {
+            return this.currentUser.id === entry.relationships?.author?.data?.id;
+        },
+        showDeleteEntry(entry) {
+            this.currentDeleteEntryId = entry.id;
+            this.showDeleteEntryDialog = true;
+        },
+        closeDeleteEntry() {
+            this.showDeleteEntryDialog = false;
+            this.currentDeleteEntryId = null;
+        },
+        executeDeleteFeedbackEntry() {
+            this.deleteFeedbackEntries({ id: this.currentDeleteEntryId });
+            this.closeDeleteEntry();
+        },
+        executeDeleteFeedback() {
+            this.deleteFeedbackElement({ id: this.feedbackElementId }).then(() => {
+                this.$emit('deleted');
+                this.$emit('close');
+            });
+        },
+        updateFeedbackElement() {
+            this.editElement = false;
+            this.loadElement();
+        },
+        async loadElement() {
+            this.loadingFeedbackElement = true;
+            await this.loadFeedbackElement({ id: this.feedbackElementId, options: { include: 'entries' } });
+            this.loadingFeedbackElement = false;
+        },
+    },
+    mounted() {
+        this.setDimensions();
+        this.loadElement();
+    },
+};
+</script>
diff --git a/resources/vue/components/feedback/FeedbackElementUpdate.vue b/resources/vue/components/feedback/FeedbackElementUpdate.vue
new file mode 100644
index 0000000000000000000000000000000000000000..d9706de490f66e9103667583e68c9a772f728a37
--- /dev/null
+++ b/resources/vue/components/feedback/FeedbackElementUpdate.vue
@@ -0,0 +1,69 @@
+<template>
+    <form class="default feedback-element-update" @submit.prevent="">
+        <h3>{{ $gettext('Feedback-Element bearbeiten') }}</h3>
+        <label>
+            {{ $gettext('Frage') }}
+            <input type="text" v-model="currentQuestion" />
+        </label>
+        <label>
+            {{ $gettext('Beschreibung') }}
+            <textarea v-model="currentDescription"></textarea>
+        </label>
+        <div class="button-wrapper">
+            <button class="button accept" @click="submitUpdate">
+                {{ $gettext('Absenden') }}
+            </button>
+            <button class="button cancel" @click="$emit('cancel')">
+                {{ $gettext('Abbrechen') }}
+            </button>
+        </div>
+    </form>
+</template>
+
+<script>
+import { mapActions, mapGetters } from 'vuex';
+export default {
+    name: 'feedback-element-update',
+    props: {
+        feedbackElementId: {
+            type: Number,
+            required: true,
+        },
+    },
+    data() {
+        return {
+            currentQuestion: '',
+            currentDescription: '',
+        };
+    },
+    computed: {
+        ...mapGetters({
+            feedbackElementById: 'feedback-elements/byId',
+        }),
+        feedbackElement() {
+            return this.feedbackElementById({ id: this.feedbackElementId });
+        },
+    },
+    methods: {
+        ...mapActions({
+            updateFeedbackElement: 'feedback-elements/update',
+        }),
+        async submitUpdate() {
+            let data = {
+                id: this.feedbackElementId,
+                type: 'feedback-elements',
+                attributes: {
+                    question: this.currentQuestion,
+                    description: this.currentDescription,
+                },
+            };
+            await this.updateFeedbackElement(data);
+            this.$emit('submit');
+        },
+    },
+    mounted() {
+        this.currentQuestion = this.feedbackElement.attributes?.question;
+        this.currentDescription = this.feedbackElement.attributes?.description.replace(/<\/?[^>]+>/gi, ' ').trim();
+    },
+};
+</script>
diff --git a/resources/vue/components/feedback/FeedbackEntryBox.vue b/resources/vue/components/feedback/FeedbackEntryBox.vue
new file mode 100644
index 0000000000000000000000000000000000000000..0400c9d25a20cc6784ca806aa5146e9d5f01a57a
--- /dev/null
+++ b/resources/vue/components/feedback/FeedbackEntryBox.vue
@@ -0,0 +1,106 @@
+<template>
+    <div class="feedback-entry-box" v-show="!loadingUser">
+        <div class="feedback-entry-box-avatar">
+            <img :src="avatarUrl" />
+        </div>
+        <div class="feedback-entry-box-content">
+            <h4>{{ title }}</h4>
+            <studip-five-stars :amount="parseInt(entry.attributes.rating)" :size="16" />
+            <p>{{ entry.attributes.comment }}</p>
+        </div>
+        <div>
+            <button v-if="canEdit" class="as-link" @click="$emit('edit')">
+                <studip-icon shape="edit" />
+            </button>
+            <button v-if="canDelete" class="as-link" @click="deleteEntry">
+                <studip-icon shape="trash" />
+            </button>
+        </div>
+    </div>
+</template>
+<script>
+import StudipFiveStars from './StudipFiveStars.vue';
+
+import { mapActions, mapGetters } from 'vuex';
+export default {
+    name: 'feedback-entry-box',
+    components: {
+        StudipFiveStars,
+    },
+    props: {
+        entry: {
+            type: Object,
+            required: true,
+        },
+        name: {
+            type: String,
+            required: false,
+        },
+        canEdit: {
+            type: Boolean,
+            default: false,
+        },
+        canDelete: {
+            type: Boolean,
+            default: false,
+        },
+    },
+    data() {
+        return {
+            loadingUser: false,
+        };
+    },
+    computed: {
+        ...mapGetters({
+            getUser: 'users/byId',
+        }),
+        title() {
+            return this.name ?? this.userName;
+        },
+        userName() {
+            return this.user?.attributes?.['formatted-name'] ?? 'Anonym';
+        },
+        user() {
+            if (this.anonymous) {
+                return null;
+            }
+            const userId = this.entry.relationships?.author?.data?.id;
+            return this.getUser({ id: userId });
+        },
+        avatarUrl() {
+            return (
+                this.user?.meta?.avatar?.small ?? STUDIP.URLHelper.getURL('assets/images/avatars/user/nobody_small.webp', {}, true)
+            );
+        },
+        anonymous() {
+            return this.entry.attributes.anonymous;
+        }
+    },
+    methods: {
+        ...mapActions({
+            loadUser: 'users/loadById',
+        }),
+        getEntryUser() {
+            this.loadingUser = true;
+            const userId = this.entry.relationships?.author?.data?.id;
+            const user = this.getUser({ id: userId });
+            if (user) {
+                this.loadingUser = false;
+                return;
+            }
+
+            this.loadUser({ id: userId }).then(() => {
+                this.loadingUser = false;
+            });
+        },
+        deleteEntry() {
+            this.$emit('delete', { id: this.entry.id });
+        },
+    },
+    mounted() {
+        if (!this.anonymous) {
+            this.getEntryUser();
+        }
+    },
+};
+</script>
diff --git a/resources/vue/components/feedback/FeedbackEntryCreate.vue b/resources/vue/components/feedback/FeedbackEntryCreate.vue
new file mode 100644
index 0000000000000000000000000000000000000000..5735d45738776dfd332eb70d4f0b2ab19df298a0
--- /dev/null
+++ b/resources/vue/components/feedback/FeedbackEntryCreate.vue
@@ -0,0 +1,114 @@
+<template>
+    <div v-if="feedbackElement" class="feedback-entry-create">
+        <studip-five-stars-input v-model="rating" />
+        <label v-if="isCommentable">
+            {{ $gettext('Kommentar') }}
+            <textarea v-model="comment"></textarea>
+        </label>
+        <label v-if="anonymousEntriesEnabled">
+            <input type="checkbox" v-model="anonymous" />
+            {{ $gettext('Feedback anonym abgeben') }}
+        </label>
+        <div class="button-wrapper">
+            <button class="button accept" @click="submitEntry">
+                {{ $gettext('Absenden') }}
+            </button>
+            <button v-if="hasEntry" class="button cancel" @click="$emit('cancel')">
+                {{ $gettext('Abbrechen') }}
+            </button>
+            
+        </div>
+    </div>
+</template>
+
+<script>
+import StudipFiveStarsInput from './StudipFiveStarsInput.vue';
+import { mapActions } from 'vuex';
+
+export default {
+    name: 'feedback-entry-create',
+    components: {
+        StudipFiveStarsInput,
+    },
+    props: {
+        feedbackElement: {
+            type: Object || null,
+        },
+        entry: {
+            type: Object,
+            default: null,
+        },
+        currentUser: {
+            type: Object,
+            required: true
+        }
+    },
+    data() {
+        return {
+            rating: 0,
+            comment: '',
+            anonymous: false
+        };
+    },
+    computed: {
+        hasEntry() {
+            return this.entry !== null;
+        },
+        anonymousEntriesEnabled() {
+            return this.feedbackElement?.attributes['anonymous-entries'];
+        },
+        isCommentable() {
+            return this.feedbackElement?.attributes['is-commentable'];
+        }
+    },
+    methods: {
+        ...mapActions({
+            loadFeedbackEntriesById: 'feedback-entries/byId',
+            createFeedbackEntries: 'feedback-entries/create',
+            updateFeedbackEntries: 'feedback-entries/update',
+        }),
+        async submitEntry() {
+            let data = {
+                attributes: {
+                    rating: this.rating,
+                },
+                relationships: {
+                    'feedback-element': {
+                        data: {
+                            type: 'feedback-elements',
+                            id: this.feedbackElement.id,
+                        },
+                    },
+                    author: {
+                        data: {
+                            id: this.currentUser.id,
+                            type: 'users' 
+                        }
+                    }
+                },
+            };
+            if (this.isCommentable) {
+                data.attributes.comment = this.comment
+            }
+            if (this.anonymousEntriesEnabled) {
+                data.attributes.anonymous = this.anonymous;
+            }
+            if (this.hasEntry) {
+                data.id = this.entry.id;
+                data.type = this.entry.type;
+                await this.updateFeedbackEntries(data);
+            } else {
+                await this.createFeedbackEntries(data);
+            }
+            this.$emit('submit');
+        },
+    },
+    mounted() {
+        if (this.hasEntry) {
+            this.rating = parseInt(this.entry.attributes.rating);
+            this.comment = this.entry.attributes.comment;
+            this.anonymous = this.entry.attributes.anonymous;
+        }
+    },
+};
+</script>
\ No newline at end of file
diff --git a/resources/vue/components/feedback/FeedbackFiveStarsHistogram.vue b/resources/vue/components/feedback/FeedbackFiveStarsHistogram.vue
new file mode 100644
index 0000000000000000000000000000000000000000..b40245276ba41252d0dd1cb20f763b86be96e52f
--- /dev/null
+++ b/resources/vue/components/feedback/FeedbackFiveStarsHistogram.vue
@@ -0,0 +1,91 @@
+<template>
+    <div class="five-stars-histogram" :class="{ vertical: vertical }">
+        <div class="five-stars-histogram-average">
+            <p class="fraction">
+                <span class="average">{{ average.toFixed(1) }}</span
+                >/5
+            </p>
+            <studip-five-stars :amount="average" />
+            <p class="total">
+                {{
+                    $gettextInterpolate($ngettext('%{n} Bewertung', '%{n} Bewertungen', entries.length), {
+                        n: entries.length,
+                    })
+                }}
+            </p>
+        </div>
+        <div class="five-stars-histogram-chart" v-if="ratings">
+            <div v-for="i in [5, 4, 3, 2, 1]" :key="'chart-' + i">
+                <span>{{ i }} <studip-icon shape="star" role="info" /></span>
+                <div class="percentage">
+                    <div class="percentage-bar" :style="{ width: getRatePercentage(ratings[i]) }">
+                        {{ getRatePercentage(ratings[i]) }}
+                    </div>
+                </div>
+                <span>{{ ratings[i] ?? 0 }}</span>
+            </div>
+        </div>
+    </div>
+</template>
+<script>
+import StudipFiveStars from './StudipFiveStars.vue';
+
+export default {
+    name: 'feedback-five-stars-histogram',
+    components: {
+        StudipFiveStars,
+    },
+    props: {
+        entries: Array,
+        vertical: {
+            type: Boolean,
+            default: false,
+        },
+    },
+    data() {
+        return {
+            ratings: null,
+        };
+    },
+    computed: {
+        average() {
+            if (this.entries.length === 0) {
+                return 0;
+            }
+            let sum = this.entries.reduce((acc, entry) => acc + parseInt(entry.attributes.rating), 0);
+
+            return sum / this.entries.length;
+        },
+    },
+    methods: {
+        getCountOfRatings() {
+            this.ratings = [];
+            this.entries.forEach((entry) => {
+                const rating = entry.attributes.rating;
+                if (this.ratings[rating]) {
+                    this.ratings[rating] += 1;
+                } else {
+                    this.ratings[rating] = 1;
+                }
+            });
+        },
+        getRatePercentage(rate) {
+            if (rate === undefined) {
+                return '0%';
+            }
+            return parseInt((rate / this.entries.length) * 100, 10) + '%';
+        },
+    },
+    mounted() {
+        this.getCountOfRatings();
+    },
+    watch: {
+        entries: {
+            handler() {
+                this.getCountOfRatings();
+            },
+            deep: true,
+        },
+    },
+};
+</script>
diff --git a/resources/vue/components/feedback/StudipFiveStars.vue b/resources/vue/components/feedback/StudipFiveStars.vue
new file mode 100644
index 0000000000000000000000000000000000000000..1a4f40b327f3889d896f8354a7922061361e4c22
--- /dev/null
+++ b/resources/vue/components/feedback/StudipFiveStars.vue
@@ -0,0 +1,46 @@
+<template>
+    <div class="studip-five-stars">
+        <studip-icon v-for="index in fullStars" :key="index+'full'" shape="star" :role="role" :size="size" /><studip-icon v-if="halfStar" shape="star-halffull" :role="role" :size="size" /><studip-icon v-for="index in emptyStars" :key="index+'empty'" shape="star-empty" :role="role" :size="size" />
+    </div>
+</template>
+
+<script>
+import StudipIcon from './../StudipIcon.vue';
+export default {
+    name: 'studip-five-stars',
+    components: {
+        StudipIcon
+    },
+    props: {
+        amount: {
+            type: Number,
+            required: true,
+            validator(value) {
+                return value <= 5 && value >= 0
+            }
+        },
+        role: {
+            type: String,
+            required: false,
+            default: 'status-yellow',
+        },
+        size: {
+            type: Number,
+            required: false,
+            default: 24,
+        }
+    },
+    computed: {
+        fullStars() {
+            return Math.floor(this.amount);
+        },
+        halfStar() {
+            return this.amount - this.fullStars >= 0.5
+        },
+        emptyStars() {
+            const half = this.halfStar ? 1 : 0;
+            return 5 - this.fullStars - half;
+        }
+    }
+}
+</script>
\ No newline at end of file
diff --git a/resources/vue/components/feedback/StudipFiveStarsInput.vue b/resources/vue/components/feedback/StudipFiveStarsInput.vue
new file mode 100644
index 0000000000000000000000000000000000000000..6fa95f09ce8602f13654767669dada90ba1660d1
--- /dev/null
+++ b/resources/vue/components/feedback/StudipFiveStarsInput.vue
@@ -0,0 +1,51 @@
+<template>
+    <div class="five-stars-input" :style="{ width: width + 'px' }">
+        <button v-for="i in 5" :key="i" @click="setValue(i)">
+            <studip-icon
+                :shape="getShape(i)"
+                :size="size"
+                :alt="
+                    $gettextInterpolate(
+                        $ngettext(
+                            'auswählen, um mit einem Stern zu bewerten.',
+                            'auswählen, um mit %{i} Sternen zu bewerten.',
+                            i
+                        ),
+                        { i: i }
+                    )
+                "
+            />
+        </button>
+    </div>
+</template>
+<script>
+import StudipIcon from './../StudipIcon.vue';
+export default {
+    name: 'studip-five-stars-input',
+    components: {
+        StudipIcon,
+    },
+    props: {
+        value: {
+            type: Number,
+        },
+        size: {
+            type: Number,
+            default: 24,
+        },
+    },
+    computed: {
+        width() {
+            return (this.size + 2 * 14) * 5;
+        },
+    },
+    methods: {
+        setValue(val) {
+            this.$emit('input', val);
+        },
+        getShape(pos) {
+            return pos <= this.value ? 'star' : 'star-empty';
+        },
+    },
+};
+</script>
diff --git a/resources/vue/courseware-index-app.js b/resources/vue/courseware-index-app.js
index e32baed7d259150b1d0876c32e3d3e1bcd294016..191385f47c3bc944e3681a1b73f2193a31a7f06f 100644
--- a/resources/vue/courseware-index-app.js
+++ b/resources/vue/courseware-index-app.js
@@ -27,6 +27,7 @@ const mountApp = async (STUDIP, createApp, element) => {
     let unit_id = null;
     let licenses = null;
     let elem;
+    let feedbackSettings = null;
 
     if ((elem = document.getElementById(element.substring(1))) !== undefined) {
         if (elem.attributes !== undefined) {
@@ -50,6 +51,10 @@ const mountApp = async (STUDIP, createApp, element) => {
             if (elem.attributes['licenses'] !== undefined) {
                 licenses = JSON.parse(elem.attributes['licenses'].value);
             }
+
+            if (elem.attributes['feedback-settings'] !== undefined) {
+                feedbackSettings = JSON.parse(elem.attributes['feedback-settings'].value);
+            }
         }
     }
     const routes = [
@@ -105,6 +110,8 @@ const mountApp = async (STUDIP, createApp, element) => {
                     'courseware-user-data-fields',
                     'courseware-user-progresses',
                     'courseware-units',
+                    'feedback-elements',
+                    'feedback-entries',
                     'files',
                     'file-refs',
                     'folders',
@@ -147,6 +154,7 @@ const mountApp = async (STUDIP, createApp, element) => {
     if (entry_type === 'courses') {
         await store.dispatch('loadTeacherStatus', STUDIP.USER_ID);
         store.dispatch('loadProgresses');
+        await store.dispatch('setFeedbackSettings', feedbackSettings);
     }
 
     store.dispatch('coursewareCurrentElement', elem_id);
diff --git a/resources/vue/courseware-shelf-app.js b/resources/vue/courseware-shelf-app.js
index 68a8ac97debe84cbbd8fefa5260a92033cf0e931..e212fd0af32f67bc49a839bd2ab709fd70d8b2b7 100644
--- a/resources/vue/courseware-shelf-app.js
+++ b/resources/vue/courseware-shelf-app.js
@@ -30,6 +30,7 @@ const mountApp = async (STUDIP, createApp, element) => {
     let entry_id = null;
     let entry_type = null;
     let licenses = null;
+    let feedbackSettings = null;
 
     if ((elem = document.getElementById(element.substring(1))) !== undefined) {
         if (elem.attributes !== undefined) {
@@ -44,6 +45,9 @@ const mountApp = async (STUDIP, createApp, element) => {
             if (elem.attributes['licenses'] !== undefined) {
                 licenses = JSON.parse(elem.attributes['licenses'].value);
             }
+            if (elem.attributes['feedback-settings'] !== undefined) {
+                feedbackSettings = JSON.parse(elem.attributes['feedback-settings'].value);
+            }
         }
     }
 
@@ -64,6 +68,8 @@ const mountApp = async (STUDIP, createApp, element) => {
                     'courseware-user-progresses',
                     'courseware-structural-elements',
                     'courseware-structural-elements-shared',
+                    'feedback-elements',
+                    'feedback-entries',
                     'files',
                     'file-refs',
                     'folders',
@@ -91,6 +97,7 @@ const mountApp = async (STUDIP, createApp, element) => {
     if (entry_type === 'courses') {
         await store.dispatch('loadTeacherStatus', STUDIP.USER_ID);
         await store.dispatch('loadCourseUnits', entry_id);
+        await store.dispatch('setFeedbackSettings', feedbackSettings);
     } else {
         await store.dispatch('loadUserUnits', entry_id);
         await store.dispatch('courseware-structural-elements-shared/loadAll', { options: { include: 'owner' } });
diff --git a/resources/vue/store/courseware/courseware-shelf.module.js b/resources/vue/store/courseware/courseware-shelf.module.js
index 641ec51af4c881d86d1ea4cd97dbe22c730040c3..dc92a23ae4601a15a48452ec86aec2fd2ee3af89 100644
--- a/resources/vue/store/courseware/courseware-shelf.module.js
+++ b/resources/vue/store/courseware/courseware-shelf.module.js
@@ -25,6 +25,8 @@ const getDefaultState = () => {
         importStructuresState: '',
         importStructuresProgress: 0,
         importErrors: [],
+
+        feedbackSettings: null,
     };
 };
 
@@ -103,6 +105,23 @@ const getters = {
     importErrors(state) {
         return state.importErrors;
     },
+    feedbackSettings(state) {
+        return state.feedbackSettings;
+    },
+    isFeedbackActivated(state, getters) {
+        return getters.feedbackSettings?.activated ?? false;
+    },
+    canCreateFeedbackElement(state, getters) {
+        return getters.feedbackSettings?.createPerm ?? false;
+    },
+    canEditFeedbackElement(state, getters) {
+        return getters.feedbackSettings?.adminPerm ?? false;
+    },
+
+    currentUser(state, getters, rootState, rootGetters) {
+        const id = getters.userId;
+        return rootGetters['users/byId']({ id });
+    },
 };
 
 export const state = { ...initialState };
@@ -158,11 +177,15 @@ export const actions = {
         context.commit('setUrlHelper', urlHelper);
     },
 
+    setFeedbackSettings(context, feedbackSettings) {
+        context.commit('setFeedbackSettings', feedbackSettings);
+    },
+
     // other actions
     loadCourseUnits({ dispatch }, cid) {
         const parent = { type: 'courses', id: cid };
         const relationship = 'courseware-units';
-        const options = { include: 'structural-element' }
+        const options = { include: 'structural-element, feedback-element' }
 
         return dispatch('loadRelatedPaginated', {
             type: 'courseware-units',
@@ -802,6 +825,10 @@ export const mutations = {
     setImportStructuresProgress(state, importStructuresProgress) {
         state.importStructuresProgress = importStructuresProgress;
     },
+
+    setFeedbackSettings(state, feedbackSettings) {
+        state.feedbackSettings = feedbackSettings;
+    }
 };
 
 export default {
diff --git a/resources/vue/store/courseware/courseware.module.js b/resources/vue/store/courseware/courseware.module.js
index 2f4dc60641803d00562856b9dd8b85c9210c3059..d5915b418d31444b0b232df98fb80aab37082de9 100644
--- a/resources/vue/store/courseware/courseware.module.js
+++ b/resources/vue/store/courseware/courseware.module.js
@@ -39,6 +39,8 @@ const getDefaultState = () => {
         showStructuralElementOerDialog: false,
         showStructuralElementPublicLinkDialog: false,
         showStructuralElementRemoveLockDialog: false,
+        showStructuralElementFeedbackDialog: false,
+        showStructuralElementFeedbackCreateDialog: false,
 
         showSuggestOerDialog: false,
 
@@ -65,6 +67,7 @@ const getDefaultState = () => {
         progresses: null,
 
         toolbarActive: true,
+        feedbackSettings: null,
     };
 };
 
@@ -74,6 +77,10 @@ const getters = {
     msg(state) {
         return state.msg;
     },
+    currentUser(state, getters, rootState, rootGetters) {
+        const id = getters.userId;
+        return rootGetters['users/byId']({ id });
+    },
     lastElement(state) {
         return state.lastElement;
     },
@@ -86,6 +93,9 @@ const getters = {
     showRootElement(state, getters) {
         return getters.rootLayout !== 'none';
     },
+    rootId(state, getters) {
+        return getters.courseware?.relationships?.root?.data?.id;
+    },
     currentElement(state) {
         return state.currentElement;
     },
@@ -220,6 +230,12 @@ const getters = {
     showStructuralElementRemoveLockDialog(state) {
         return state.showStructuralElementRemoveLockDialog;
     },
+    showStructuralElementFeedbackDialog(state) {
+        return state.showStructuralElementFeedbackDialog;
+    },
+    showStructuralElementFeedbackCreateDialog(state) {
+        return state.showStructuralElementFeedbackCreateDialog;
+    },
     showOverviewElementAddDialog(state) {
         return state.showOverviewElementAddDialog;
     },
@@ -281,7 +297,19 @@ const getters = {
 
     toolbarActive(state) {
         return state.toolbarActive;
-    }
+    },
+    feedbackSettings(state) {
+        return state.feedbackSettings;
+    },
+    isFeedbackActivated(state, getters) {
+        return getters.feedbackSettings?.activated ?? false;
+    },
+    canCreateFeedbackElement(state, getters) {
+        return getters.feedbackSettings?.createPerm ?? false;
+    },
+    canEditFeedbackElement(state, getters) {
+        return getters.feedbackSettings?.adminPerm ?? false;
+    },
 };
 
 export const state = { ...initialState };
@@ -1015,6 +1043,13 @@ export const actions = {
         context.commit('setShowStructuralElementRemoveLockDialog', bool);
     },
 
+    showStructuralElementFeedbackDialog(context, bool) {
+        context.commit('setShowStructuralElementFeedbackDialog', bool);
+    },
+    showStructuralElementFeedbackCreateDialog(context, bool) {
+        context.commit('setShowStructuralElementFeedbackCreateDialog', bool);
+    },
+
     setShowOverviewElementAddDialog(context, bool) {
         context.commit('setShowOverviewElementAddDialog', bool);
     },
@@ -1502,7 +1537,10 @@ export const actions = {
 
     toggleToolbarActive({ commit, rootGetters }) {
         commit('setToolbarActive', !rootGetters['toolbarActive']);
-    }
+    },
+    setFeedbackSettings(context, feedbackSettings) {
+        context.commit('setFeedbackSettings', feedbackSettings);
+    },
 };
 
 /* eslint no-param-reassign: ["error", { "props": false }] */
@@ -1649,6 +1687,13 @@ export const mutations = {
         state.showStructuralElementRemoveLockDialog = showRemoveLock;
     },
 
+    setShowStructuralElementFeedbackDialog(state, showFeedback) {
+        state.showStructuralElementFeedbackDialog = showFeedback;
+    },
+    setShowStructuralElementFeedbackCreateDialog(state, showFeedbackCreate) {
+        state.showStructuralElementFeedbackCreateDialog = showFeedbackCreate;
+    },
+
     setImportFilesState(state, importFilesState) {
         state.importFilesState = importFilesState;
     },
@@ -1700,6 +1745,9 @@ export const mutations = {
     },
     setToolbarActive(state, active) {
         state.toolbarActive = active;
+    },
+    setFeedbackSettings(state, feedbackSettings) {
+        state.feedbackSettings = feedbackSettings;
     }
 };