diff options
| author | Murtaza Sultani <sultani@data-quest.de> | 2025-11-19 09:33:25 +0100 |
|---|---|---|
| committer | Jan-Hendrik Willms <tleilax+studip@gmail.com> | 2026-02-27 13:54:05 +0100 |
| commit | e6a106f2314239d8c7f7781058dbf7e99d403675 (patch) | |
| tree | ff46cfed8ff40dbcc6b075aa5af0178015137208 | |
| parent | 06efdbcb518a77891c748193f6193dc0ec79e2ea (diff) | |
Resolve "Forum: Refaktorierung der Kategorie, Topic und Diskussions Metadaten sowie Behebung der fehlerhaften Anzeige ungelesener Beiträge"
Closes #6058
Merge request studip/studip!4613
| -rw-r--r-- | lib/classes/JsonApi/Schemas/Forum/Category.php | 15 | ||||
| -rw-r--r-- | lib/classes/JsonApi/Schemas/Forum/Discussion.php | 11 | ||||
| -rw-r--r-- | lib/classes/JsonApi/Schemas/Forum/Topic.php | 13 | ||||
| -rw-r--r-- | lib/models/Forum/Category.php | 22 | ||||
| -rw-r--r-- | lib/models/Forum/Discussion.php | 26 | ||||
| -rw-r--r-- | lib/models/Forum/Topic.php | 21 | ||||
| -rw-r--r-- | resources/vue/components/forum/categories/CategoryItem.vue | 16 | ||||
| -rw-r--r-- | resources/vue/components/forum/discussions/DiscussionIndex.vue | 8 | ||||
| -rw-r--r-- | resources/vue/components/forum/topics/TopicItem.vue | 16 | ||||
| -rw-r--r-- | resources/vue/store/pinia/forum/ForumPost.js | 9 |
10 files changed, 106 insertions, 51 deletions
diff --git a/lib/classes/JsonApi/Schemas/Forum/Category.php b/lib/classes/JsonApi/Schemas/Forum/Category.php index 5e3012b..0835937 100644 --- a/lib/classes/JsonApi/Schemas/Forum/Category.php +++ b/lib/classes/JsonApi/Schemas/Forum/Category.php @@ -46,15 +46,16 @@ class Category extends SchemaProvider */ public function getResourceMeta($resource) { - $metaData = $resource->getMetaData(); + $metadata = $resource->getMetadata(); return [ - 'topics-count' => (int) $metaData['topics_count'], - 'discussions-count' => (int) $metaData['discussions_count'], - 'postings-count' => (int) $metaData['postings_count'], - 'user-read-index' => (int) $metaData['user_read_index'], - 'users-count' => (int) $metaData['users_count'], - 'recent-activity' => $metaData['recent_activity'] ? date('c', $metaData['recent_activity']) : '', + 'topics-count' => (int) $metadata['topics_count'], + 'discussions-count' => (int) $metadata['discussions_count'], + 'postings-count' => (int) $metadata['postings_count'], + 'unread-postings-count' => (int) $metadata['unread_postings_count'], + 'user-read-index' => (int) $metadata['user_read_index'], + 'users-count' => (int) $metadata['users_count'], + 'recent-activity' => $metadata['recent_activity'] ? date('c', $metadata['recent_activity']) : '', ]; } diff --git a/lib/classes/JsonApi/Schemas/Forum/Discussion.php b/lib/classes/JsonApi/Schemas/Forum/Discussion.php index 9252aaa..a8c5c57 100644 --- a/lib/classes/JsonApi/Schemas/Forum/Discussion.php +++ b/lib/classes/JsonApi/Schemas/Forum/Discussion.php @@ -53,13 +53,14 @@ class Discussion extends SchemaProvider */ public function getResourceMeta($resource) { - $metaData = $resource->getMetaData(); + $metadata = $resource->getMetadata(); return [ - 'postings-count' => (int) $metaData['postings_count'], - 'recent-postings-count' => (int) $metaData['recent_postings_count'], - 'user-read-index' => (int) $metaData['user_read_index'], - 'recent-activity' => $metaData['recent_activity'] ? date('c', $metaData['recent_activity']) : '' + 'postings-count' => (int) $metadata['postings_count'], + 'recent-postings-count' => (int) $metadata['recent_postings_count'], + 'unread-postings-count' => (int) $metadata['unread_postings_count'], + 'user-read-index' => (int) $metadata['user_read_index'], + 'recent-activity' => $metadata['recent_activity'] ? date('c', $metadata['recent_activity']) : '' ]; } diff --git a/lib/classes/JsonApi/Schemas/Forum/Topic.php b/lib/classes/JsonApi/Schemas/Forum/Topic.php index 22a8c83..6a9659b 100644 --- a/lib/classes/JsonApi/Schemas/Forum/Topic.php +++ b/lib/classes/JsonApi/Schemas/Forum/Topic.php @@ -47,14 +47,15 @@ class Topic extends SchemaProvider */ public function getResourceMeta($resource) { - $metaData = $resource->getMetaData(); + $metadata = $resource->getMetadata(); return [ - 'discussions-count' => (int) $metaData['discussions_count'], - 'postings-count' => (int) $metaData['postings_count'], - 'user-read-index' => (int) $metaData['user_read_index'], - 'users-count' => (int) $metaData['users_count'], - 'recent-activity' => $metaData['recent_activity'] ? date('c', $metaData['recent_activity']) : '', + 'discussions-count' => (int) $metadata['discussions_count'], + 'postings-count' => (int) $metadata['postings_count'], + 'unread-postings-count' => (int) $metadata['unread_postings_count'], + 'user-read-index' => (int) $metadata['user_read_index'], + 'users-count' => (int) $metadata['users_count'], + 'recent-activity' => $metadata['recent_activity'] ? date('c', $metadata['recent_activity']) : '', ]; } diff --git a/lib/models/Forum/Category.php b/lib/models/Forum/Category.php index 6ee52c8..74cd189 100644 --- a/lib/models/Forum/Category.php +++ b/lib/models/Forum/Category.php @@ -56,9 +56,9 @@ class Category extends \SimpleORMap return self::findBySQL("range_id = ? ORDER BY position ASC, mkdate DESC", [$range_id]); } - public function getMetaData(): array + public function getMetadata(): array { - return DBManager::get()->fetchOne( + $metadata= DBManager::get()->fetchOne( "SELECT COUNT(DISTINCT`forum_topics`.`topic_id`) AS 'topics_count', COUNT(DISTINCT `forum_discussions`.`discussion_id`) AS 'discussions_count', @@ -73,7 +73,15 @@ class Category extends \SimpleORMap ON fpr.discussion_id = fd2.discussion_id AND fpr.user_id = :user_id WHERE ft2.category_id = :category_id - ) AS 'user_read_index' + ) AS 'user_read_index', + ( + SELECT + COUNT(DISTINCT fp.posting_id) + FROM forum_postings fp + JOIN `forum_discussions` fd USING (discussion_id) + JOIN `forum_topics` ft USING (topic_id) + WHERE ft.category_id = :category_id AND fp.user_id != :user_id + ) AS 'others_postings_count' FROM `forum_topics` LEFT JOIN `forum_discussions` USING (`topic_id`) LEFT JOIN `forum_postings` USING (`discussion_id`) @@ -83,6 +91,14 @@ class Category extends \SimpleORMap 'user_id' => User::findCurrent()?->user_id ] ); + + return [ + ...$metadata, + 'unread_postings_count' => max( + 0, + $metadata['others_postings_count'] - (int) $metadata['user_read_index'] + ) + ]; } public function transformData(): array diff --git a/lib/models/Forum/Discussion.php b/lib/models/Forum/Discussion.php index de82422..5b8edc5 100644 --- a/lib/models/Forum/Discussion.php +++ b/lib/models/Forum/Discussion.php @@ -240,7 +240,7 @@ class Discussion extends SimpleORMap ]; } - public function getMetaData(int $last_visit = 0): array + public function getMetadata(int $last_visit = 0): array { $user_id = User::findCurrent()?->user_id; @@ -249,7 +249,7 @@ class Discussion extends SimpleORMap $last_visit = object_get_visit($this->topic->range_id, $plugin_id, 'last', '', $user_id); } - return DBManager::get()->fetchOne( + $metadata = DBManager::get()->fetchOne( "SELECT COUNT(`posting_id`) 'postings_count', MAX(`mkdate`) 'recent_activity', @@ -261,10 +261,16 @@ class Discussion extends SimpleORMap ) 'user_read_index', ( SELECT - COUNT(DISTINCT fp.posting_id) - FROM forum_postings fp - WHERE fp.discussion_id = :discussion_id AND fp.mkdate > :last_visit - ) AS 'recent_postings_count' + COUNT(DISTINCT posting_id) + FROM forum_postings + WHERE discussion_id = :discussion_id AND mkdate > :last_visit AND `user_id` != :user_id + ) AS 'recent_postings_count', + ( + SELECT + COUNT(DISTINCT posting_id) + FROM forum_postings + WHERE discussion_id = :discussion_id AND `user_id` != :user_id + ) AS 'others_postings_count' FROM `forum_postings` WHERE `discussion_id` = :discussion_id", [ 'discussion_id' => $this->discussion_id, @@ -272,6 +278,14 @@ class Discussion extends SimpleORMap 'last_visit' => $last_visit ] ); + + return [ + ...$metadata, + 'unread_postings_count' => max( + 0, + $metadata['others_postings_count'] - (int) $metadata['user_read_index'] + ) + ]; } public function onCreate(): void diff --git a/lib/models/Forum/Topic.php b/lib/models/Forum/Topic.php index 2bf4c27..9b5ce7f 100644 --- a/lib/models/Forum/Topic.php +++ b/lib/models/Forum/Topic.php @@ -119,9 +119,9 @@ class Topic extends SimpleORMap ); } - public function getMetaData(): array + public function getMetadata(): array { - return DBManager::get()->fetchOne( + $metadata = DBManager::get()->fetchOne( "SELECT COUNT(DISTINCT `forum_discussions`.`discussion_id`) AS 'discussions_count', COUNT(DISTINCT `forum_postings`.`posting_id`) AS 'postings_count', @@ -135,7 +135,14 @@ class Topic extends SimpleORMap ON fpr.discussion_id = fd2.discussion_id AND fpr.user_id = :user_id WHERE fd2.topic_id = :topic_id - ) AS 'user_read_index' + ) AS 'user_read_index', + ( + SELECT + COUNT(DISTINCT fp.posting_id) + FROM forum_postings fp + JOIN `forum_discussions` fd USING (discussion_id) + WHERE fd.topic_id = :topic_id AND fp.user_id != :user_id + ) AS 'others_postings_count' FROM `forum_discussions` LEFT JOIN `forum_postings` USING (`discussion_id`) WHERE `forum_discussions`.`topic_id` = :topic_id", @@ -144,6 +151,14 @@ class Topic extends SimpleORMap 'user_id' => User::findCurrent()?->user_id ] ); + + return [ + ...$metadata, + 'unread_postings_count' => max( + 0, + $metadata['others_postings_count'] - (int) $metadata['user_read_index'] + ) + ]; } public function transformData(): array diff --git a/resources/vue/components/forum/categories/CategoryItem.vue b/resources/vue/components/forum/categories/CategoryItem.vue index 9b3bf12..291f14f 100644 --- a/resources/vue/components/forum/categories/CategoryItem.vue +++ b/resources/vue/components/forum/categories/CategoryItem.vue @@ -87,14 +87,14 @@ const swapCategory = event => { :title="$gettext('Zur Kategorie')"> <span class="category-title line-clamp-2">{{ category.name }}</span> <span - v-if="!forumConfig.allowGuestAccess && category.meta.postings_count > category.meta.user_read_index" + v-if="!forumConfig.allowGuestAccess && category.meta.unread_postings_count" class="unread-items-badge" role="status" aria-live="polite" - :aria-label="$gettext('Sie haben %{count} ungelesene Beiträge', {count: category.meta.postings_count - category.meta.user_read_index})" - :title="$gettext('Sie haben %{count} ungelesene Beiträge', {count: category.meta.postings_count - category.meta.user_read_index})" + :aria-label="$gettext('Sie haben %{count} ungelesene Beiträge', {count: category.meta.unread_postings_count})" + :title="$gettext('Sie haben %{count} ungelesene Beiträge', {count: category.meta.unread_postings_count})" > - {{ category.meta.postings_count - category.meta.user_read_index }} + {{ category.meta.unread_postings_count }} </span> </a> </div> @@ -203,14 +203,14 @@ const swapCategory = event => { </span> <span - v-if="!forumConfig.allowGuestAccess && category.meta.postings_count > category.meta.user_read_index" + v-if="!forumConfig.allowGuestAccess && category.meta.unread_postings_count" class="unread-items-badge" role="status" aria-live="polite" - :aria-label="$gettext('Sie haben %{count} ungelesene Beiträge', {count: category.meta.postings_count - category.meta.user_read_index})" - :title="$gettext('Sie haben %{count} ungelesene Beiträge', {count: category.meta.postings_count - category.meta.user_read_index})" + :aria-label="$gettext('Sie haben %{count} ungelesene Beiträge', {count: category.meta.unread_postings_count})" + :title="$gettext('Sie haben %{count} ungelesene Beiträge', {count: category.meta.unread_postings_count})" > - {{ category.meta.postings_count - category.meta.user_read_index }} + {{ category.meta.unread_postings_count }} </span> </div> <div class="actions"> diff --git a/resources/vue/components/forum/discussions/DiscussionIndex.vue b/resources/vue/components/forum/discussions/DiscussionIndex.vue index f5295fa..f5c5d32 100644 --- a/resources/vue/components/forum/discussions/DiscussionIndex.vue +++ b/resources/vue/components/forum/discussions/DiscussionIndex.vue @@ -169,14 +169,14 @@ onMounted(() => { <span class="title-with-actions_title discussion-title line-clamp-2 m-0">{{ discussion.title }}</span> <template v-if="!forumConfig.allowGuestAccess"> <span - v-if="redirect !== 'recent' && discussion.meta.postings_count > discussion.meta.user_read_index" + v-if="redirect !== 'recent' && discussion.meta.unread_postings_count" class="unread-items-badge" role="status" aria-live="polite" - :aria-label="$gettext('Sie haben %{count} ungelesene Beiträge.', {count: discussion.meta.postings_count - discussion.meta.user_read_index})" - :title="$gettext('Sie haben %{count} ungelesene Beiträge.', {count: discussion.meta.postings_count - discussion.meta.user_read_index})" + :aria-label="$gettext('Sie haben %{count} ungelesene Beiträge.', {count: discussion.meta.unread_postings_count})" + :title="$gettext('Sie haben %{count} ungelesene Beiträge.', {count: discussion.meta.unread_postings_count})" > - {{ discussion.meta.postings_count - discussion.meta.user_read_index }} + {{ discussion.meta.unread_postings_count }} </span> <span v-if="redirect === 'recent' && discussion.meta.recent_postings_count" diff --git a/resources/vue/components/forum/topics/TopicItem.vue b/resources/vue/components/forum/topics/TopicItem.vue index 06ee10e..a7d62d2 100644 --- a/resources/vue/components/forum/topics/TopicItem.vue +++ b/resources/vue/components/forum/topics/TopicItem.vue @@ -83,14 +83,14 @@ const swapTopic = event => { <a class="title-with-actions__link" :href="getTopicURL(topic.id)" :title="$gettext('Zum Thema')"> <span class="topic-title line-clamp-2">{{ topic.name }}</span> <span - v-if="!forumConfig.allowGuestAccess && topic.meta.postings_count > topic.meta.user_read_index" + v-if="!forumConfig.allowGuestAccess && topic.meta.unread_postings_count" class="unread-items-badge" role="status" aria-live="polite" - :aria-label="$gettext('Sie haben %{count} ungelesene Beiträge', {count: topic.meta.postings_count - topic.meta.user_read_index})" - :title="$gettext('Sie haben %{count} ungelesene Beiträge', {count: topic.meta.postings_count - topic.meta.user_read_index})" + :aria-label="$gettext('Sie haben %{count} ungelesene Beiträge', {count: topic.meta.unread_postings_count})" + :title="$gettext('Sie haben %{count} ungelesene Beiträge', {count: topic.meta.unread_postings_count})" > - {{ topic.meta.postings_count - topic.meta.user_read_index }} + {{ topic.meta.unread_postings_count }} </span> </a> </div> @@ -191,14 +191,14 @@ const swapTopic = event => { </span> <span - v-if="!forumConfig.allowGuestAccess && topic.meta.postings_count > topic.meta.user_read_index" + v-if="!forumConfig.allowGuestAccess && topic.meta.unread_postings_count" class="unread-items-badge" role="status" aria-live="polite" - :aria-label="$gettext('Sie haben %{count} ungelesene Beiträge', {count: topic.meta.postings_count - topic.meta.user_read_index})" - :title="$gettext('Sie haben %{count} ungelesene Beiträge', {count: topic.meta.postings_count - topic.meta.user_read_index})" + :aria-label="$gettext('Sie haben %{count} ungelesene Beiträge', {count: topic.meta.unread_postings_count})" + :title="$gettext('Sie haben %{count} ungelesene Beiträge', {count: topic.meta.unread_postings_count})" > - {{ topic.meta.postings_count - topic.meta.user_read_index }} + {{ topic.meta.unread_postings_count }} </span> </div> diff --git a/resources/vue/store/pinia/forum/ForumPost.js b/resources/vue/store/pinia/forum/ForumPost.js index 3990ccc..20ede25 100644 --- a/resources/vue/store/pinia/forum/ForumPost.js +++ b/resources/vue/store/pinia/forum/ForumPost.js @@ -5,6 +5,7 @@ export const useForumPost = defineStore( () => { const posts = ref([]); + const firstUnreadPostIndex = ref(-1); function initPosts(newPosts) { posts.value = newPosts; @@ -40,14 +41,20 @@ export const useForumPost = defineStore( } } + function updateFirstUnreadPostIndex(index) { + firstUnreadPostIndex.value = index; + } + return { posts, + firstUnreadPostIndex, initPosts, addPost, updatePost, removePost, addPostReaction, - removePostReaction + removePostReaction, + updateFirstUnreadPostIndex } } ) |
