Skip to content
Snippets Groups Projects
Commit ba52642f authored by Ron Lucke's avatar Ron Lucke
Browse files

StEP #3255

Merge request studip/studip!2355
parent e09c643e
No related branches found
No related tags found
No related merge requests found
Showing
with 221 additions and 154 deletions
<?php
final class CoursewareAddOptionalComments extends Migration
{
public function description()
{
return 'Add column commentable to cw_blocks and cw_structural_elements';
}
protected function up()
{
DBManager::get()->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`");
}
}
......@@ -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);
}
......
......@@ -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');
......
......@@ -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');
}
......
......@@ -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();
......
......@@ -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);
......
......@@ -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([
......
......@@ -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']),
......
......@@ -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']),
......
......@@ -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));
......
......@@ -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();
......
......@@ -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';
......
.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
.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;
......@@ -55,3 +49,19 @@
.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
......@@ -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;
......
@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
$color: var(--base-color-20);
$ownColor: var(--petrol-40);
.cw-talk-bubble-wrapper {
display: flex;
flex-direction: row;
justify-content: start;
.cw-talk-bubble-avatar {
padding: 8px;
}
.cw-talk-bubble {
margin: 10px 20px;
position: relative;
width: 85%;
max-width: 80%;
height: auto;
background-color: var(--dark-gray-color-10);
float: left;
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 {
padding: 1em;
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 {
color: var(--dark-gray-color-80);
text-align: right;
font-size: 0.8em;
margin-bottom: -0.5em;
}
button {
border: none;
background-color: transparent;
vertical-align: middle;
cursor: pointer;
}
}
&.cw-talk-bubble-own-post {
float: right;
}
}
&:after {
......@@ -30,29 +69,31 @@
height: 0;
top: 0px;
bottom: auto;
border: 22px solid;
border-color: var(--dark-gray-color-10) transparent transparent transparent;
left: -20px;
border: 16px solid;
border-color: $color transparent transparent transparent;
border-radius: 4px;
left: -14px;
right: auto;
}
&.cw-talk-bubble-own-post:after {
left: auto;
right: -20px;
}
.cw-talk-bubble-user {
padding: 1em 1em 0 1em;
&.cw-talk-bubble-own-post {
justify-content: end;
.cw-talk-bubble-avatar {
display: inline-block;
.cw-talk-bubble {
flex-direction: row-reverse;
background-color: $ownColor;
&: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
......@@ -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 {
......@@ -239,35 +236,3 @@
}
}
}
\ No newline at end of file
@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
......@@ -9,6 +9,9 @@
@deleteBlock="deleteBlock"
@removeLock="removeLock"
@copyToClipboard="copyToClipboard"
@deactivateComments="deactivateComments"
@activateComments="activateComments"
@showFeedback="showFeedback"
/>
</div>
</template>
......@@ -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');
}
},
};
......
......@@ -7,6 +7,7 @@
v-for="comment in comments"
:key="comment.id"
:payload="buildPayload(comment)"
@delete="deleteComment(comment)"
/>
</div>
<div class="cw-block-comment-create">
......@@ -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,
};
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment