From ba52642f0634f211432e877d9dff0d2dec75d806 Mon Sep 17 00:00:00 2001 From: Ron Lucke Date: Mon, 18 Dec 2023 12:03:38 +0000 Subject: StEP #3255 Merge request studip/studip!2355 --- .../5.5.11_courseware_add_optional_comments.php | 21 +++ .../JsonApi/Routes/Courseware/Authority.php | 4 +- .../JsonApi/Routes/Courseware/BlocksCreate.php | 1 + .../JsonApi/Routes/Courseware/BlocksUpdate.php | 4 + .../Routes/Courseware/StructuralElementsCreate.php | 3 +- .../Routes/Courseware/StructuralElementsUpdate.php | 4 + .../JsonApi/Routes/Courseware/UnitsCreate.php | 3 +- lib/classes/JsonApi/Schemas/Courseware/Block.php | 1 + .../Schemas/Courseware/StructuralElement.php | 1 + lib/models/Courseware/Block.php | 4 + lib/models/Courseware/StructuralElement.php | 9 +- resources/assets/stylesheets/scss/courseware.scss | 3 +- .../scss/courseware/blocks/default-block.scss | 37 +---- .../stylesheets/scss/courseware/comments.scss | 26 ++-- .../courseware/containers/default-container.scss | 18 --- .../scss/courseware/layouts/call-to-action.scss | 19 +++ .../scss/courseware/layouts/talk-bubble.scss | 133 +++++++++++------ .../scss/courseware/structural-element.scss | 41 +----- .../courseware/blocks/CoursewareBlockActions.vue | 33 +++++ .../courseware/blocks/CoursewareBlockComments.vue | 10 +- .../blocks/CoursewareBlockDiscussion.vue | 116 +++++++++++---- .../courseware/blocks/CoursewareBlockFeedback.vue | 33 ++--- .../courseware/blocks/CoursewareDefaultBlock.vue | 61 ++++++-- .../layouts/CoursewareCallToActionBox.vue | 70 +++++++++ .../courseware/layouts/CoursewareTalkBubble.vue | 58 ++++++-- .../CoursewareStructuralElement.vue | 157 ++++++++++++++++++--- .../CoursewareStructuralElementComments.vue | 10 +- .../CoursewareStructuralElementFeedback.vue | 52 ++++--- .../courseware/widgets/CoursewareViewWidget.vue | 14 -- .../vue/store/courseware/courseware.module.js | 52 +++++++ 30 files changed, 713 insertions(+), 285 deletions(-) create mode 100644 db/migrations/5.5.11_courseware_add_optional_comments.php create mode 100644 resources/assets/stylesheets/scss/courseware/layouts/call-to-action.scss create mode 100644 resources/vue/components/courseware/layouts/CoursewareCallToActionBox.vue diff --git a/db/migrations/5.5.11_courseware_add_optional_comments.php b/db/migrations/5.5.11_courseware_add_optional_comments.php new file mode 100644 index 0000000..7c12a2e --- /dev/null +++ b/db/migrations/5.5.11_courseware_add_optional_comments.php @@ -0,0 +1,21 @@ +exec("ALTER TABLE `cw_blocks` ADD `commentable` TINYINT(1) NOT NULL AFTER `visible`"); + DBManager::get()->exec("ALTER TABLE `cw_structural_elements` ADD `commentable` TINYINT(1) NOT NULL AFTER `public`"); + } + + protected function down() + { + DBManager::get()->exec("ALTER TABLE `cw_blocks` DROP `commentable`"); + DBManager::get()->exec("ALTER TABLE `cw_structural_elements` DROP `commentable`"); + } +} diff --git a/lib/classes/JsonApi/Routes/Courseware/Authority.php b/lib/classes/JsonApi/Routes/Courseware/Authority.php index 3df103d..88eb3df 100644 --- a/lib/classes/JsonApi/Routes/Courseware/Authority.php +++ b/lib/classes/JsonApi/Routes/Courseware/Authority.php @@ -400,7 +400,7 @@ class Authority return $perm; } - public static function canUpdateStructuralElementFeedback(User $user, StructuralElementComment $resource) + public static function canUpdateStructuralElementFeedback(User $user, StructuralElementFeedback $resource) { return self::canCreateStructuralElementFeedback($user, $resource->structural_element); } @@ -410,7 +410,7 @@ class Authority return $resource->user_id === $user->id || self::canUpdateStructuralElement($user, $resource->structural_element); } - public static function canDeleteStructuralElementFeedback(User $user, StructuralElementComment $resource) + public static function canDeleteStructuralElementFeedback(User $user, StructuralElementFeedback $resource) { return self::canUpdateStructuralElementFeedback($user, $resource); } diff --git a/lib/classes/JsonApi/Routes/Courseware/BlocksCreate.php b/lib/classes/JsonApi/Routes/Courseware/BlocksCreate.php index 04c7d92..d1d3afb 100644 --- a/lib/classes/JsonApi/Routes/Courseware/BlocksCreate.php +++ b/lib/classes/JsonApi/Routes/Courseware/BlocksCreate.php @@ -102,6 +102,7 @@ class BlocksCreate extends JsonApiController 'block_type' => $get('data.attributes.block-type'), 'payload' => '', 'visible' => 1, + 'commentable' => 0 ]); $payload = $get('data.attributes.payload'); diff --git a/lib/classes/JsonApi/Routes/Courseware/BlocksUpdate.php b/lib/classes/JsonApi/Routes/Courseware/BlocksUpdate.php index fcec2bb..6cf06ff 100644 --- a/lib/classes/JsonApi/Routes/Courseware/BlocksUpdate.php +++ b/lib/classes/JsonApi/Routes/Courseware/BlocksUpdate.php @@ -84,6 +84,10 @@ class BlocksUpdate extends JsonApiController $resource->visible = $get('data.attributes.visible'); } + if (is_bool($get('data.attributes.commentable'))) { + $resource->commentable = $get('data.attributes.commentable'); + } + if ($get('data.relationships.container.data.id')) { $resource->container_id = $get('data.relationships.container.data.id'); } diff --git a/lib/classes/JsonApi/Routes/Courseware/StructuralElementsCreate.php b/lib/classes/JsonApi/Routes/Courseware/StructuralElementsCreate.php index 496a8f7..c038c45 100644 --- a/lib/classes/JsonApi/Routes/Courseware/StructuralElementsCreate.php +++ b/lib/classes/JsonApi/Routes/Courseware/StructuralElementsCreate.php @@ -83,7 +83,8 @@ class StructuralElementsCreate extends JsonApiController 'payload' => self::arrayGet($json, 'data.attributes.payload', ''), 'read_approval' => $parent->read_approval, 'write_approval' => $parent->write_approval, - 'position' => $parent->countChildren() + 'position' => $parent->countChildren(), + 'commentable' => 0 ]); $struct->store(); diff --git a/lib/classes/JsonApi/Routes/Courseware/StructuralElementsUpdate.php b/lib/classes/JsonApi/Routes/Courseware/StructuralElementsUpdate.php index 6bf0e79..455aacc 100644 --- a/lib/classes/JsonApi/Routes/Courseware/StructuralElementsUpdate.php +++ b/lib/classes/JsonApi/Routes/Courseware/StructuralElementsUpdate.php @@ -140,6 +140,10 @@ class StructuralElementsUpdate extends JsonApiController $resource->withdraw_date = $json['data']['attributes']['withdraw-date']; } + if (isset($json['data']['attributes']['commentable'])) { + $resource->commentable = $json['data']['attributes']['commentable']; + } + // update parent if (self::arrayHas($json, 'data.relationships.parent')) { $parent = $this->getParentFromJson($json); diff --git a/lib/classes/JsonApi/Routes/Courseware/UnitsCreate.php b/lib/classes/JsonApi/Routes/Courseware/UnitsCreate.php index a6159e3..5909f2a 100644 --- a/lib/classes/JsonApi/Routes/Courseware/UnitsCreate.php +++ b/lib/classes/JsonApi/Routes/Courseware/UnitsCreate.php @@ -96,7 +96,8 @@ class UnitsCreate extends JsonApiController 'title' => self::arrayGet($json, 'data.attributes.title', ''), 'purpose' => self::arrayGet($json, 'data.attributes.purpose', ''), 'payload' => self::arrayGet($json, 'data.attributes.payload', ''), - 'position' => 0 + 'position' => 0, + 'commentable' => 0 ]); $unit = \Courseware\Unit::create([ diff --git a/lib/classes/JsonApi/Schemas/Courseware/Block.php b/lib/classes/JsonApi/Schemas/Courseware/Block.php index 03eb56b..e608188 100644 --- a/lib/classes/JsonApi/Schemas/Courseware/Block.php +++ b/lib/classes/JsonApi/Schemas/Courseware/Block.php @@ -40,6 +40,7 @@ class Block extends SchemaProvider 'block-type' => (string) $resource->getBlockType(), 'title' => (string) $resource->type->getTitle(), 'visible' => (bool) $resource['visible'], + 'commentable' => (bool) $resource['commentable'], 'payload' => $resource->type->getPayload(), 'mkdate' => date('c', $resource['mkdate']), 'chdate' => date('c', $resource['chdate']), diff --git a/lib/classes/JsonApi/Schemas/Courseware/StructuralElement.php b/lib/classes/JsonApi/Schemas/Courseware/StructuralElement.php index 4335e89..ab1dd0f 100644 --- a/lib/classes/JsonApi/Schemas/Courseware/StructuralElement.php +++ b/lib/classes/JsonApi/Schemas/Courseware/StructuralElement.php @@ -54,6 +54,7 @@ class StructuralElement extends SchemaProvider 'can-edit' => $resource->canEdit($user), 'can-visit' => $resource->canVisit($user), 'is-link' => (int) $resource['is_link'], + 'commentable' => (bool) $resource['commentable'], 'target-id' => (int) $resource['target_id'], 'external-relations' => $resource['external_relations']->getIterator(), 'mkdate' => date('c', $resource['mkdate']), diff --git a/lib/models/Courseware/Block.php b/lib/models/Courseware/Block.php index b794832..8b3d6f4 100644 --- a/lib/models/Courseware/Block.php +++ b/lib/models/Courseware/Block.php @@ -22,6 +22,7 @@ use User; * @property int $position database column * @property string|null $block_type database column * @property int $visible database column + * @property int $commentable database column * @property string $payload database column * @property int $mkdate database column * @property int $chdate database column @@ -172,6 +173,7 @@ class Block extends \SimpleORMap implements \PrivacyObject 'block-type'=> $this->type->getType(), 'title'=> $this->type->getTitle(), 'visible'=> $this->visible, + 'commentable' => $this->commentable, 'payload'=> $this->type->getPayload(), 'mkdate'=> $this->mkdate, 'chdate'=> $this->chdate @@ -204,6 +206,7 @@ class Block extends \SimpleORMap implements \PrivacyObject 'block_type' => $this->type->getType(), 'payload' => json_encode($this->type->copyPayload($rangeId)), 'visible' => 1, + 'commentable' => 0 ]); //update Container payload @@ -227,6 +230,7 @@ class Block extends \SimpleORMap implements \PrivacyObject 'block_type' => $data->attributes->{'block-type'}, 'payload' => json_encode($data->attributes->payload), 'visible' => 1, + 'commentable' => 0 ]); $block->payload = json_encode($block->type->copyPayload($rangeId)); diff --git a/lib/models/Courseware/StructuralElement.php b/lib/models/Courseware/StructuralElement.php index 34cef80..a63c73f 100644 --- a/lib/models/Courseware/StructuralElement.php +++ b/lib/models/Courseware/StructuralElement.php @@ -31,6 +31,7 @@ use User; * @property string|null $purpose database column * @property \JSONArrayObject $payload database column * @property int $public database column + * @property int $commentable database column * @property int $release_date database column * @property int $withdraw_date database column * @property \JSONArrayObject $read_approval database column @@ -758,6 +759,7 @@ class StructuralElement extends \SimpleORMap implements \PrivacyObject 'owner_id' => $user->id, 'editor_id' => $user->id, 'title' => _('neue Seite'), + 'commentable' => 0 ]); $struct->store(); @@ -841,6 +843,7 @@ SQL; 'purpose' => $purpose ?: $this->purpose, 'position' => 0, 'payload' => $this->payload, + 'commentable' => 0 ]); $element->store(); @@ -892,7 +895,8 @@ SQL; 'image_id' => $image_id, 'image_type' => $this->image_type, 'read_approval' => $parent->read_approval, - 'write_approval' => $parent->write_approval + 'write_approval' => $parent->write_approval, + 'commentable' => 0 ]); $element->store(); @@ -1032,7 +1036,8 @@ SQL; 'position' => $parent->countChildren(), 'payload' => $this->payload, 'read_approval' => $parent->read_approval, - 'write_approval' => $parent->write_approval + 'write_approval' => $parent->write_approval, + 'commentable' => 0 ]); $element->store(); diff --git a/resources/assets/stylesheets/scss/courseware.scss b/resources/assets/stylesheets/scss/courseware.scss index 6c3d7ea..d83a23e 100644 --- a/resources/assets/stylesheets/scss/courseware.scss +++ b/resources/assets/stylesheets/scss/courseware.scss @@ -20,7 +20,8 @@ @import './courseware/containers/tabs.scss'; @import './courseware/blocks/default-block.scss'; -@import './courseware/layouts/collapsible.scss'; + +@import './courseware/layouts/call-to-action.scss'; @import './courseware/layouts/companion.scss'; @import './courseware/layouts/import-zip.scss'; @import './courseware/layouts/input-file.scss'; diff --git a/resources/assets/stylesheets/scss/courseware/blocks/default-block.scss b/resources/assets/stylesheets/scss/courseware/blocks/default-block.scss index 21e583e..d65927b 100644 --- a/resources/assets/stylesheets/scss/courseware/blocks/default-block.scss +++ b/resources/assets/stylesheets/scss/courseware/blocks/default-block.scss @@ -1,6 +1,7 @@ .cw-default-block { display: flex; - flex-flow: row; + flex-flow: column nowrap; + .cw-default-block-invisible-info { img { vertical-align: text-bottom; @@ -95,21 +96,6 @@ } } - -.cw-container-wrapper-discuss { - .cw-container-colspan-full { - .cw-content-wrapper { - max-width: $max-content-width; - } - } - .cw-container-colspan-half, - .cw-container-colspan-half-center { - .cw-content-wrapper { - max-width: 540px; - } - } -} - .cw-block-title { padding: 4px; background-color: var(--content-color-20); @@ -132,22 +118,3 @@ padding-top: 106px; } } - -.cw-call-to-action { - border: solid thin var(--content-color-40); - border-top: none; - - button { - width: 100%; - background-color: var(--activity-color-20); - border: none; - text-align: left; - padding: 1em; - cursor: pointer; - - img { - margin: 0 1em; - vertical-align: middle; - } - } -} \ No newline at end of file diff --git a/resources/assets/stylesheets/scss/courseware/comments.scss b/resources/assets/stylesheets/scss/courseware/comments.scss index f953e86..cec94b0 100644 --- a/resources/assets/stylesheets/scss/courseware/comments.scss +++ b/resources/assets/stylesheets/scss/courseware/comments.scss @@ -1,17 +1,11 @@ -.cw-structural-element-feedback, -.cw-structural-element-comments { - padding: 0 1em; -} - .cw-structural-element-feedback-items, .cw-structural-element-comments-items, .cw-block-feedback-items, .cw-block-comments-items { min-height: 1em; - max-height: 225px; + max-height: 270px; overflow-y: auto; overflow-x: hidden; - margin: -1em -1em 0em 0em; padding: 0em 1em 0em 0em; scroll-behavior: smooth; } @@ -30,7 +24,7 @@ .cw-block-feedback-create, .cw-block-comment-create { border-top: solid thin var(--content-color-40); - padding-top: 1em; + padding: 8px 1em 0 1em; textarea { width: calc(100% - 6px); resize: none; @@ -54,4 +48,20 @@ .cw-comments-overview-dialog-comments-context { margin: 0 0 1.5em 0; +} + +.cw-block-discussion { + .cw-call-to-action:not(:first-child) { + border-top: none; + } +} + +.cw-default-block-active { + .cw-block-discussion { + margin: 0 -2px 0 0px; + .cw-call-to-action { + border-top: none; + } + } + } \ No newline at end of file diff --git a/resources/assets/stylesheets/scss/courseware/containers/default-container.scss b/resources/assets/stylesheets/scss/courseware/containers/default-container.scss index 7798d50..a959d86 100644 --- a/resources/assets/stylesheets/scss/courseware/containers/default-container.scss +++ b/resources/assets/stylesheets/scss/courseware/containers/default-container.scss @@ -104,24 +104,6 @@ form.cw-container-dialog-edit-form { } } -.cw-container-wrapper-discuss { - flex-direction: column; - - .cw-container-colspan-full { - max-width: unset; - } - .cw-container-colspan-half-center, - .cw-container-colspan-half { - max-width: 1050px; - } - .cw-container-colspan-half-center { - width: 100%; - .cw-container-content { - width: 1050px; - } - } -} - .cw-radioset { display: flex; flex-direction: row; diff --git a/resources/assets/stylesheets/scss/courseware/layouts/call-to-action.scss b/resources/assets/stylesheets/scss/courseware/layouts/call-to-action.scss new file mode 100644 index 0000000..2e5e3b9 --- /dev/null +++ b/resources/assets/stylesheets/scss/courseware/layouts/call-to-action.scss @@ -0,0 +1,19 @@ +@use '../../../mixins.scss' as *; + +.cw-call-to-action { + border: solid thin var(--content-color-40); + + .action-button { + width: 100%; + background-color: var(--activity-color-20); + border: none; + text-align: left; + padding: 1em; + cursor: pointer; + + img { + margin: 0 1em; + vertical-align: middle; + } + } +} \ No newline at end of file diff --git a/resources/assets/stylesheets/scss/courseware/layouts/talk-bubble.scss b/resources/assets/stylesheets/scss/courseware/layouts/talk-bubble.scss index 06062b6..c3d30c2 100644 --- a/resources/assets/stylesheets/scss/courseware/layouts/talk-bubble.scss +++ b/resources/assets/stylesheets/scss/courseware/layouts/talk-bubble.scss @@ -1,58 +1,99 @@ -.cw-talk-bubble { - margin: 10px 20px; - position: relative; - width: 85%; - height: auto; - background-color: var(--dark-gray-color-10); - float: left; - - .cw-talk-bubble-talktext { - padding: 1em; - text-align: left; - line-height: 1.5em; - - .cw-talk-bubble-talktext-time { - color: var(--dark-gray-color-80); - text-align: right; - font-size: 0.8em; - margin-bottom: -0.5em; - } - } +$color: var(--base-color-20); +$ownColor: var(--petrol-40); - &.cw-talk-bubble-own-post { - float: right; - } +.cw-talk-bubble-wrapper { + display: flex; + flex-direction: row; + justify-content: start; - &:after { - content: ' '; - position: absolute; - width: 0; - height: 0; - top: 0px; - bottom: auto; - border: 22px solid; - border-color: var(--dark-gray-color-10) transparent transparent transparent; - left: -20px; - right: auto; + .cw-talk-bubble-avatar { + padding: 8px; } - &.cw-talk-bubble-own-post:after { - left: auto; - right: -20px; + .cw-talk-bubble { + margin: 10px 20px; + position: relative; + max-width: 80%; + height: auto; + background-color: $color; + border-radius: 10px; + + .cw-talk-bubble-content { + padding: 8px 1em; + + .cw-talk-bubble-header { + margin-bottom: 8px; + + a { + font-weight: 700; + } + } + + .cw-talk-bubble-talktext { + margin-bottom: 4px; + text-align: left; + line-height: 1.5em; + + .cw-talk-bubble-footer { + float: right; + margin-top: 4px; + padding-bottom: 4px; + + &:before { + content: " "; + width: 1em; + display: inline-block; + } + + .cw-talk-bubble-talktext-time { + text-align: right; + font-size: 0.8em; + margin-bottom: -0.5em; + } + + button { + border: none; + background-color: transparent; + vertical-align: middle; + cursor: pointer; + } + } + + } + } + + &:after { + content: ' '; + position: absolute; + width: 0; + height: 0; + top: 0px; + bottom: auto; + border: 16px solid; + border-color: $color transparent transparent transparent; + border-radius: 4px; + left: -14px; + right: auto; + } } - .cw-talk-bubble-user { - padding: 1em 1em 0 1em; + &.cw-talk-bubble-own-post { + justify-content: end; + + .cw-talk-bubble { + flex-direction: row-reverse; + background-color: $ownColor; - .cw-talk-bubble-avatar { - display: inline-block; + &:after { + border-color: $ownColor transparent transparent transparent; + left: auto; + right: -14px; + } } - span { - padding-left: 4px; - color: var(--dark-gray-color-45); - font-weight: 600; - vertical-align: top; + + .cw-talk-bubble-header { + flex-direction: row-reverse; } } } \ No newline at end of file diff --git a/resources/assets/stylesheets/scss/courseware/structural-element.scss b/resources/assets/stylesheets/scss/courseware/structural-element.scss index ed1ba38..4609f8c 100644 --- a/resources/assets/stylesheets/scss/courseware/structural-element.scss +++ b/resources/assets/stylesheets/scss/courseware/structural-element.scss @@ -49,8 +49,9 @@ } } - .cw-structural-element-discussion { - max-width: 1606px; + .cw-structural-element-feedback-wrapper, + .cw-structural-element-comments-wrapper { + max-width: calc(1095px - 2px); width: 100%; margin-bottom: 1em; } @@ -68,10 +69,6 @@ margin: 0 auto; padding: 91px 15px 15px 15px; } - - &.cw-container-wrapper-discuss { - max-width: 1606px; - } } .cw-structural-element-description { @@ -238,36 +235,4 @@ } } } -} - -@media only screen and (max-width: 1820px) { - .cw-structural-element .cw-container-wrapper.cw-container-wrapper-discuss { - max-width: $max-content-width; - .cw-container.cw-container-list.cw-container-item.cw-container-colspan-full { - .cw-default-block { - flex-flow: column; - .cw-discuss-wrapper { - margin-left: 0; - margin-top: 8px; - } - } - } - } -} - -@media only screen and (max-width: 1200px) { - .cw-structural-element .cw-container-wrapper.cw-container-wrapper-discuss { - max-width: $max-content-width; - .cw-container.cw-container-list.cw-container-item.cw-container-colspan-half, - .cw-container.cw-container-list.cw-container-item.cw-container-colspan-half-center { - .cw-default-block { - flex-flow: column; - .cw-discuss-wrapper { - margin-left: 0; - margin-top: 8px; - max-width: 540px; - } - } - } - } } \ No newline at end of file diff --git a/resources/vue/components/courseware/blocks/CoursewareBlockActions.vue b/resources/vue/components/courseware/blocks/CoursewareBlockActions.vue index 3ffff18..89beb0a 100644 --- a/resources/vue/components/courseware/blocks/CoursewareBlockActions.vue +++ b/resources/vue/components/courseware/blocks/CoursewareBlockActions.vue @@ -9,6 +9,9 @@ @deleteBlock="deleteBlock" @removeLock="removeLock" @copyToClipboard="copyToClipboard" + @deactivateComments="deactivateComments" + @activateComments="activateComments" + @showFeedback="showFeedback" /> @@ -34,6 +37,7 @@ export default { ...mapGetters({ userId: 'userId', userIsTeacher: 'userIsTeacher', + getRelatedFeedback: 'courseware-block-feedback/related', }), blocked() { return this.block?.relationships?.['edit-blocker']?.data !== null; @@ -47,6 +51,16 @@ export default { blockedByAnotherUser() { return this.blocked && this.userId !== this.blockerId; }, + hasFeedback() { + const { id, type } = this.block; + const feedback = this.getRelatedFeedback({ parent: { id, type }, relationship: 'feedback' }); + + if (feedback === null || feedback.length === 0) { + return false; + } + + return true; + }, menuItems() { let menuItems = []; if (this.canEdit) { @@ -61,6 +75,16 @@ export default { icon: this.block.attributes.visible ? 'visibility-visible' : 'visibility-invisible', // do we change the icons ? emit: 'setVisibility', }); + if (this.userIsTeacher) { + menuItems.push({ + id: 4, + label: this.block.attributes.commentable + ? this.$gettext('Kommentare abschalten') + : this.$gettext('Kommentare aktivieren'), + icon: 'comment2', + emit: this.block.attributes.commentable ? 'deactivateComments' : 'activateComments', + }); + } } if (this.blocked && this.blockedByAnotherUser && this.userIsTeacher) { menuItems.push({ @@ -145,6 +169,15 @@ export default { }, copyToClipboard() { this.$emit('copyToClipboard'); + }, + activateComments() { + this.$emit('activateComments') + }, + deactivateComments() { + this.$emit('deactivateComments') + }, + showFeedback() { + this.$emit('showFeedback'); } }, }; diff --git a/resources/vue/components/courseware/blocks/CoursewareBlockComments.vue b/resources/vue/components/courseware/blocks/CoursewareBlockComments.vue index 68dc648..9b1cc44 100644 --- a/resources/vue/components/courseware/blocks/CoursewareBlockComments.vue +++ b/resources/vue/components/courseware/blocks/CoursewareBlockComments.vue @@ -7,6 +7,7 @@ v-for="comment in comments" :key="comment.id" :payload="buildPayload(comment)" + @delete="deleteComment(comment)" />
@@ -61,7 +62,8 @@ export default { methods: { ...mapActions({ createComments: 'courseware-block-comments/create', - loadRelatedComments: 'courseware-block-comments/loadRelated' + loadRelatedComments: 'courseware-block-comments/loadRelated', + deleteBlockComment: 'courseware-block-comments/delete' }), async loadComments() { const parent = { @@ -96,6 +98,9 @@ export default { this.loadComments(); this.createComment = ''; }, + deleteComment(comment) { + this.deleteBlockComment({id: comment.id, type: comment.type }); + }, buildPayload(comment) { const commenter = this.relatedUser({ parent: { id: comment.id, type: comment.type }, @@ -109,7 +114,8 @@ export default { chdate: comment.attributes.chdate, mkdate: comment.attributes.mkdate, user_id: commenter.id, - user_name: commenter.attributes['formatted-name'], + user_formatted_name: commenter.attributes['formatted-name'], + username: commenter?.attributes?.username ?? '', user_avatar: commenter.meta.avatar.small, }; diff --git a/resources/vue/components/courseware/blocks/CoursewareBlockDiscussion.vue b/resources/vue/components/courseware/blocks/CoursewareBlockDiscussion.vue index 716e6ee..e29e15a 100644 --- a/resources/vue/components/courseware/blocks/CoursewareBlockDiscussion.vue +++ b/resources/vue/components/courseware/blocks/CoursewareBlockDiscussion.vue @@ -1,31 +1,37 @@ diff --git a/resources/vue/components/courseware/blocks/CoursewareBlockFeedback.vue b/resources/vue/components/courseware/blocks/CoursewareBlockFeedback.vue index dfc95dc..c13d500 100644 --- a/resources/vue/components/courseware/blocks/CoursewareBlockFeedback.vue +++ b/resources/vue/components/courseware/blocks/CoursewareBlockFeedback.vue @@ -11,16 +11,19 @@ v-for="feedback in feedback" :key="feedback.id" :payload="buildPayload(feedback)" + @delete="deleteFeedback(feedback)" />
- +
@@ -31,7 +34,6 @@ import CoursewareCompanionBox from '../layouts/CoursewareCompanionBox.vue'; import CoursewareTalkBubble from '../layouts/CoursewareTalkBubble.vue'; import { mapActions, mapGetters } from 'vuex'; - export default { name: 'courseware-block-feedback', components: { @@ -45,8 +47,8 @@ export default { data() { return { feedbackText: '', - placeHolder: this.$gettext('Schreiben Sie ein Feedback...'), - srMessage: '' + placeHolder: this.$gettext('Schreiben Sie eine Anmerkung...'), + srMessage: '', }; }, computed: { @@ -67,12 +69,13 @@ export default { } return false; - } + }, }, methods: { ...mapActions({ createFeedback: 'courseware-block-feedback/create', loadRelatedFeedback: 'courseware-block-feedback/loadRelated', + deleteBlockFeedback: 'courseware-block-feedback/delete', }), buildPayload(feedback) { const { id, type } = feedback; @@ -83,7 +86,8 @@ export default { content: feedback.attributes.feedback, chdate: feedback.attributes.chdate, mkdate: feedback.attributes.mkdate, - user_name: user?.attributes?.['formatted-name'] ?? '', + user_formatted_name: user?.attributes?.['formatted-name'] ?? '', + username: user?.attributes?.username ?? '', user_avatar: user?.meta?.avatar.small, }; }, @@ -119,23 +123,16 @@ export default { this.feedbackText = ''; this.loadFeedback(); }, + deleteFeedback(feedback) { + this.deleteBlockFeedback({ id: feedback.id, type: feedback.type }); + }, updateSrMessage(message) { this.srMessage = ''; this.srMessage = message; - } - }, - async mounted() { - await this.loadFeedback(this.block.id); + }, }, updated() { this.$refs.feedbacks.scrollTop = this.$refs.feedbacks.scrollHeight; }, - watch: { - feedback() { - if (this.feedback && this.feedback.length > 0) { - this.$emit('hasFeedback'); - } - } - } }; diff --git a/resources/vue/components/courseware/blocks/CoursewareDefaultBlock.vue b/resources/vue/components/courseware/blocks/CoursewareDefaultBlock.vue index d11a538..2fabdda 100644 --- a/resources/vue/components/courseware/blocks/CoursewareDefaultBlock.vue +++ b/resources/vue/components/courseware/blocks/CoursewareDefaultBlock.vue @@ -1,5 +1,5 @@ + + \ No newline at end of file diff --git a/resources/vue/components/courseware/layouts/CoursewareTalkBubble.vue b/resources/vue/components/courseware/layouts/CoursewareTalkBubble.vue index cd76aaa..007a72e 100644 --- a/resources/vue/components/courseware/layouts/CoursewareTalkBubble.vue +++ b/resources/vue/components/courseware/layouts/CoursewareTalkBubble.vue @@ -1,20 +1,35 @@ \ No newline at end of file + diff --git a/resources/vue/components/courseware/structural-element/CoursewareStructuralElement.vue b/resources/vue/components/courseware/structural-element/CoursewareStructuralElement.vue index dcb3969..4f70f48 100644 --- a/resources/vue/components/courseware/structural-element/CoursewareStructuralElement.vue +++ b/resources/vue/components/courseware/structural-element/CoursewareStructuralElement.vue @@ -77,11 +77,30 @@ @linkElement="menuAction('linkElement')" @removeLock="menuAction('removeLock')" @activateFullscreen="menuAction('activateFullscreen')" + @activateComments="menuAction('activateComments')" + @deactivateComments="menuAction('deactivateComments')" + @showFeedback="menuAction('showFeedback')" />
+ + +
- -
+ + +
@@ -62,7 +63,8 @@ export default { methods: { ...mapActions({ createComments: 'courseware-structural-element-comments/create', - loadRelatedComments: 'courseware-structural-element-comments/loadRelated' + loadRelatedComments: 'courseware-structural-element-comments/loadRelated', + deleteElementComment: 'courseware-structural-element-comments/delete' }), async loadComments() { const parent = { @@ -110,12 +112,16 @@ export default { chdate: comment.attributes.chdate, mkdate: comment.attributes.mkdate, user_id: commenter.id, - user_name: commenter.attributes['formatted-name'], + user_formatted_name: commenter.attributes['formatted-name'], + username: commenter?.attributes?.username ?? '', user_avatar: commenter.meta.avatar.small, }; return payload; }, + deleteComment(comment) { + this.deleteElementComment({ id: comment.id, type: comment.type }); + }, updateSrMessage(message) { this.srMessage = ''; this.srMessage = message; diff --git a/resources/vue/components/courseware/structural-element/CoursewareStructuralElementFeedback.vue b/resources/vue/components/courseware/structural-element/CoursewareStructuralElementFeedback.vue index fd950bc..acecf63 100644 --- a/resources/vue/components/courseware/structural-element/CoursewareStructuralElementFeedback.vue +++ b/resources/vue/components/courseware/structural-element/CoursewareStructuralElementFeedback.vue @@ -10,16 +10,19 @@ v-for="feedback in feedback" :key="feedback.id" :payload="buildPayload(feedback)" + @delete="deleteFeedback(feedback)" />
+ v-if="!userIsTeacher && feedback.length === 0" + :msgCompanion="$gettext('Es wurde noch keine Anmerkungen abgegeben.')" + mood="pointing" + />
- +
@@ -42,8 +45,8 @@ export default { data() { return { feedbackText: '', - placeHolder: this.$gettext('Schreiben Sie ein Feedback...'), - srMessage: '' + placeHolder: this.$gettext('Schreiben Sie eine Anmerkung...'), + srMessage: '', }; }, computed: { @@ -67,12 +70,13 @@ export default { } return false; - } + }, }, methods: { ...mapActions({ createFeedback: 'courseware-structural-element-feedback/create', loadRelatedFeedback: 'courseware-structural-element-feedback/loadRelated', + deleteElementFeedback: 'courseware-structural-element-feedback/delete', }), buildPayload(feedback) { const { id, type } = feedback; @@ -83,7 +87,8 @@ export default { content: feedback.attributes.feedback, chdate: feedback.attributes.chdate, mkdate: feedback.attributes.mkdate, - user_name: user?.attributes?.['formatted-name'] ?? '', + user_formatted_name: user?.attributes?.['formatted-name'] ?? '', + username: user?.attributes?.username ?? '', user_avatar: user?.meta?.avatar.small, }; }, @@ -101,7 +106,7 @@ export default { }); }, async postFeedback() { - this.updateSrMessage(this.$gettext('Feedback gesendet')); + this.updateSrMessage(this.$gettext('Anmerkung gesendet')); const data = { attributes: { feedback: this.feedbackText, @@ -110,32 +115,25 @@ export default { 'structural-element': { data: { id: this.structuralElement.id, - type: this.structuralElement.type - } - } + type: this.structuralElement.type, + }, + }, }, }; - await this.createFeedback( data, { root: true }); + await this.createFeedback(data, { root: true }); this.feedbackText = ''; this.loadFeedback(); }, + deleteFeedback(feedback) { + this.deleteElementFeedback({ id: feedback.id, type: feedback.type }); + }, updateSrMessage(message) { this.srMessage = ''; this.srMessage = message; - } - }, - async mounted() { - await this.loadFeedback(); + }, }, updated() { this.$refs.feedbacks.scrollTop = this.$refs.feedbacks.scrollHeight; }, - watch: { - feedback() { - if (this.feedback && this.feedback.length > 0) { - this.$emit('hasFeedback'); - } - } - } -} - \ No newline at end of file +}; + diff --git a/resources/vue/components/courseware/widgets/CoursewareViewWidget.vue b/resources/vue/components/courseware/widgets/CoursewareViewWidget.vue index d8a8563..6f9d787 100644 --- a/resources/vue/components/courseware/widgets/CoursewareViewWidget.vue +++ b/resources/vue/components/courseware/widgets/CoursewareViewWidget.vue @@ -15,14 +15,6 @@ Bearbeiten -
  • - -
  • @@ -50,9 +42,6 @@ export default { editView() { return this.viewMode === 'edit'; }, - discussView() { - return this.viewMode === 'discuss'; - }, canEdit() { if (!this.structuralElement) { return false; @@ -77,9 +66,6 @@ export default { setEditView() { this.coursewareViewMode('edit'); }, - setDiscussView() { - this.coursewareViewMode('discuss'); - }, }, }; diff --git a/resources/vue/store/courseware/courseware.module.js b/resources/vue/store/courseware/courseware.module.js index 1f85d56..db13cf1 100644 --- a/resources/vue/store/courseware/courseware.module.js +++ b/resources/vue/store/courseware/courseware.module.js @@ -532,6 +532,32 @@ export const actions = { }, + async activateStructuralElementComments({ dispatch }, { element }) { + + element.attributes.commentable = true; + + const updatedElement = await dispatch('setStructuralElementComments', { element: element }); + + return updatedElement; + + }, + async deactivateStructuralElementComments({ dispatch }, { element }) { + + element.attributes.commentable = false; + + const updatedElement = await dispatch('setStructuralElementComments', { element: element }); + + return updatedElement; + }, + + async setStructuralElementComments({ dispatch }, { element }) { + await dispatch('lockObject', { id: element.id, type: 'courseware-structural-elements' }); + const updatedElement = await dispatch('courseware-structural-elements/update', element, { root: true }); + await dispatch('unlockObject', { id: element.id, type: 'courseware-structural-elements' }); + + return updatedElement; + }, + async createBlockInContainer({ dispatch }, { container, blockType }) { const block = { attributes: { @@ -609,6 +635,32 @@ export const actions = { return dispatch('loadContainer', containerId); }, + async activateBlockComments({ dispatch }, { block }) { + + block.attributes.commentable = true; + + const updatedBlock = await dispatch('setBlockComments', { block: block }); + + return updatedBlock; + + }, + async deactivateBlockComments({ dispatch }, { block }) { + + block.attributes.commentable = false; + + const updatedBlock = await dispatch('setBlockComments', { block: block }); + + return updatedBlock; + }, + + async setBlockComments({ dispatch }, { block }) { + await dispatch('lockObject', { id: block.id, type: 'courseware-blocks' }); + const updatedBlock = await dispatch('courseware-blocks/update', block, { root: true }); + await dispatch('unlockObject', { id: block.id, type: 'courseware-blocks' }); + + return updatedBlock; + }, + async storeCoursewareSettings({ dispatch, getters }, { permission, progression, certificateSettings, reminderSettings, resetProgressSettings }) { -- cgit v1.0