diff --git a/app/controllers/jsupdater.php b/app/controllers/jsupdater.php
index 448230ba118e3b0cc9ac06a4551465ca860b44fa..d0cd7219c5796b6113921c6cf9e6ff89415b4d48 100644
--- a/app/controllers/jsupdater.php
+++ b/app/controllers/jsupdater.php
@@ -113,6 +113,7 @@ class JsupdaterController extends AuthenticatedController
     {
         $pageInfo = Request::getArray("page_info");
         $data = [
+            'coursewareclipboard' => $this->getCoursewareClipboardUpdates($pageInfo),
             'blubber' => $this->getBlubberUpdates($pageInfo),
             'messages' => $this->getMessagesUpdates($pageInfo),
             'personalnotifications' => $this->getPersonalNotificationUpdates($pageInfo),
@@ -258,4 +259,9 @@ class JsupdaterController extends AuthenticatedController
 
         return $data;
     }
+
+    private function getCoursewareClipboardUpdates($pageInfo)
+    {
+        return count(\Courseware\Clipboard::findUsersClipboards($GLOBALS["user"])) != $pageInfo['coursewareclipboard']['counter'];
+    }
 }
diff --git a/db/migrations/5.4.5_create_cw_clipboards_table.php b/db/migrations/5.4.5_create_cw_clipboards_table.php
new file mode 100644
index 0000000000000000000000000000000000000000..02fc9613a9d1146e3af8c3b3b9a1a40f90c9fce5
--- /dev/null
+++ b/db/migrations/5.4.5_create_cw_clipboards_table.php
@@ -0,0 +1,39 @@
+<?php
+
+class CreateCwClipboardsTable extends Migration
+{
+    public function description()
+    {
+        return 'create table for courseware clipboards';
+    }
+
+    public function up()
+    {
+        $db = DBManager::get();
+
+        $query = "CREATE TABLE IF NOT EXISTS `cw_clipboards` (
+            `id`                    INT(11) NOT NULL AUTO_INCREMENT,
+            `user_id`               CHAR(32) COLLATE latin1_bin NOT NULL,
+            `name`                  VARCHAR(255) NOT NULL,
+            `description`           MEDIUMTEXT NOT NULL,
+            `block_id`              INT(11) NULL,
+            `container_id`          INT(11) NULL,
+            `structural_element_id` INT(11) NULL,
+            `object_type`           ENUM('courseware-structural-elements', 'courseware-containers', 'courseware-blocks') COLLATE latin1_bin NOT NULL,
+            `object_kind`           VARCHAR(255) COLLATE latin1_bin NOT NULL,
+            `backup`                MEDIUMTEXT NOT NULL,
+            `mkdate`                INT(11) UNSIGNED NOT NULL,
+            `chdate`                INT(11) UNSIGNED NOT NULL,
+
+            PRIMARY KEY (`id`),
+            INDEX index_user_id (`user_id`)
+        )";
+        $db->exec($query);
+    }
+
+    public function down()
+    {
+        $db = \DBManager::get();
+        $db->exec('DROP TABLE IF EXISTS `cw_clipboards`');
+    }
+}
diff --git a/lib/classes/JsonApi/RouteMap.php b/lib/classes/JsonApi/RouteMap.php
index bef6327d19c57e4acbd45a3e251ec2657bbfced4..f60c4dd5d1577bf3b13567c6ad4440e50ff07049 100644
--- a/lib/classes/JsonApi/RouteMap.php
+++ b/lib/classes/JsonApi/RouteMap.php
@@ -491,6 +491,16 @@ class RouteMap
         $group->delete('/courseware-units/{id}', Routes\Courseware\UnitsDelete::class);
         // not a JSON route
         $group->post('/courseware-units/{id}/copy', Routes\Courseware\UnitsCopy::class);
+
+        $group->get('/courseware-clipboards', Routes\Courseware\ClipboardsIndex::class);
+        $group->get('/users/{id}/courseware-clipboards', Routes\Courseware\UsersClipboardsIndex::class);
+        $group->delete('/users/{id}/courseware-clipboards/{type:courseware-blocks|courseware-containers}', Routes\Courseware\UsersClipboardsDelete::class);
+        $group->get('/courseware-clipboards/{id}', Routes\Courseware\ClipboardsShow::class);
+        $group->post('/courseware-clipboards', Routes\Courseware\ClipboardsCreate::class);
+        $group->patch('/courseware-clipboards/{id}', Routes\Courseware\ClipboardsUpdate::class);
+        $group->delete('/courseware-clipboards/{id}', Routes\Courseware\ClipboardsDelete::class);
+
+        $group->post('/courseware-clipboards/{id}/insert', Routes\Courseware\ClipboardsInsert::class);
     }
 
     private function addAuthenticatedFilesRoutes(RouteCollectorProxy $group): void
diff --git a/lib/classes/JsonApi/Routes/Courseware/Authority.php b/lib/classes/JsonApi/Routes/Courseware/Authority.php
index 740364b6a1340963732803ec616a75498ed3b322..9fb281005b52d450b50aab46c963b85f55964b80 100644
--- a/lib/classes/JsonApi/Routes/Courseware/Authority.php
+++ b/lib/classes/JsonApi/Routes/Courseware/Authority.php
@@ -5,6 +5,7 @@ namespace JsonApi\Routes\Courseware;
 use Courseware\Block;
 use Courseware\BlockComment;
 use Courseware\BlockFeedback;
+use Courseware\Clipboard;
 use Courseware\Container;
 use Courseware\Instance;
 use Courseware\StructuralElement;
@@ -512,4 +513,41 @@ class Authority
         return $request_user->id === $user->id;
     }
 
+
+    public static function canShowClipboard(User $user, Clipboard $resource): bool
+    {
+        return $resource->user_id === $user->id;
+    }
+
+    public static function canIndexClipboardsOfAUser(User $request_user, User $user): bool
+    {
+        return $request_user->id === $user->id;
+    }
+
+    public static function canIndexClipboards(User $user): bool
+    {
+        return $GLOBALS['perm']->have_perm('root', $user->id);
+    }
+
+    public static function canCreateClipboard(User $user): bool
+    {
+        return true;
+    }
+
+    public static function canUpdateClipboard(User $user, Clipboard $resource): bool
+    {
+        return $resource->user_id === $user->id;
+    }
+
+    public static function canDeleteClipboard(User $user, Clipboard $resource): bool
+    {
+        return self::canUpdateClipboard($user, $resource);
+    }
+
+    public static function canDeleteClipboardsOfAUser(User $request_user, User $user): bool
+    {
+        return self::canIndexClipboardsOfAUser($request_user, $user);
+    }
+
+
 }
diff --git a/lib/classes/JsonApi/Routes/Courseware/BlocksCopy.php b/lib/classes/JsonApi/Routes/Courseware/BlocksCopy.php
index 3e34ed0e97dbd248955c422bdc09ac4d5b41afc3..d8bb1c5304f384e64a60f97f24f561e11cb08c17 100644
--- a/lib/classes/JsonApi/Routes/Courseware/BlocksCopy.php
+++ b/lib/classes/JsonApi/Routes/Courseware/BlocksCopy.php
@@ -29,14 +29,21 @@ class BlocksCopy extends NonJsonApiController
         $data = $request->getParsedBody()['data'];
 
         $block = \Courseware\Block::find($data['block']['id']);
+        if (!$block) {
+            throw new RecordNotFoundException();
+        }
         $container = \Courseware\Container::find($data['parent_id']);
+        if (!$container) {
+            throw new RecordNotFoundException();
+        }
+        $sectionIndex = $data['section'];
         $user = $this->getUser($request);
 
         if (!Authority::canCreateBlocks($user, $container) || !Authority::canUpdateBlock($user, $block)) {
             throw new AuthorizationFailedException();
         }
 
-        $new_block = $this->copyBlock($user, $block, $container);
+        $new_block = $this->copyBlock($user, $block, $container, $sectionIndex);
 
         $response = $response->withHeader('Content-Type', 'application/json');
         $response->getBody()->write((string) json_encode($new_block));
@@ -44,19 +51,8 @@ class BlocksCopy extends NonJsonApiController
         return $response;
     }
 
-    private function copyBlock(\User $user, \Courseware\Block $remote_block, \Courseware\Container $container)
-    {
-
-        $block = $remote_block->copy($user, $container);
-
-        $this->updateContainer($container, $block);
-
-        return $block;
-    }
-
-    private function updateContainer(\Courseware\Container $container, \Courseware\Block $block)
+    private function copyBlock(\User $user, \Courseware\Block $remote_block, \Courseware\Container $container, $sectionIndex)
     {
-        //TODO update section block ids
-        return true;
+        return $remote_block->copy($user, $container, $sectionIndex);
     }
 }
diff --git a/lib/classes/JsonApi/Routes/Courseware/ClipboardsCreate.php b/lib/classes/JsonApi/Routes/Courseware/ClipboardsCreate.php
new file mode 100644
index 0000000000000000000000000000000000000000..48aa78a464ffec6ad08410de180ffb12ec667bdd
--- /dev/null
+++ b/lib/classes/JsonApi/Routes/Courseware/ClipboardsCreate.php
@@ -0,0 +1,123 @@
+<?php
+
+namespace JsonApi\Routes\Courseware;
+
+use JsonApi\Errors\AuthorizationFailedException;
+use JsonApi\Errors\RecordNotFoundException;
+use JsonApi\JsonApiController;
+use JsonApi\Routes\ValidationTrait;
+use JsonApi\Schemas\Courseware\Clipboard as ClipboardSchema;
+use Psr\Http\Message\ResponseInterface as Response;
+use Psr\Http\Message\ServerRequestInterface as Request;
+
+/**
+ * Create a clipboard.
+ * 
+ * @author  Ron Lucke <lucke@elan-ev.de>
+ * @license GPL2 or any later version
+ *
+ * @since   Stud.IP 5.4
+ */
+class ClipboardsCreate extends JsonApiController
+{
+    use ValidationTrait;
+
+    /**
+     * @SuppressWarnings(PHPMD.UnusedFormalParameter)
+     */
+    public function __invoke(Request $request, Response $response, $args)
+    {
+        $json = $this->validate($request);
+        $user = $this->getUser($request);
+        if (!Authority::canCreateClipboard($user)) {
+            throw new AuthorizationFailedException();
+        }
+        $object = $this->getObject($json);
+        if (!$object) {
+            throw new RecordNotFoundException();
+        }
+        $clipboard = $this->createClipboard($user, $json, $object);
+
+        return $this->getCreatedResponse($clipboard);
+    }
+
+    /**
+     * @SuppressWarnings(PHPMD.UnusedFormalParameters)
+     */
+    protected function validateResourceDocument($json, $data)
+    {
+        if (!self::arrayHas($json, 'data')) {
+            return 'Missing `data` member at document´s top level.';
+        }
+        if (!self::arrayHas($json, 'data.attributes.name')) {
+            return 'Missing `name` value.';
+        }
+        if (!self::arrayHas($json, 'data.attributes.object-type')) {
+            return 'Missing `object-type` value.';
+        }
+        if (
+            !(
+                self::arrayHas($json, 'data.attributes.block-id')
+                || self::arrayHas($json, 'data.attributes.container-id')
+                || self::arrayHas($json, 'data.attributes.structural-element-id')
+            )
+        ) {
+            return 'Missing `block-id`, `container-id` or `structural-element-id` value.';
+        }
+        if (!self::arrayHas($json, 'data.attributes.object-kind')) {
+            return 'Missing `object-kind` value.';
+        }
+        if (!$this->validateObjectType($json)) {
+            return 'Invalid `object-type` value.';
+        }
+    }
+
+    private function createClipboard(\User $user, array $json, $object)
+    {
+        $clipboard = \Courseware\Clipboard::create([
+            'user_id' => $user->id,
+            'name' => self::arrayGet($json, 'data.attributes.name'),
+            'block_id' => self::arrayGet($json, 'data.attributes.block-id'),
+            'container_id' => self::arrayGet($json, 'data.attributes.container-id'),
+            'structural_element_id' => self::arrayGet($json, 'data.attributes.structural-element-id'),
+            'object_type' => self::arrayGet($json, 'data.attributes.object-type'),
+            'object_kind' => self::arrayGet($json, 'data.attributes.object-kind'),
+            'backup' => $this->createBackup($object)
+        ]);
+
+        return $clipboard;
+    }
+
+    private function createBackup($object): string
+    {
+        return $object->getClipboardBackup();
+    }
+
+    private function validateObjectType($json): bool
+    {
+        $type = self::arrayGet($json, 'data.attributes.object-type');
+
+        return in_array($type, ['courseware-structural-elements', 'courseware-containers', 'courseware-blocks']);
+    }
+
+    private function getObject($json): ?object
+    {
+        $object = null;
+        $type = self::arrayGet($json, 'data.attributes.object-type');
+
+        switch ($type) {
+            case 'courseware-structural-elements':
+                $object = \Courseware\StructuralElement::find(self::arrayGet($json, 'data.attributes.structural-element-id'));
+                break;
+            case 'courseware-containers':
+                $object = \Courseware\Container::find(self::arrayGet($json, 'data.attributes.container-id'));
+                break;
+            case 'courseware-blocks':
+                $object = \Courseware\Block::find(self::arrayGet($json, 'data.attributes.block-id'));
+                break;
+        }
+
+        return $object;
+    }
+}
+
diff --git a/lib/classes/JsonApi/Routes/Courseware/ClipboardsDelete.php b/lib/classes/JsonApi/Routes/Courseware/ClipboardsDelete.php
new file mode 100644
index 0000000000000000000000000000000000000000..a917ec28d9ea785b8f91bde478ff3b9b90677b56
--- /dev/null
+++ b/lib/classes/JsonApi/Routes/Courseware/ClipboardsDelete.php
@@ -0,0 +1,39 @@
+<?php
+
+namespace JsonApi\Routes\Courseware;
+
+use Courseware\Clipboard;
+use JsonApi\Errors\AuthorizationFailedException;
+use JsonApi\Errors\RecordNotFoundException;
+use JsonApi\JsonApiController;
+use Psr\Http\Message\ResponseInterface as Response;
+use Psr\Http\Message\ServerRequestInterface as Request;
+
+/**
+ * Delete one clipboard.
+ * 
+ * @author  Ron Lucke <lucke@elan-ev.de>
+ * @license GPL2 or any later version
+ *
+ * @since   Stud.IP 5.4
+ */
+class ClipboardsDelete extends JsonApiController
+{
+    /**
+     * @SuppressWarnings(PHPMD.UnusedFormalParameter)
+     */
+    public function __invoke(Request $request, Response $response, $args)
+    {
+        $resource = Clipboard::find($args['id']);
+        if (!$resource) {
+            throw new RecordNotFoundException();
+        }
+        $user = $this->getUser($request);
+        if (!Authority::canDeleteClipboard($user, $resource)) {
+            throw new AuthorizationFailedException();
+        }
+        $resource->delete();
+
+        return $this->getCodeResponse(204);
+    }
+}
diff --git a/lib/classes/JsonApi/Routes/Courseware/ClipboardsIndex.php b/lib/classes/JsonApi/Routes/Courseware/ClipboardsIndex.php
new file mode 100644
index 0000000000000000000000000000000000000000..b3a5423904634bfea30f963ba8b31bfdf964a50e
--- /dev/null
+++ b/lib/classes/JsonApi/Routes/Courseware/ClipboardsIndex.php
@@ -0,0 +1,42 @@
+<?php
+
+namespace JsonApi\Routes\Courseware;
+
+use Courseware\Clipboard;
+use JsonApi\Errors\AuthorizationFailedException;
+use JsonApi\JsonApiController;
+use Psr\Http\Message\ResponseInterface as Response;
+use Psr\Http\Message\ServerRequestInterface as Request;
+
+/**
+ * Displays all clipboards
+ * 
+ * @author  Ron Lucke <lucke@elan-ev.de>
+ * @license GPL2 or any later version
+ *
+ * @since   Stud.IP 5.4
+ */
+class ClipboardsIndex extends JsonApiController
+{
+    protected $allowedPagingParameters = ['offset', 'limit'];
+
+    protected $allowedIncludePaths = ['user'];
+
+    /**
+     * @SuppressWarnings(PHPMD.UnusedFormalParameter)
+     */
+    public function __invoke(Request $request, Response $response, $args)
+    {
+        $user = $this->getUser($request);
+        if (!Authority::canIndexClipboards($user)) {
+            throw new AuthorizationFailedException();
+        }
+
+        list($offset, $limit) = $this->getOffsetAndLimit();
+
+        $total = Clipboard::countBySQL('1');
+        $resources = Clipboard::findBySQL("1 ORDER BY mkdate LIMIT {$offset}, {$limit}");
+
+        return $this->getPaginatedContentResponse($resources, $total);
+    }
+}
\ No newline at end of file
diff --git a/lib/classes/JsonApi/Routes/Courseware/ClipboardsInsert.php b/lib/classes/JsonApi/Routes/Courseware/ClipboardsInsert.php
new file mode 100644
index 0000000000000000000000000000000000000000..5f5f7d07905b6022f25025c23050871f8b196b8a
--- /dev/null
+++ b/lib/classes/JsonApi/Routes/Courseware/ClipboardsInsert.php
@@ -0,0 +1,68 @@
+<?php
+
+namespace JsonApi\Routes\Courseware;
+
+use JsonApi\NonJsonApiController;
+use Courseware\Block;
+use Courseware\Clipboard;
+use Courseware\Container;
+use Courseware\StructuralElement;
+use JsonApi\Errors\AuthorizationFailedException;
+use JsonApi\Errors\BadRequestException;
+use JsonApi\Errors\RecordNotFoundException;
+use JsonApi\Errors\UnprocessableEntityException;
+use Psr\Http\Message\ResponseInterface as Response;
+use Psr\Http\Message\ServerRequestInterface as Request;
+
+/**
+ * 
+ *
+ * @author  Ron Lucke <lucke@elan-ev.de>
+ * @license GPL2 or any later version
+ *
+ * @since   Stud.IP 5.4
+ */
+
+class ClipboardsInsert extends NonJsonApiController
+{
+    public function __invoke(Request $request, Response $response, array $args)
+    {
+        $data = $request->getParsedBody()['data'];
+        $clipboard = Clipboard::find($args['id']);
+
+        if (!$clipboard) {
+            throw new RecordNotFoundException();
+        }
+
+        $user = $this->getUser($request);
+        $backup = json_decode($clipboard->backup);
+
+        if ($clipboard->object_type === 'courseware-blocks') {
+            $sectionIndex = $data['section'];
+            $container = \Courseware\Container::find($data['parent_id']);
+            if (!$container) {
+                throw new RecordNotFoundException();
+            }
+            if (!Authority::canCreateBlocks($user, $container)) {
+                throw new AuthorizationFailedException();
+            }
+            $object = Block::createFromData($user, $backup, $container, $sectionIndex);
+        }
+
+        if ($clipboard->object_type === 'courseware-containers') {
+            $element = \Courseware\StructuralElement::find($data['parent_id']);
+            if (!$element) {
+                throw new RecordNotFoundException();
+            }
+            if (!Authority::canCreateContainer($user, $element)) {
+                throw new AuthorizationFailedException();
+            }
+            $object = Container::createFromData($user, $backup, $element);
+        }
+
+        $response = $response->withHeader('Content-Type', 'application/json');
+        $response->getBody()->write((string) json_encode($object));
+
+        return $response;
+    }
+}
diff --git a/lib/classes/JsonApi/Routes/Courseware/ClipboardsShow.php b/lib/classes/JsonApi/Routes/Courseware/ClipboardsShow.php
new file mode 100644
index 0000000000000000000000000000000000000000..db1636769aa879b674f2bcad95f16fca37302326
--- /dev/null
+++ b/lib/classes/JsonApi/Routes/Courseware/ClipboardsShow.php
@@ -0,0 +1,46 @@
+<?php
+
+namespace JsonApi\Routes\Courseware;
+
+use Courseware\Clipboard;
+use JsonApi\Errors\AuthorizationFailedException;
+use JsonApi\Errors\RecordNotFoundException;
+use JsonApi\Schemas\Courseware\Clipboard as ClipboardSchema;
+use JsonApi\JsonApiController;
+use Psr\Http\Message\ResponseInterface as Response;
+use Psr\Http\Message\ServerRequestInterface as Request;
+
+/**
+ * Displays one clipboard.
+ * 
+ * @author  Ron Lucke <lucke@elan-ev.de>
+ * @license GPL2 or any later version
+ *
+ * @since   Stud.IP 5.4
+ */
+class ClipboardsShow extends JsonApiController
+{
+    protected $allowedIncludePaths = [
+        ClipboardSchema::REL_USER,
+    ];
+
+    /**
+     * @SuppressWarnings(PHPMD.UnusedFormalParameter)
+     * @param array $args
+     * @return Response
+     */
+    public function __invoke(Request $request, Response $response, $args)
+    {
+        /** @var ?\Courseware\Clipboard $resource */
+        $resource = Clipboard::find($args['id']);
+        if (!$resource) {
+            throw new RecordNotFoundException();
+        }
+
+        if (!Authority::canShowClipboard($this->getUser($request), $resource)) {
+            throw new AuthorizationFailedException();
+        }
+
+        return $this->getContentResponse($resource);
+    }
+}
\ No newline at end of file
diff --git a/lib/classes/JsonApi/Routes/Courseware/ClipboardsUpdate.php b/lib/classes/JsonApi/Routes/Courseware/ClipboardsUpdate.php
new file mode 100644
index 0000000000000000000000000000000000000000..d9c8c952f225af23404b977007cedc095792a29d
--- /dev/null
+++ b/lib/classes/JsonApi/Routes/Courseware/ClipboardsUpdate.php
@@ -0,0 +1,68 @@
+<?php
+
+namespace JsonApi\Routes\Courseware;
+
+use Courseware\Clipboard;
+use JsonApi\Errors\AuthorizationFailedException;
+use JsonApi\Errors\RecordNotFoundException;
+use JsonApi\JsonApiController;
+use JsonApi\Routes\ValidationTrait;
+use JsonApi\Schemas\Courseware\Unit as UnitSchema;
+use Psr\Http\Message\ResponseInterface as Response;
+use Psr\Http\Message\ServerRequestInterface as Request;
+
+/**
+ * Update one Clipboard.
+ * 
+ * @author  Ron Lucke <lucke@elan-ev.de>
+ * @license GPL2 or any later version
+ *
+ * @since   Stud.IP 5.4
+ */
+class ClipboardsUpdate extends JsonApiController
+{
+    use ValidationTrait;
+
+    /**
+     * @SuppressWarnings(PHPMD.UnusedFormalParameter)
+     */
+    public function __invoke(Request $request, Response $response, $args)
+    {
+        $resource = Clipboard::find($args['id']);
+        if (!$resource) {
+            throw new RecordNotFoundException();
+        }
+        $json = $this->validate($request, $resource);
+        $user = $this->getUser($request);
+        if (!Authority::canUpdateClipboard($user, $resource)) {
+            throw new AuthorizationFailedException();
+        }
+        $resource = $this->updateClipboard($user, $resource, $json);
+
+        return $this->getContentResponse($resource);
+    }
+
+    /**
+     * @SuppressWarnings(PHPMD.UnusedFormalParameters)
+     */
+    protected function validateResourceDocument($json, $resource)
+    {
+        if (!self::arrayHas($json, 'data')) {
+            return 'Missing `data` member at document´s top level.';
+        }
+    }
+
+    private function updateClipboard(\User $user, Clipboard $resource, array $json): Clipboard
+    {
+        if (self::arrayHas($json, 'data.attributes.name')) {
+            $resource->name = self::arrayGet($json, 'data.attributes.name');
+        }
+        if (self::arrayHas($json, 'data.attributes.description')) {
+            $resource->description = self::arrayGet($json, 'data.attributes.description');
+        }
+
+        $resource->store();
+
+        return $resource;
+    }
+}
diff --git a/lib/classes/JsonApi/Routes/Courseware/ContainersCopy.php b/lib/classes/JsonApi/Routes/Courseware/ContainersCopy.php
index cbef54c762e239b10d792d567390e1fcc3481427..eea3d5aa10df13d9cda4dca38afd159af774856f 100644
--- a/lib/classes/JsonApi/Routes/Courseware/ContainersCopy.php
+++ b/lib/classes/JsonApi/Routes/Courseware/ContainersCopy.php
@@ -29,7 +29,13 @@ class ContainersCopy extends NonJsonApiController
         $data = $request->getParsedBody()['data'];
 
         $container = \Courseware\Container::find($data['container']['id']);
+        if (!$container) {
+            throw new RecordNotFoundException();
+        }
         $element = \Courseware\StructuralElement::find($data['parent_id']);
+        if (!$element) {
+            throw new RecordNotFoundException();
+        }
         $user = $this->getUser($request);
         if (!Authority::canCreateContainer($user, $element) || !Authority::canUpdateContainer($user, $container)) {
             throw new AuthorizationFailedException();
diff --git a/lib/classes/JsonApi/Routes/Courseware/UsersClipboardsDelete.php b/lib/classes/JsonApi/Routes/Courseware/UsersClipboardsDelete.php
new file mode 100644
index 0000000000000000000000000000000000000000..38694f2fca0ba2d70fae694cfc2a717aaf97debb
--- /dev/null
+++ b/lib/classes/JsonApi/Routes/Courseware/UsersClipboardsDelete.php
@@ -0,0 +1,40 @@
+<?php
+
+namespace JsonApi\Routes\Courseware;
+
+use Courseware\Clipboard;
+use JsonApi\Errors\AuthorizationFailedException;
+use JsonApi\Errors\RecordNotFoundException;
+use JsonApi\JsonApiController;
+use Psr\Http\Message\ResponseInterface as Response;
+use Psr\Http\Message\ServerRequestInterface as Request;
+
+/**
+ * Displays all clipboards of one user.
+ * 
+ * @author  Ron Lucke <lucke@elan-ev.de>
+ * @license GPL2 or any later version
+ *
+ * @since   Stud.IP 5.4
+ */
+class UsersClipboardsDelete extends JsonApiController
+{
+    /**
+     * @SuppressWarnings(PHPMD.UnusedFormalParameter)
+     */
+    public function __invoke(Request $request, Response $response, $args)
+    {
+        $user = \User::find($args['id']);
+        if (!$user) {
+            throw new RecordNotFoundException();
+        }
+        $request_user = $this->getUser($request);
+        if (!Authority::canDeleteClipboardsOfAUser($request_user, $user)) {
+            throw new AuthorizationFailedException();
+        }
+
+        Clipboard::deleteUsersClipboards($user, $args['type']);
+
+        return  $this->getCodeResponse(204);
+    }
+}
diff --git a/lib/classes/JsonApi/Routes/Courseware/UsersClipboardsIndex.php b/lib/classes/JsonApi/Routes/Courseware/UsersClipboardsIndex.php
new file mode 100644
index 0000000000000000000000000000000000000000..6b98d01331e9befddb0c810dea5a5391343f016c
--- /dev/null
+++ b/lib/classes/JsonApi/Routes/Courseware/UsersClipboardsIndex.php
@@ -0,0 +1,50 @@
+<?php
+
+namespace JsonApi\Routes\Courseware;
+
+use Courseware\Clipboard;
+use JsonApi\Errors\AuthorizationFailedException;
+use JsonApi\Errors\RecordNotFoundException;
+use JsonApi\JsonApiController;
+use Psr\Http\Message\ResponseInterface as Response;
+use Psr\Http\Message\ServerRequestInterface as Request;
+
+/**
+ * Displays all clipboards of one user.
+ * 
+ * @author  Ron Lucke <lucke@elan-ev.de>
+ * @license GPL2 or any later version
+ *
+ * @since   Stud.IP 5.4
+ */
+class UsersClipboardsIndex extends JsonApiController
+{
+    use CoursewareInstancesHelper;
+
+    protected $allowedIncludePaths = [
+        'user',
+    ];
+
+    protected $allowedPagingParameters = ['offset', 'limit'];
+
+    /**
+     * @SuppressWarnings(PHPMD.UnusedFormalParameter)
+     */
+    public function __invoke(Request $request, Response $response, $args)
+    {
+        $user = \User::find($args['id']);
+        if (!$user) {
+            throw new RecordNotFoundException();
+        }
+        $request_user = $this->getUser($request);
+        if (!Authority::canIndexClipboardsOfAUser($request_user, $user)) {
+            throw new AuthorizationFailedException();
+        }
+
+        $resources = Clipboard::findUsersClipboards($user);
+        $total = count($resources);
+        [$offset, $limit] = $this->getOffsetAndLimit();
+
+        return $this->getPaginatedContentResponse(array_slice($resources, $offset, $limit), $total);
+    }
+}
diff --git a/lib/classes/JsonApi/SchemaMap.php b/lib/classes/JsonApi/SchemaMap.php
index 44b12d9639bf0445f815a89c0990b7333995e724..19d21f5648dae951cdbd0edf3b586724b672b0f4 100644
--- a/lib/classes/JsonApi/SchemaMap.php
+++ b/lib/classes/JsonApi/SchemaMap.php
@@ -54,6 +54,7 @@ class SchemaMap
             \Courseware\Block::class => Schemas\Courseware\Block::class,
             \Courseware\BlockComment::class => Schemas\Courseware\BlockComment::class,
             \Courseware\BlockFeedback::class => Schemas\Courseware\BlockFeedback::class,
+            \Courseware\Clipboard::class => Schemas\Courseware\Clipboard::class,
             \Courseware\Container::class => Schemas\Courseware\Container::class,
             \Courseware\Instance::class => Schemas\Courseware\Instance::class,
             \Courseware\StructuralElement::class => Schemas\Courseware\StructuralElement::class,
diff --git a/lib/classes/JsonApi/Schemas/Courseware/Clipboard.php b/lib/classes/JsonApi/Schemas/Courseware/Clipboard.php
new file mode 100644
index 0000000000000000000000000000000000000000..e65f1b108be5a9ce74b8330e462b6730ea1293cd
--- /dev/null
+++ b/lib/classes/JsonApi/Schemas/Courseware/Clipboard.php
@@ -0,0 +1,90 @@
+<?php
+
+namespace JsonApi\Schemas\Courseware;
+
+use JsonApi\Schemas\SchemaProvider;
+use Neomerx\JsonApi\Contracts\Schema\ContextInterface;
+use Neomerx\JsonApi\Schema\Link;
+
+class Clipboard extends SchemaProvider
+{
+    const TYPE = 'courseware-clipboards';
+
+    const REL_USER = 'user';
+    const REL_STRUCTURAL_ELEMENT = 'structural-element';
+    const REL_CONTAINER = 'container';
+    const REL_BLOCK = 'block';
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getId($resource): ?string
+    {
+        return $resource->id;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getAttributes($resource, ContextInterface $context): iterable
+    {
+        return [
+            'name' => (string) $resource->name,
+            'description' => (string) $resource->description,
+            'block-id' => (int) $resource->block_id,
+            'container-id' => (int) $resource->container_id,
+            'structural-element-id' => (int) $resource->structural_element_id,
+            'object-type' => (string) $resource->object_type,
+            'object-kind' => (string) $resource->object_kind,
+            'backup' => $resource->backup,
+            'mkdate'    => date('c', $resource->mkdate),
+            'chdate'    => date('c', $resource->chdate),
+        ];
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getRelationships($resource, ContextInterface $context): iterable
+    {
+        $relationships = [];
+
+        $relationships[self::REL_USER] = $resource->user
+            ? [
+                self::RELATIONSHIP_LINKS => [
+                    Link::RELATED => $this->createLinkToResource($resource->user),
+                ],
+                self::RELATIONSHIP_DATA => $resource->user,
+            ]
+            : [self::RELATIONSHIP_DATA => null];
+        
+        $relationships[self::REL_BLOCK] = $resource->block
+        ? [
+                self::RELATIONSHIP_LINKS => [
+                    Link::RELATED => $this->createLinkToResource($resource->block),
+                ],
+                self::RELATIONSHIP_DATA => $resource->block,
+            ]
+            : [self::RELATIONSHIP_DATA => null];
+
+        $relationships[self::REL_CONTAINER] = $resource->container
+            ? [
+                    self::RELATIONSHIP_LINKS => [
+                        Link::RELATED => $this->createLinkToResource($resource->container),
+                    ],
+                    self::RELATIONSHIP_DATA => $resource->container,
+                ]
+                : [self::RELATIONSHIP_DATA => null];
+
+        $relationships[self::REL_STRUCTURAL_ELEMENT] = $resource->structural_element
+            ? [
+                    self::RELATIONSHIP_LINKS => [
+                        Link::RELATED => $this->createLinkToResource($resource->structural_element),
+                    ],
+                    self::RELATIONSHIP_DATA => $resource->structural_element,
+                ]
+                : [self::RELATIONSHIP_DATA => null];
+
+        return $relationships;
+    }
+}
\ No newline at end of file
diff --git a/lib/models/Courseware/Block.php b/lib/models/Courseware/Block.php
index bd0361897dd65c93f11a903f1dc86d032f93f581..d311e0fc4911429bc8ff310f2b96451f929ef784 100644
--- a/lib/models/Courseware/Block.php
+++ b/lib/models/Courseware/Block.php
@@ -162,6 +162,25 @@ class Block extends \SimpleORMap implements \PrivacyObject
         return $user->getFullName();
     }
 
+    public function getClipboardBackup(): string
+    {
+        $block = [
+            'type' => 'courseware-blocks',
+            'id' => $this->id,
+            'attributes' => [
+                'position'=> $this->position,
+                'block-type'=> $this->type->getType(),
+                'title'=> $this->type->getTitle(),
+                'visible'=> $this->visible,
+                'payload'=> $this->type->getPayload(),
+                'mkdate'=> $this->mkdate,
+                'chdate'=> $this->chdate
+            ]
+        ];
+
+        return json_encode($block, true);
+    }
+
     /**
      * Copies this block into another container such that the given user is the owner of the copy.
      *
@@ -170,13 +189,13 @@ class Block extends \SimpleORMap implements \PrivacyObject
      *
      * @return Block the copy of this block
      */
-    public function copy(User $user, Container $container): Block
+    public function copy(User $user, Container $container, $sectionIndex = null): Block
     {
         /** @var StructuralElement $struct */
-        $struct = StructuralElement::find($container->structural_element_id);
+        $struct = $container->structural_element;
         $rangeId = $struct->getRangeId();
 
-        $block = self::build([
+        $block = self::create([
             'container_id' => $container->id,
             'owner_id' => $user->id,
             'editor_id' => $user->id,
@@ -187,10 +206,34 @@ class Block extends \SimpleORMap implements \PrivacyObject
             'visible' => 1,
         ]);
 
+        //update Container payload
+        $container->type->addBlock($block, $sectionIndex);
+        $container->store();
+
+        return $block;
+    }
+
+    public static function createFromData(User $user, $data, Container $container, $sectionIndex = null): Block
+    {
+        $struct = $container->structural_element;
+        $rangeId = $struct->getRangeId();
+
+        $block = self::create([
+            'container_id' => $container->id,
+            'owner_id' => $user->id,
+            'editor_id' => $user->id,
+            'edit_blocker_id' => null,
+            'position' => $container->countBlocks(),
+            'block_type' => $data->attributes->{'block-type'},
+            'visible' => 1,
+        ]);
+
+        $dataPayload = (array)$data->attributes->payload;
+        $block->payload = json_encode($block->type->copyPayload('', $dataPayload), true);
         $block->store();
 
         //update Container payload
-        $container->type->addBlock($block);
+        $container->type->addBlock($block, $sectionIndex);
         $container->store();
 
         return $block;
diff --git a/lib/models/Courseware/BlockTypes/Audio.php b/lib/models/Courseware/BlockTypes/Audio.php
index dd2588a1e5ea301f8a5697591528790a2ba24435..7196f209a7449b6a17a0f56f79094344ad9e96b2 100644
--- a/lib/models/Courseware/BlockTypes/Audio.php
+++ b/lib/models/Courseware/BlockTypes/Audio.php
@@ -74,11 +74,13 @@ class Audio extends BlockType
         }
     }
 
-    public function copyPayload(string $rangeId = ''): array
+    public function copyPayload(string $rangeId = '', $payload = null): array
     {
-        $payload = $this->getPayload();
+        if (!$payload) {
+            $payload = $this->getPayload();
+        }
 
-        if ('' != $payload['file_id']) {
+        if (!empty($payload['file_id'])) {
             $payload['file_id'] = $this->copyFileById($payload['file_id'], $rangeId);
         }
 
diff --git a/lib/models/Courseware/BlockTypes/BeforeAfter.php b/lib/models/Courseware/BlockTypes/BeforeAfter.php
index b4f33ca9c0854dbefc6eea994887f764547330c3..5ad772cac72be9b2548950fcfffc5495222a317e 100644
--- a/lib/models/Courseware/BlockTypes/BeforeAfter.php
+++ b/lib/models/Courseware/BlockTypes/BeforeAfter.php
@@ -75,11 +75,13 @@ class BeforeAfter extends BlockType
         return $files;
     }
 
-    public function copyPayload(string $rangeId = ''): array
+    public function copyPayload(string $rangeId = '', $payload = null): array
     {
-        $payload = $this->getPayload();
+        if (!$payload) {
+            $payload = $this->getPayload();
+        }
 
-        if ('' != $payload['before_file_id']) {
+        if (!empty($payload['before_file_id'])) {
             $payload['before_file_id'] = $this->copyFileById($payload['before_file_id'], $rangeId);
         }
 
diff --git a/lib/models/Courseware/BlockTypes/BlockType.php b/lib/models/Courseware/BlockTypes/BlockType.php
index 5c21c01bb983036fc5d59014a1c91d4de648590d..8c03c8808326022e82075a28fe89dee2c566814d 100644
--- a/lib/models/Courseware/BlockTypes/BlockType.php
+++ b/lib/models/Courseware/BlockTypes/BlockType.php
@@ -232,9 +232,9 @@ abstract class BlockType
     }
 
     // TODO: (tgloeggl) DocBlock ergänzen
-    public function copyPayload(string $rangeId = ''): array
+    public function copyPayload(string $rangeId = '', $payload = null): array
     {
-        return $this->getPayload();
+        return $payload ?: $this->getPayload();
     }
 
     /**
@@ -339,6 +339,10 @@ abstract class BlockType
             return $file_map[$fileId];
         }
 
+        if ($rangeId === '') {
+            $rangeId = $this->block->container->structural_element->range_id;
+        }
+
         $user = \User::findCurrent();
         if ($file_ref = \FileRef::find($fileId)) {
             $copiedFile = \FileManager::copyFile(
@@ -375,6 +379,10 @@ abstract class BlockType
             return $folder_map[$folderId];
         }
 
+        if ($rangeId === '') {
+            $rangeId = $this->block->container->structural_element->range_id;
+        }
+
         $user = \User::findCurrent();
         $destinationFolder = $this->getDestinationFolder($user, $rangeId);
         if ($sourceFolder = \Folder::find($folderId)) {
diff --git a/lib/models/Courseware/BlockTypes/Canvas.php b/lib/models/Courseware/BlockTypes/Canvas.php
index e7b14e99665bd9ed3745aae213e7d8b41d2ff82e..34dc431b5b0f9e123f60f1571ea9f3b6e0f820ad 100644
--- a/lib/models/Courseware/BlockTypes/Canvas.php
+++ b/lib/models/Courseware/BlockTypes/Canvas.php
@@ -65,11 +65,13 @@ class Canvas extends BlockType
         return $files;
     }
 
-    public function copyPayload(string $rangeId = ''): array
+    public function copyPayload(string $rangeId = '', $payload = null): array
     {
-        $payload = $this->getPayload();
+        if (!$payload) {
+            $payload = $this->getPayload();
+        }
 
-        if ('' != $payload['file_id']) {
+        if (!empty($payload['file_id'])) {
             $payload['file_id'] = $this->copyFileById($payload['file_id'], $rangeId);
         }
 
diff --git a/lib/models/Courseware/BlockTypes/DialogCards.php b/lib/models/Courseware/BlockTypes/DialogCards.php
index 74b843cbe268886314886aa4ccae4e1514b6ad8f..62eea5fb3f99857b1d49fdf603b5d50abcd366f9 100644
--- a/lib/models/Courseware/BlockTypes/DialogCards.php
+++ b/lib/models/Courseware/BlockTypes/DialogCards.php
@@ -80,9 +80,12 @@ class DialogCards extends BlockType
         return $files;
     }
 
-    public function copyPayload(string $rangeId = ''): array
+    public function copyPayload(string $rangeId = '', $payload = null): array
     {
-        $payload = $this->getPayload();
+        if (!$payload) {
+            $payload = $this->getPayload();
+        }
+
         foreach ($payload['cards'] as &$card) {
             if ('' != $card['front_file_id']) {
                 $card['front_file_id'] = $this->copyFileById($card['front_file_id'], $rangeId);
diff --git a/lib/models/Courseware/BlockTypes/Document.php b/lib/models/Courseware/BlockTypes/Document.php
index db1ba6ee179fb0dfcc6ccc4d6808a2a155cb8eef..20950089c5bca208e4a8c7a361e17dde305e27da 100644
--- a/lib/models/Courseware/BlockTypes/Document.php
+++ b/lib/models/Courseware/BlockTypes/Document.php
@@ -67,11 +67,13 @@ class Document extends BlockType
         return $files;
     }
 
-    public function copyPayload(string $rangeId = ''): array
+    public function copyPayload(string $rangeId = '', $payload = null): array
     {
-        $payload = $this->getPayload();
+        if (!$payload) {
+            $payload = $this->getPayload();
+        }
 
-        if ('' != $payload['file_id']) {
+        if (!empty($payload['file_id'])) {
             $payload['file_id'] = $this->copyFileById($payload['file_id'], $rangeId);
         }
 
diff --git a/lib/models/Courseware/BlockTypes/Download.php b/lib/models/Courseware/BlockTypes/Download.php
index d736009d1815230ec4a52367b32a8ff756039adb..f53c8c7f288331552eeb52de204701c16fa17bfc 100644
--- a/lib/models/Courseware/BlockTypes/Download.php
+++ b/lib/models/Courseware/BlockTypes/Download.php
@@ -61,11 +61,13 @@ class Download extends BlockType
         return $files;
     }
 
-    public function copyPayload(string $rangeId = ''): array
+    public function copyPayload(string $rangeId = '', $payload = null): array
     {
-        $payload = $this->getPayload();
+        if (!$payload) {
+            $payload = $this->getPayload();
+        }
 
-        if ('' != $payload['file_id']) {
+        if (!empty($payload['file_id'])) {
             $payload['file_id'] = $this->copyFileById($payload['file_id'], $rangeId);
         }
 
diff --git a/lib/models/Courseware/BlockTypes/Folder.php b/lib/models/Courseware/BlockTypes/Folder.php
index bc34f854f24fc547829deeed13a9e04058dca5c8..d5156b6298d04d63883d9acd3833320213477b07 100644
--- a/lib/models/Courseware/BlockTypes/Folder.php
+++ b/lib/models/Courseware/BlockTypes/Folder.php
@@ -104,11 +104,13 @@ class Folder extends BlockType
         return \FileRef::findByFolder_id($payload['folder_id']);
     }
 
-    public function copyPayload(string $rangeId = ''): array
+    public function copyPayload(string $rangeId = '', $payload = null): array
     {
-        $payload = $this->getPayload();
+        if (!$payload) {
+            $payload = $this->getPayload();
+        }
 
-        if ('' != $payload['folder_id']) {
+        if (!empty($payload['folder_id'])) {
             $payload['folder_id'] = $this->copyFolderById($payload['folder_id'], $rangeId);
         }
 
diff --git a/lib/models/Courseware/BlockTypes/Gallery.php b/lib/models/Courseware/BlockTypes/Gallery.php
index 5f9bb0b6d56d3c8c8d5acc5f503f09306cef3f8d..1d931ea99519104d24a1a958f773bf6c44855533 100644
--- a/lib/models/Courseware/BlockTypes/Gallery.php
+++ b/lib/models/Courseware/BlockTypes/Gallery.php
@@ -123,10 +123,13 @@ class Gallery extends BlockType
         return $files;
     }
 
-    public function copyPayload(string $rangeId = ''): array
+    public function copyPayload(string $rangeId = '', $payload = null): array
     {
-        $payload = $this->getPayload();
-        if ('' != $payload['folder_id']) {
+        if (!$payload) {
+            $payload = $this->getPayload();
+        }
+
+        if (!empty($payload['folder_id'])) {
             $payload['folder_id'] = $this->copyFolderById($payload['folder_id'], $rangeId);
         }
 
diff --git a/lib/models/Courseware/BlockTypes/Headline.php b/lib/models/Courseware/BlockTypes/Headline.php
index 0c821386b82dee191489486bb10b5e2213596bc6..eace9465685f2a23c57fd76dcc745d23c4aea9f2 100644
--- a/lib/models/Courseware/BlockTypes/Headline.php
+++ b/lib/models/Courseware/BlockTypes/Headline.php
@@ -73,11 +73,13 @@ class Headline extends BlockType
         return $files;
     }
 
-    public function copyPayload(string $rangeId = ''): array
+    public function copyPayload(string $rangeId = '', $payload = null): array
     {
-        $payload = $this->getPayload();
+        if (!$payload) {
+            $payload = $this->getPayload();
+        }
 
-        if ('' != $payload['background_image_id']) {
+        if (!empty($payload['background_image_id'])) {
             $payload['background_image_id'] = $this->copyFileById($payload['background_image_id'], $rangeId);
             $payload['background_image'] = '';
         }
diff --git a/lib/models/Courseware/BlockTypes/ImageMap.php b/lib/models/Courseware/BlockTypes/ImageMap.php
index e275c46254ef90dd66b8e008977e41b1de9f8aa1..3f8f37b2b61c346c2b3ba078c8624483d132e47c 100644
--- a/lib/models/Courseware/BlockTypes/ImageMap.php
+++ b/lib/models/Courseware/BlockTypes/ImageMap.php
@@ -55,11 +55,13 @@ class ImageMap extends BlockType
         return $files;
     }
 
-    public function copyPayload(string $rangeId = ''): array
+    public function copyPayload(string $rangeId = '', $payload = null): array
     {
-        $payload = $this->getPayload();
+        if (!$payload) {
+            $payload = $this->getPayload();
+        }
 
-        if ('' != $payload['file_id']) {
+        if (!empty($payload['file_id'])) {
             $payload['file_id'] = $this->copyFileById($payload['file_id'], $rangeId);
         }
 
diff --git a/lib/models/Courseware/BlockTypes/Text.php b/lib/models/Courseware/BlockTypes/Text.php
index 0a8921932353c36ada25a896e5358cf791594a5c..e918c6a44e5ae1e68707adbfd732559a2c2f794e 100644
--- a/lib/models/Courseware/BlockTypes/Text.php
+++ b/lib/models/Courseware/BlockTypes/Text.php
@@ -88,9 +88,11 @@ class Text extends BlockType
         return $files;
     }
 
-    public function copyPayload(string $rangeId = ''): array
+    public function copyPayload(string $rangeId = '', $payload = null): array
     {
-        $payload = $this->getPayload();
+        if (!$payload) {
+            $payload = $this->getPayload();
+        }
         $document = new \DOMDocument();
 
         if ($payload['text']) {
diff --git a/lib/models/Courseware/BlockTypes/Video.php b/lib/models/Courseware/BlockTypes/Video.php
index 431f69b68e6db77e87980ee7716cfcd03bd67345..a32ca668f43dcbe7d4a8115e40bd407bad7099a7 100644
--- a/lib/models/Courseware/BlockTypes/Video.php
+++ b/lib/models/Courseware/BlockTypes/Video.php
@@ -68,11 +68,13 @@ class Video extends BlockType
     }
 
 
-    public function copyPayload(string $rangeId = ''): array
+    public function copyPayload(string $rangeId = '', $payload = null): array
     {
-        $payload = $this->getPayload();
+        if (!$payload) {
+            $payload = $this->getPayload();
+        }
 
-        if ('' != $payload['file_id']) {
+        if (!empty($payload['file_id'])) {
             $payload['file_id'] = $this->copyFileById($payload['file_id'], $rangeId);
         }
 
diff --git a/lib/models/Courseware/Clipboard.php b/lib/models/Courseware/Clipboard.php
new file mode 100644
index 0000000000000000000000000000000000000000..7a42d33bf935589a4f388c89758968aeb6b0eb85
--- /dev/null
+++ b/lib/models/Courseware/Clipboard.php
@@ -0,0 +1,78 @@
+<?php
+
+namespace Courseware;
+
+use User;
+
+/**
+ * Courseware's clipboards.
+ *
+ * @author  Ron Lucke <lucke@elan-ev.de>
+ * @license GPL2 or any later version
+ *
+ * @since   Stud.IP 5.4
+ * 
+ * @property int                            $id                     database column
+ * @property string                         $user_id                database column
+ * @property string                         $name                   database column
+ * @property string                         $description            database column
+ * @property int                            $block_id               database column
+ * @property int                            $container_id           database column
+ * @property int                            $structural_element_id  database column
+ * @property string                         $object_type            database column
+ * @property string                         $object_kind            database column
+ * @property string                         $backup                 database column
+ * @property int                            $mkdate                 database column
+ * @property int                            $chdate                 database column
+ * @property \User                          $user                   belongs_to User
+ * @property Block $block belongs_to Block
+ * @property Container $container belongs_to Container
+ * @property StructuralElement $structural_element belongs_to StructuralElement
+ * @SuppressWarnings(PHPMD.TooManyPublicMethods)
+ * @SuppressWarnings(PHPMD.ExcessiveClassComplexity)
+ */
+
+class Clipboard extends \SimpleORMap
+{
+    protected static function configure($config = [])
+    {
+        $config['db_table'] = 'cw_clipboards';
+
+        $config['belongs_to']['user'] = [
+            'class_name' => User::class,
+            'foreign_key' => 'user_id',
+            'on_delete' => 'delete',
+        ];
+
+        $config['belongs_to']['block'] = [
+            'class_name' => Block::class,
+            'foreign_key' => 'block_id',
+        ];
+
+        $config['belongs_to']['container'] = [
+            'class_name' => Container::class,
+            'foreign_key' => 'container_id',
+        ];
+    
+        $config['belongs_to']['structural_element'] = [
+            'class_name' => StructuralElement::class,
+            'foreign_key' => 'structural_element_id',
+        ];
+
+        parent::configure($config);
+    }
+
+    public static function findUsersClipboards($user): array
+    {
+        return self::findBySQL('user_id = ?', [$user->id]);
+    }
+
+    public static function deleteUsersClipboards($user, $type): void
+    {
+        self::deleteBySQL(
+            'user_id = ? AND object_type = ?',
+            [$user->id, $type]
+        );
+    }
+
+}
diff --git a/lib/models/Courseware/Container.php b/lib/models/Courseware/Container.php
index 082a9a0176904f136b2ef46c17180b190e11e9ad..5ce8f29a7d96244930edebf7b4377f58d7ab12a7 100644
--- a/lib/models/Courseware/Container.php
+++ b/lib/models/Courseware/Container.php
@@ -98,6 +98,33 @@ class Container extends \SimpleORMap implements \PrivacyObject
         return Block::countBySql('container_id = ?', [$this->id]);
     }
 
+    public function getClipboardBackup(): string
+    {
+        $container = [
+            'type' => 'courseware-containers',
+            'id' => $this->id,
+            'attributes' => [
+                'position' => $this->position,
+                'site' => $this->site,
+                'container-type' => $this->type->getType(),
+                'title' => $this->type->getTitle(),
+                'visible' => $this->visible,
+                'payload' => $this->type->getPayload(),
+                'mkdate' => $this->mkdate,
+                'chdate' => $this->chdate
+            ],
+            'blocks' => $this->getClipboardBackupBlocks()
+        ];
+        return json_encode($container, true);
+    }
+
+    public function getClipboardBackupBlocks(): array
+    {
+        return $this->blocks->map(function (Block $block) {
+            return json_decode($block->getClipboardBackup());
+        });
+    }
+
     /**
      * Copies this block into another structural element such that the given user is the owner of the copy.
      *
@@ -108,7 +135,7 @@ class Container extends \SimpleORMap implements \PrivacyObject
      */
     public function copy(User $user, StructuralElement $element): array
     {
-        $container = self::build([
+        $container = self::create([
             'structural_element_id' => $element->id,
             'owner_id' => $user->id,
             'editor_id' => $user->id,
@@ -118,8 +145,6 @@ class Container extends \SimpleORMap implements \PrivacyObject
             'payload' => $this['payload'],
         ]);
 
-        $container->store();
-
         list($blockMapIds, $blockMapObjs) = $this->copyBlocks($user, $container);
 
         $container['payload'] = $container->type->copyPayload($blockMapIds);
@@ -160,4 +185,35 @@ class Container extends \SimpleORMap implements \PrivacyObject
         }
         
     }
+
+    public static function createFromData(User $user, $data, StructuralElement $element): Container
+    {
+        $container = self::create([
+            'structural_element_id' => $element->id,
+            'owner_id' => $user->id,
+            'editor_id' => $user->id,
+            'edit_blocker_id' => null,
+            'position' => $element->countContainers(),
+            'container_type' => $data->attributes->{'container-type'},
+            'payload' => json_encode($data->attributes->payload),
+        ]);
+
+        $blockMap = self::createBlocksFromData($user, $container, $data);
+        $container['payload'] = $container->type->copyPayload($blockMap);
+        $container->store();
+
+        return $container;
+    }
+
+    private static function createBlocksFromData($user, $container, $data): array
+    {
+        $blockMap = [];
+
+        foreach ($data->blocks as $block) {
+            $newBlock = Block::createFromData($user, $block, $container);
+            $blockMap[$block->id] = $newBlock->id;
+        }
+
+        return $blockMap;
+    }
 }
diff --git a/lib/models/Courseware/ContainerTypes/AccordionContainer.php b/lib/models/Courseware/ContainerTypes/AccordionContainer.php
index 832520401aa807626f1db1e5df5ed5b09cf90f31..185b6d113d8b26eac1eb9dc8587ab79ac359f247 100644
--- a/lib/models/Courseware/ContainerTypes/AccordionContainer.php
+++ b/lib/models/Courseware/ContainerTypes/AccordionContainer.php
@@ -41,11 +41,15 @@ class AccordionContainer extends ContainerType
         ];
     }
 
-    public function addBlock($block): void
+    public function addBlock($block, $sectionIndex = null): void
     {
         $payload = $this->getPayload();
 
-        array_push($payload['sections'][count($payload['sections']) - 1]['blocks'], $block->id);
+        if ($sectionIndex !== null) {
+            array_push($payload['sections'][$sectionIndex]['blocks'], $block->id);
+        } else {
+            array_push($payload['sections'][count($payload['sections']) - 1]['blocks'], $block->id);
+        }
 
         $this->setPayload($payload);
     }
diff --git a/lib/models/Courseware/ContainerTypes/ContainerType.php b/lib/models/Courseware/ContainerTypes/ContainerType.php
index dbe6df255b546369fcc50fab1a3e1c0d230e91e3..6cbcdf964a138a9c58f1dcfc9595fb02cdee5bcd 100644
--- a/lib/models/Courseware/ContainerTypes/ContainerType.php
+++ b/lib/models/Courseware/ContainerTypes/ContainerType.php
@@ -58,7 +58,7 @@ abstract class ContainerType
      *
      *  @param /Courseware/Block $block - block to be added to the container
      */
-    abstract public function addBlock($block): void;
+    abstract public function addBlock($block, $sectionIndex): void;
 
     /**
      * Returns all known types of containers: core types and plugin types as well.
@@ -197,8 +197,9 @@ abstract class ContainerType
 
         foreach ($payload['sections'] as &$section) {
             foreach ($section['blocks'] as &$block) {
-                $block = $block_map[$block] ?? null;
+                $block = strval($block_map[$block]) ?? null;
             }
+            $section['blocks'] = array_filter($section['blocks']);
         }
 
         return $payload;
diff --git a/lib/models/Courseware/ContainerTypes/ListContainer.php b/lib/models/Courseware/ContainerTypes/ListContainer.php
index 4918271a40e322e5aafb03748adc276159d49570..b98d8a1aaf9aa57c7cc60e54b19bba43016bf54b 100644
--- a/lib/models/Courseware/ContainerTypes/ListContainer.php
+++ b/lib/models/Courseware/ContainerTypes/ListContainer.php
@@ -41,7 +41,7 @@ class ListContainer extends ContainerType
         ];
     }
 
-    public function addBlock($block): void
+    public function addBlock($block, $sectionIndex = null): void
     {
         $payload = $this->getPayload();
 
diff --git a/lib/models/Courseware/ContainerTypes/TabsContainer.php b/lib/models/Courseware/ContainerTypes/TabsContainer.php
index b884bbb67ee0a745137d01d80cbf3495b641721c..01f3d451043a3d79148745bfaee3a0a9a566a436 100644
--- a/lib/models/Courseware/ContainerTypes/TabsContainer.php
+++ b/lib/models/Courseware/ContainerTypes/TabsContainer.php
@@ -42,11 +42,14 @@ class TabsContainer extends ContainerType
         ];
     }
 
-    public function addBlock($block): void
+    public function addBlock($block, $sectionIndex = null): void
     {
         $payload = $this->getPayload();
-
-        array_push($payload['sections'][count($payload['sections']) - 1]['blocks'], $block->id);
+        if ($sectionIndex !== null) {
+            array_push($payload['sections'][$sectionIndex]['blocks'], $block->id);
+        } else {
+            array_push($payload['sections'][count($payload['sections']) - 1]['blocks'], $block->id);
+        }
 
         $this->setPayload($payload);
     }
diff --git a/lib/models/Courseware/StructuralElement.php b/lib/models/Courseware/StructuralElement.php
index fabfd8465e6638751ae88ff7381767a0e6f2c340..a4f5cf1f8f0e4ef8fea8f6605a15751e1ef07796 100644
--- a/lib/models/Courseware/StructuralElement.php
+++ b/lib/models/Courseware/StructuralElement.php
@@ -769,6 +769,12 @@ SQL;
         return $this->image ? $this->image->getDownloadURL() : null;
     }
 
+    public static function getClipboardBackup(): string
+    {
+        //TODO
+        return '';
+    }
+
     /**
      * Copies this instance into another course oder users contents.
      *
diff --git a/resources/assets/javascripts/lib/actionmenu.js b/resources/assets/javascripts/lib/actionmenu.js
index 8acf80cfa3207c8df679f1e82ff95848ad4b88ba..42e5e783aa1319c256da4d1e8b3218db2f2c2954 100644
--- a/resources/assets/javascripts/lib/actionmenu.js
+++ b/resources/assets/javascripts/lib/actionmenu.js
@@ -117,9 +117,10 @@ class ActionMenu {
         this.content = $('.action-menu-content', element);
         this.is_reversed = reversed;
         this.is_open = false;
+        const additionalClasses = Object.values({ ...this.element[0].classList }).filter((item) => item != 'action-menu');
         const menu_width  = this.content.width();
         const menu_height = this.content.height();
-
+        
         // Reposition the menu?
         if (position) {
             const form = this.element.closest('form');
@@ -135,6 +136,7 @@ class ActionMenu {
                 $('.action-menu-icon', element).clone().data('action-menu-element', element).prependTo(this.menu);
 
                 this.menu
+                    .addClass(additionalClasses.join(' '))
                     .offset(this.element.offset())
                     .appendTo(breakpoint);
 
diff --git a/resources/assets/stylesheets/scss/buttons.scss b/resources/assets/stylesheets/scss/buttons.scss
index ef819817c0e207401fc898f5deb53cc6446368a9..659a26a0a5b4ef7bb2b4ad64f1205978f4dcdc16 100644
--- a/resources/assets/stylesheets/scss/buttons.scss
+++ b/resources/assets/stylesheets/scss/buttons.scss
@@ -109,6 +109,9 @@ button.button {
 .button.arr_left {
     @include button-with-icon(arr_1left, clickable, info_alt);
 }
+.button.refresh {
+    @include button-with-icon(refresh, clickable, info_alt);
+}
 .button.arr_right {
     @include button-with-icon(arr_1right, clickable, info_alt);
     &::before {
diff --git a/resources/assets/stylesheets/scss/courseware.scss b/resources/assets/stylesheets/scss/courseware.scss
index 964d1bf573477031e0c8eb0d52e4e000fe31753c..a4508514d0c9bb9e6a113a0e5d9e6ce25330a5cd 100644
--- a/resources/assets/stylesheets/scss/courseware.scss
+++ b/resources/assets/stylesheets/scss/courseware.scss
@@ -1920,7 +1920,6 @@ b l o c k a d d e r
         &:hover {
             border-color: var(--base-color);
         }
-
         .cw-blockadder-item {
             padding: 64px 10px 4px 10px;
             @include background-icon(unit-test, clickable, 48);
@@ -1933,7 +1932,7 @@ b l o c k a d d e r
                     @include background-icon($icon, clickable, 48);
                 }
             }
-        
+            .cw-clipboard-item-title,
             .cw-blockadder-item-title {
                 display: inline-block;
                 font-weight: 600;
@@ -1954,7 +1953,6 @@ b l o c k a d d e r
     }
 }
 
-
 .cw-block-adder-area {
     background-color: $white;
     border: solid thin $content-color-40;
@@ -2074,6 +2072,63 @@ b l o c k a d d e r
         }
     }
 }
+.cw-element-inserter-wrapper {
+    display: flex;
+    flex-wrap: wrap;
+    justify-content: space-between;
+}
+
+
+.cw-clipboard-item-wrapper {
+    display: flex;
+    width: calc(50% - 4px);
+    border: solid thin var(--content-color-40);
+    margin-bottom: 4px;
+
+    &:hover {
+        border-color: var(--base-color);
+    }
+
+    .cw-clipboard-item {
+        width: 207px;
+        padding: 64px 10px 4px 10px;
+        @include background-icon(unit-test, clickable, 48);
+        background-position: 10px 10px;
+        background-repeat: no-repeat;
+        cursor: pointer;
+        background-color: var(--white);
+        border: none;
+        text-align: left;
+        color: var(--base-color);
+
+        @each $item, $icon in $blockadder-items {
+            &.cw-clipboard-item-#{$item} {
+                @include background-icon($icon, clickable, 48);
+            }
+        }
+        @each $item, $icon in $containeradder-items {
+            &.cw-clipboard-item-#{$item} {
+                @include background-icon($icon, clickable, 48);
+            }
+        }
+    
+        .cw-clipboard-item-title {
+            display: inline-block;
+            font-weight: 600;
+            margin-bottom: 2px;
+        }
+
+    }
+    .cw-clipboard-item-action-menu-wrapper {
+        padding: 8px;
+    }
+}
+.action-menu.is-open,
+.action-menu-wrapper.is-open {
+    &.cw-clipboard-item-action-menu {
+        z-index: 42;
+    }
+}
 
 /* * * * * * * * * * * * *
 b l o c k a d d e r  e n d
diff --git a/resources/vue/components/courseware/CoursewareBlockActions.vue b/resources/vue/components/courseware/CoursewareBlockActions.vue
index 031e3aa4d99cfbeed1c49cc0f38f569afa814e26..2b6252789e8539d662abccdc32badcad3eb2b89b 100644
--- a/resources/vue/components/courseware/CoursewareBlockActions.vue
+++ b/resources/vue/components/courseware/CoursewareBlockActions.vue
@@ -8,6 +8,7 @@
             @showInfo="showInfo"
             @deleteBlock="deleteBlock"
             @removeLock="removeLock"
+            @copyToClipboard="copyToClipboard"
         />
     </div>
 </template>
@@ -53,7 +54,7 @@ export default {
                     if (!this.blocked) {
                         menuItems.push({ id: 1, label: this.$gettext('Block bearbeiten'), icon: 'edit', emit: 'editBlock' });
                         menuItems.push({
-                            id: 2,
+                            id: 3,
                             label: this.block.attributes.visible
                                 ? this.$gettext('unsichtbar setzen')
                                 : this.$gettext('sichtbar setzen'),
@@ -77,6 +78,12 @@ export default {
                             emit: 'deleteBlock' 
                         });
                     }
+                    menuItems.push({
+                        id: 2,
+                        label: this.$gettext('Block merken'),
+                        icon: 'clipboard',
+                        emit: 'copyToClipboard'
+                    });
                     menuItems.push({
                         id: 7,
                         label: this.$gettext('Informationen zum Block'),
@@ -135,6 +142,9 @@ export default {
         },
         removeLock() {
             this.$emit('removeLock');
+        },
+        copyToClipboard() {
+            this.$emit('copyToClipboard');
         }
     },
 };
diff --git a/resources/vue/components/courseware/CoursewareClipboardItem.vue b/resources/vue/components/courseware/CoursewareClipboardItem.vue
new file mode 100644
index 0000000000000000000000000000000000000000..aa975c8b46ce33ac58f7211914756899b067f8aa
--- /dev/null
+++ b/resources/vue/components/courseware/CoursewareClipboardItem.vue
@@ -0,0 +1,246 @@
+<template>
+    <div class="cw-clipboard-item-wrapper">
+        <button class="cw-clipboard-item" :class="['cw-clipboard-item-' + kind]" @click.prevent="insertItem">
+            <header class="sr-only">
+                {{ srTitle }}
+            </header>
+            <header class="cw-clipboard-item-title" aria-hidden="true">
+                {{ name }}
+            </header>
+            <p class="cw-clipboard-item-description">
+                {{ description }}
+            </p>
+        </button>
+        <div class="cw-clipboard-item-action-menu-wrapper">
+            <studip-action-menu
+                class="cw-clipboard-item-action-menu"
+                :items="menuItems"
+                :context="name"
+                @insertItemCopy="insertItemCopy"
+                @editItem="showEditItem"
+                @deleteItem="deleteItem"
+            />
+        </div>
+        <studip-dialog
+            v-if="showEditDialog"
+            :title="$gettext('Umbenennen')"
+            :confirmText="$gettext('Speichern')"
+            confirmClass="accept"
+            :closeText="$gettext('Abbrechen')"
+            closeClass="cancel"
+            height="360"
+            width="500"
+            @close="closeEditItem"
+            @confirm="storeItem"
+        >
+            <template v-slot:dialogContent>
+                <form class="default" @submit.prevent="">
+                    <label>
+                        {{ $gettext('Titel') }}
+                        <input type="text" v-model="currentClipboard.attributes.name" />
+                    </label>
+                    <label>
+                        {{ $gettext('Beschreibung') }}
+                        <textarea v-model="currentClipboard.attributes.description"></textarea>
+                    </label>
+                </form>
+            </template>
+        </studip-dialog>
+    </div>
+</template>
+
+<script>
+import { mapActions, mapGetters } from 'vuex';
+
+export default {
+    name: 'courseware-clipboard-item',
+    components: {},
+    props: {
+        clipboard: Object,
+    },
+    data() {
+        return {
+            showEditDialog: false,
+            currentClipboard: null,
+
+            text: {
+                errorMessage: this.$gettext('Es ist ein Fehler aufgetreten.'),
+                positionWarning: this.$gettext(
+                    'Bitte wählen Sie einen Ort aus, an dem der Block eingefügt werden soll.'
+                ),
+                blockSuccess: this.$gettext('Der Block wurde erfolgreich eingefügt.'),
+                containerSuccess: this.$gettext('Der Abschnitt wurde erfolgreich eingefügt.'),
+            },
+        };
+    },
+    computed: {
+        ...mapGetters({
+            blockAdder: 'blockAdder',
+            currentElement: 'currentElement',
+        }),
+        name() {
+            return this.clipboard.attributes.name;
+        },
+        description() {
+            return this.clipboard.attributes.description;
+        },
+        isBlock() {
+            return this.clipboard.attributes['object-type'] === 'courseware-blocks';
+        },
+        kind() {
+            return this.clipboard.attributes['object-kind'];
+        },
+        blockId() {
+            return this.clipboard.attributes['block-id'];
+        },
+        blockNotFound() {
+            return this.clipboard.relationships.block.data === null;
+        },
+        containerId() {
+            return this.clipboard.attributes['container-id'];
+        },
+        containerNotFound() {
+            return this.clipboard.relationships.container.data === null;
+        },
+        itemNotFound() {
+            if (this.isBlock) {
+                return this.blockNotFound;
+            }
+
+            return this.containerNotFound;
+        },
+        menuItems() {
+            let menuItems = [];
+            if (!this.itemNotFound) {
+                menuItems.push({
+                    id: 1,
+                    label: this.$gettext('Kopie des aktuellen Stands einfügen'),
+                    icon: 'copy',
+                    emit: 'insertItemCopy',
+                });
+            }
+            menuItems.push({ id: 2, label: this.$gettext('Umbenennen'), icon: 'edit', emit: 'editItem' });
+            menuItems.push({ id: 3, label: this.$gettext('Löschen'), icon: 'trash', emit: 'deleteItem' });
+
+            menuItems.sort((a, b) => a.id - b.id);
+            return menuItems;
+        },
+        blockAdderActive() {
+            return Object.keys(this.blockAdder).length !== 0;
+        },
+        srTitle() {
+            return this.isBlock ? 
+                this.$gettextInterpolate(this.$gettext(`Block %{name} einfügen`), { name: this.name }) :
+                this.$gettextInterpolate(this.$gettext(`Abschnitt %{name} einfügen`), { name: this.name });
+        }
+    },
+    methods: {
+        ...mapActions({
+            companionInfo: 'companionInfo',
+            companionSuccess: 'companionSuccess',
+            companionWarning: 'companionWarning',
+            copyContainer: 'copyContainer',
+            copyBlock: 'copyBlock',
+            clipboardInsertBlock: 'clipboardInsertBlock',
+            clipboardInsertContainer: 'clipboardInsertContainer',
+            loadStructuralElement: 'loadStructuralElement',
+            loadContainer: 'loadContainer',
+            deleteClipboard: 'courseware-clipboards/delete',
+            updateClipboard: 'courseware-clipboards/update',
+            loadClipboard: 'courseware-clipboards/loadById',
+        }),
+
+        async insertItem() {
+            let insertError = false;
+
+            if (this.isBlock) {
+                if (!this.blockAdderActive) {
+                    this.companionWarning({ info: this.text.positionWarning });
+                    return;
+                }
+                try {
+                    await this.clipboardInsertBlock({
+                        parentId: this.blockAdder.container.id,
+                        section: this.blockAdder.section,
+                        clipboard: this.clipboard,
+                    });
+                } catch (error) {
+                    insertError = true;
+                    this.companionWarning({ info: this.text.errorMessage });
+                }
+                if (!insertError) {
+                    await this.loadContainer(this.blockAdder.container.id);
+                    this.companionSuccess({ info: this.text.blockSuccess });
+                }
+            } else {
+                try {
+                    await this.clipboardInsertContainer({
+                        parentId: this.currentElement,
+                        clipboard: this.clipboard,
+                    });
+                } catch (error) {
+                    insertError = true;
+                    this.companionWarning({ info: this.text.errorMessage });
+                }
+                if (!insertError) {
+                    this.loadStructuralElement(this.currentElement);
+                    this.companionSuccess({ info: this.text.containerSuccess });
+                }
+            }
+        },
+
+        async insertItemCopy() {
+            let insertError = false;
+
+            if (this.isBlock) {
+                if (!this.blockAdderActive) {
+                    this.companionWarning({ info: this.text.positionWarning });
+                    return;
+                }
+                try {
+                    await this.copyBlock({
+                        parentId: this.blockAdder.container.id,
+                        section: this.blockAdder.section,
+                        block: { id: this.blockId },
+                    });
+                } catch (error) {
+                    insertError = true;
+                    this.companionWarning({ info: this.text.errorMessage });
+                }
+                if (!insertError) {
+                    await this.loadContainer(this.blockAdder.container.id);
+                    this.companionSuccess({ info: this.text.blockSuccess });
+                }
+            } else {
+                try {
+                    await this.copyContainer({ parentId: this.currentElement, container: { id: this.containerId } });
+                } catch (error) {
+                    insertError = true;
+                    this.companionWarning({ info: this.text.errorMessage });
+                }
+                if (!insertError) {
+                    this.loadStructuralElement(this.currentElement);
+                    this.companionSuccess({ info: this.text.containerSuccess });
+                }
+            }
+        },
+        deleteItem() {
+            this.deleteClipboard({ id: this.clipboard.id });
+        },
+        showEditItem() {
+            this.showEditDialog = true;
+        },
+        closeEditItem() {
+            this.showEditDialog = false;
+        },
+        async storeItem() {
+            this.closeEditItem();
+            await this.updateClipboard(this.currentClipboard);
+            this.loadClipboard({ id: this.currentClipboard.id });
+        },
+    },
+    mounted() {
+        this.currentClipboard = _.cloneDeep(this.clipboard);
+    },
+};
+</script>
diff --git a/resources/vue/components/courseware/CoursewareContainerActions.vue b/resources/vue/components/courseware/CoursewareContainerActions.vue
index 87c0e12a5e608ccc41ccde6b79af61476e5d1270..2480abaaeee2a50ddd37b7a02cca398083de93d3 100644
--- a/resources/vue/components/courseware/CoursewareContainerActions.vue
+++ b/resources/vue/components/courseware/CoursewareContainerActions.vue
@@ -7,6 +7,7 @@
             @changeContainer="changeContainer"
             @deleteContainer="deleteContainer"
             @removeLock="removeLock"
+            @copyToClipboard="copyToClipboard"
         />
     </div>
 </template>
@@ -43,12 +44,13 @@ export default {
                     menuItems.push({ id: 1, label: this.$gettext('Abschnitt bearbeiten'), icon: 'edit', emit: 'editContainer' });
                 }
                 menuItems.push({ id: 2, label: this.$gettext('Abschnitt verändern'), icon: 'settings', emit: 'changeContainer' });
-                menuItems.push({ id: 3, label: this.$gettext('Abschnitt löschen'), icon: 'trash', emit: 'deleteContainer' });
+                menuItems.push({ id: 3, label: this.$gettext('Abschnitt merken'), icon: 'clipboard', emit: 'copyToClipboard' });
+                menuItems.push({ id: 5, label: this.$gettext('Abschnitt löschen'), icon: 'trash', emit: 'deleteContainer' });
             }
 
             if (this.blocked && this.blockedByAnotherUser && this.userIsTeacher) {
                 menuItems.push({
-                    id: 4,
+                    id: 3,
                     label: this.$gettext('Sperre aufheben'),
                     icon: 'lock-unlocked',
                     emit: 'removeLock',
@@ -77,6 +79,9 @@ export default {
         },
         removeLock() {
             this.$emit('removeLock');
+        },
+        copyToClipboard() {
+            this.$emit('copyToClipboard');
         }
     },
 };
diff --git a/resources/vue/components/courseware/CoursewareDefaultBlock.vue b/resources/vue/components/courseware/CoursewareDefaultBlock.vue
index ff9701986c346c7e2d1b949dd3a70306bd811931..eb592ac8e01596b73eb072c6c0169ac5f13bf1ef 100644
--- a/resources/vue/components/courseware/CoursewareDefaultBlock.vue
+++ b/resources/vue/components/courseware/CoursewareDefaultBlock.vue
@@ -23,6 +23,7 @@
                     @showExportOptions="displayFeature('ExportOptions')"
                     @deleteBlock="displayDeleteDialog()"
                     @removeLock="displayRemoveLockDialog()"
+                    @copyToClipboard="copyToClipboard"
                 />
             </header>
             <div v-show="isOpen">
@@ -201,12 +202,14 @@ export default {
         ...mapActions({
             companionInfo: 'companionInfo',
             companionWarning: 'companionWarning',
+            companionSuccess: 'companionSuccess',
             deleteBlock: 'deleteBlockInContainer',
             lockObject: 'lockObject',
             unlockObject: 'unlockObject',
             loadContainer: 'loadContainer',
             loadBlock: 'courseware-blocks/loadById',
             updateContainer: 'updateContainer',
+            createClipboard: 'courseware-clipboards/create'
         }),
         async displayFeature(element) {
             if (this.showEdit && element === 'Edit') {
@@ -358,6 +361,19 @@ export default {
             await this.unlockObject({ id: this.block.id , type: 'courseware-blocks' });
             await this.loadBlock({ id: this.block.id });
             this.showRemoveLockDialog = false;
+        },
+        async copyToClipboard() {
+            const clipboard = {
+                attributes: {
+                    name: this.block.attributes.title,
+                    'block-id': this.block.id,
+                    'object-type': this.block.type,
+                    'object-kind': this.block.attributes['block-type'],
+                }
+            };
+
+            await this.createClipboard(clipboard, { root: true });
+            this.companionSuccess({ info: this.$gettext('Block wurde in Merkliste abgelegt.') });
         }
 
     },
diff --git a/resources/vue/components/courseware/CoursewareDefaultContainer.vue b/resources/vue/components/courseware/CoursewareDefaultContainer.vue
index a3f99d0f07220a19904720102fb1f007599ef706..313a9788ca88f4da9820496c62809dce002e4fdf 100644
--- a/resources/vue/components/courseware/CoursewareDefaultContainer.vue
+++ b/resources/vue/components/courseware/CoursewareDefaultContainer.vue
@@ -20,6 +20,7 @@
                     @changeContainer="displayChangeDialog"
                     @deleteContainer="displayDeleteDialog"
                     @removeLock="displayRemoveLockDialog"
+                    @copyToClipboard="copyToClipboard"
                 />
             </header>
             <div v-show="isOpen"
@@ -214,12 +215,14 @@ export default {
         ...mapActions({
             companionInfo: 'companionInfo',
             companionWarning: 'companionWarning',
+            companionSuccess: 'companionSuccess',
             updateContainer: 'updateContainer',
             loadContainer: 'courseware-containers/loadById',
             deleteContainer: 'deleteContainer',
             lockObject: 'lockObject',
             unlockObject: 'unlockObject',
-            coursewareBlockAdder: 'coursewareBlockAdder'
+            coursewareBlockAdder: 'coursewareBlockAdder',
+            createClipboard: 'courseware-clipboards/create'
         }),
         async displayEditDialog() {
             await this.loadContainer({ id: this.container.id, options: { include: 'edit-blocker' } });
@@ -362,6 +365,21 @@ export default {
             this.showRemoveLockDialog = false;
         },
 
+        async copyToClipboard() {
+            const clipboard = {
+                attributes: {
+                    name: this.container.attributes.title,
+                    'container-id': this.container.id,
+                    'object-type': this.container.type,
+                    'object-kind': this.container.attributes['container-type'],
+                    
+                }
+            };
+
+            await this.createClipboard(clipboard, { root: true });
+            this.companionSuccess({ info: this.$gettext('Abschnitt wurde in Merkliste abgelegt.') });
+        }
+
     },
 
     watch: {
diff --git a/resources/vue/components/courseware/CoursewareToolsBlockadder.vue b/resources/vue/components/courseware/CoursewareToolsBlockadder.vue
index 15adf2bdcb489d96af277b8c3c859e523df8e2ae..36348a38f171fc1d76818df909234b81e5e1686c 100644
--- a/resources/vue/components/courseware/CoursewareToolsBlockadder.vue
+++ b/resources/vue/components/courseware/CoursewareToolsBlockadder.vue
@@ -99,7 +99,57 @@
                     :secondSection="$gettext('zweites Element')"
                 ></courseware-container-adder-item>
             </courseware-tab>
+            <courseware-tab :name="$gettext('Merkliste')" :selected="showClipboard" :index="2" :style="{ maxHeight: maxHeight + 'px' }">
+                <courseware-collapsible-box :title="$gettext('Blöcke')" :open="clipboardBlocks.length > 0">
+                    <template v-if="clipboardBlocks.length > 0">
+                        <div class="cw-element-inserter-wrapper">
+                            <courseware-clipboard-item
+                                v-for="(clipboard, index) in clipboardBlocks"
+                                :key="index"
+                                :clipboard="clipboard"
+                                @inserted="$emit('blockAdded')"
+                            />
+                        </div>
+                        <button class="button trash" @click="clearClipboard('courseware-blocks')">
+                            {{ $gettext('Alle Blöcke aus Merkliste entfernen') }}
+                        </button>
+                    </template>
+                    <courseware-companion-box
+                        v-else
+                        mood="pointing"
+                        :msgCompanion="$gettext('Die Merkliste enthält keine Blöcke.')"
+                    />
+                </courseware-collapsible-box>
+                <courseware-collapsible-box :title="$gettext('Abschnitte')" :open="clipboardContainers.length > 0">
+                    <template v-if="clipboardContainers.length > 0">
+                        <div class="cw-element-inserter-wrapper">
+                            <courseware-clipboard-item
+                                v-for="(clipboard, index) in clipboardContainers"
+                                :key="index"
+                                :clipboard="clipboard"
+                            />
+                        </div>
+                        <button class="button trash" @click="clearClipboard('courseware-containers')">
+                            {{ $gettext('Alle Abschnitte aus Merkliste entfernen') }}
+                        </button>
+                    </template>
+                    <courseware-companion-box
+                        v-else
+                        mood="pointing"
+                        :msgCompanion="$gettext('Die Merkliste enthält keine Abschnitte.')"
+                    />
+                </courseware-collapsible-box>
+            </courseware-tab>
         </courseware-tabs>
+        <studip-dialog
+            v-if="showDeleteClipboardDialog"
+            :title="textDeleteClipboardTitle"
+            :question="textDeleteClipboardAlert"
+            height="200"
+            width="500"
+            @confirm="executeDeleteClipboard"
+            @close="closeDeleteClipboardDialog"
+        ></studip-dialog>
     </div>
 </template>
 
@@ -107,18 +157,26 @@
 import CoursewareTabs from './CoursewareTabs.vue';
 import CoursewareTab from './CoursewareTab.vue';
 import CoursewareBlockadderItem from './CoursewareBlockadderItem.vue';
+import CoursewareClipboardItem from './CoursewareClipboardItem.vue';
 import CoursewareContainerAdderItem from './CoursewareContainerAdderItem.vue';
 import CoursewareCompanionBox from './CoursewareCompanionBox.vue';
+import CoursewareCollapsibleBox from './CoursewareCollapsibleBox.vue';
 import { mapActions, mapGetters } from 'vuex';
 
+import StudipDialog from '../StudipDialog.vue';
+
 export default {
     name: 'cw-tools-blockadder',
     components: {
         CoursewareTabs,
         CoursewareTab,
         CoursewareBlockadderItem,
+        CoursewareClipboardItem,
         CoursewareContainerAdderItem,
         CoursewareCompanionBox,
+        CoursewareCollapsibleBox,
+
+        StudipDialog,
     },
     props: {
         stickyRibbon: {
@@ -130,11 +188,14 @@ export default {
         return {
             showBlockadder: true,
             showContaineradder: false,
+            showClipboard: false,
             searchInput: '',
             currentFilterCategory: '',
             filteredBlockTypes: [],
             categorizedBlocks: [],
-            selectedContainerStyle: 'full'
+            selectedContainerStyle: 'full',
+            showDeleteClipboardDialog: false,
+            deleteClipboardType: null
         };
     },
     computed: {
@@ -145,6 +206,8 @@ export default {
             containerTypes: 'containerTypes',
             favoriteBlockTypes: 'favoriteBlockTypes',
             showToolbar: 'showToolbar',
+            usersClipboards: 'courseware-clipboards/all',
+            userId: 'userId'
         }),
         blockTypes() {
             let blockTypes = JSON.parse(JSON.stringify(this.unorderedBlockTypes));
@@ -177,6 +240,34 @@ export default {
             } else {
                 return parseInt(Math.min(window.innerHeight * 0.75, window.innerHeight - 197)) - 120;
             }
+        },
+        clipboardBlocks() {
+            return this.usersClipboards
+                .filter(clipboard => clipboard.attributes['object-type'] === 'courseware-blocks')
+                .sort((a, b) => b.attributes.mkdate - a.attributes.mkdate);
+        },
+        clipboardContainers() {
+            return this.usersClipboards
+                .filter(clipboard => clipboard.attributes['object-type'] === 'courseware-containers')
+                .sort((a, b) => b.attributes.mkdate < a.attributes.mkdate);
+        },
+        textDeleteClipboardTitle() {
+            if (this.deleteClipboardType === 'courseware-blocks') {
+                return this.$gettext('Merkliste für Blöcke leeren');
+            }
+            if (this.deleteClipboardType === 'courseware-containers') {
+                return this.$gettext('Merkliste für Abschnitte leeren');
+            }
+            return '';
+        },
+        textDeleteClipboardAlert() {
+            if (this.deleteClipboardType === 'courseware-blocks') {
+                return this.$gettext('Möchten Sie die Merkliste für Blöcke unwiderruflich leeren?');
+            }
+            if (this.deleteClipboardType === 'courseware-containers') {
+                return this.$gettext('Möchten Sie die Merkliste für Abschnitte unwiderruflich leeren?');
+            }
+            return '';
         }
     },
     methods: {
@@ -184,17 +275,23 @@ export default {
             removeFavoriteBlockType: 'removeFavoriteBlockType',
             addFavoriteBlockType: 'addFavoriteBlockType',
             coursewareContainerAdder: 'coursewareContainerAdder',
-            companionWarning: 'companionWarning'
+            companionWarning: 'companionWarning',
+            deleteUserClipboards: 'deleteUserClipboards'
         }),
         displayContainerAdder() {
             this.showContaineradder = true;
             this.showBlockadder = false;
+            this.showClipboard = false;
         },
         displayBlockAdder() {
             this.showContaineradder = false;
+            this.showClipboard = false;
             this.showBlockadder = true;
             this.disableContainerAdder();
         },
+        displayClipboard() {
+            this.showClipboard = true;
+        },
         isBlockFav(block) {
             let isFav = false;
             this.favoriteBlockTypes.forEach((type) => {
@@ -279,6 +376,20 @@ export default {
             this.filteredBlockTypes = this.blockTypes;
             this.searchInput = '';
             this.currentFilterCategory = '';
+        },
+        clearClipboard(type) {
+            this.deleteClipboardType = type;
+            this.showDeleteClipboardDialog = true;
+        },
+        executeDeleteClipboard() {
+            if (this.deleteClipboardType) {
+                this.deleteUserClipboards({uid: this.userId, type: this.deleteClipboardType});
+            }
+            this.closeDeleteClipboardDialog();
+        },
+        closeDeleteClipboardDialog() {
+            this.showDeleteClipboardDialog = false;
+            this.deleteClipboardType = null;
         }
     },
     mounted() {
diff --git a/resources/vue/courseware-index-app.js b/resources/vue/courseware-index-app.js
index 5b66ef7d1954897069fc8623193dc436b5dcf65a..4741cd46974c6f437e97ee4e99bc442f506824c4 100644
--- a/resources/vue/courseware-index-app.js
+++ b/resources/vue/courseware-index-app.js
@@ -92,6 +92,7 @@ const mountApp = async (STUDIP, createApp, element) => {
                     'courseware-blocks',
                     'courseware-block-comments',
                     'courseware-block-feedback',
+                    'courseware-clipboards',
                     'courseware-containers',
                     'courseware-instances',
                     'courseware-public-links',
@@ -144,11 +145,19 @@ const mountApp = async (STUDIP, createApp, element) => {
     store.dispatch('oerEnabled', oer_enabled);
     store.dispatch('licenses', licenses);
     store.dispatch('courseware-templates/loadAll');
+    store.dispatch('loadUserClipboards', STUDIP.USER_ID);
 
     const pluginManager = new PluginManager();
     store.dispatch('setPluginManager', pluginManager);
     STUDIP.eventBus.emit('courseware:init-plugin-manager', pluginManager);
 
+    STUDIP.JSUpdater.register(
+        'coursewareclipboard',
+        () => { store.dispatch('loadUserClipboards', STUDIP.USER_ID)},
+        () => { return { 'counter' : store.getters['courseware-clipboards/all'].length };},
+        5000
+    );
+
     const app = createApp({
         render: (h) => h(IndexApp),
         router,
diff --git a/resources/vue/store/courseware/courseware.module.js b/resources/vue/store/courseware/courseware.module.js
index fc688f015ea882ccd75e69497e41adb848ef89a8..9669a6138b18d36d236f056c23bc4a36d7c2e7d1 100644
--- a/resources/vue/store/courseware/courseware.module.js
+++ b/resources/vue/store/courseware/courseware.module.js
@@ -429,11 +429,12 @@ export const actions = {
         return dispatch('folders/loadById', { id: folderId, options }, { root: true });
     },
 
-    copyBlock({ getters }, { parentId, block }) {
+    copyBlock({ getters }, { parentId, block, section }) {
         const copy = {
             data: {
                 block: block,
                 parent_id: parentId,
+                section: section
             },
         };
 
@@ -441,6 +442,16 @@ export const actions = {
             // console.log(resp);
         });
     },
+    clipboardInsertBlock({ getters }, { parentId, clipboard, section }) {
+        const insert = {
+            data: {
+                parent_id: parentId,
+                section: section
+            },
+        };
+
+        return state.httpClient.post(`courseware-clipboards/${clipboard.id}/insert`, insert);
+    },
     copyContainer({ getters }, { parentId, container }) {
         const copy = {
             data: {
@@ -453,6 +464,15 @@ export const actions = {
             // console.log(resp);
         });
     },
+    clipboardInsertContainer({ getters },{ parentId, clipboard }) {
+        const insert = {
+            data: {
+                parent_id: parentId,
+            },
+        };
+
+        return state.httpClient.post(`courseware-clipboards/${clipboard.id}/insert`, insert);
+    },
     async copyStructuralElement({ dispatch, getters, rootGetters }, { parentId, elementId, removePurpose, migrate, modifications }) {
         const copy = { data: { parent_id: parentId, remove_purpose: removePurpose, migrate: migrate, modifications: modifications } };
 
@@ -1354,6 +1374,25 @@ export const actions = {
     async loadProgresses({ dispatch, commit, getters }) {
         const progresses = await dispatch('loadUnitProgresses', { unitId: getters.context.unit });
         commit('setProgresses', progresses);
+    },
+
+    loadUserClipboards({ dispatch }, uid) {
+        dispatch('courseware-clipboards/resetState');
+        const parent = { type: 'users', id: uid };
+        const relationship = 'courseware-clipboards';
+        const options = {}
+
+        return dispatch('loadRelatedPaginated', {
+            type: 'courseware-clipboards',
+            parent,
+            relationship,
+            options,
+        });
+    },
+
+    async deleteUserClipboards({ dispatch, rootGetters }, { uid, type }) {
+        await state.httpClient.delete(`users/${uid}/courseware-clipboards/${type}`);
+        dispatch('loadUserClipboards', uid);
     }
 };