Implemented Forum

This commit is contained in:
Torsten Schulz
2024-10-24 11:22:40 +02:00
parent 663564aa96
commit f74a16e58e
8 changed files with 270 additions and 3 deletions

View File

@@ -58,6 +58,31 @@ const forumController = {
console.error('Error in createTopic:', error);
res.status(400).json({ error: error.message });
}
},
async getTopic(req, res) {
try {
const { userid: userId } = req.headers;
const { id: topicId } = req.params;
const topic = await forumService.getTopic(userId, topicId);
res.status(200).json(topic);
} catch (error) {
console.error('Error in getTopic:', error);
res.status(400).json({ error: error.message });
}
},
async addMessage(req, res) {
try {
const { userid: userId } = req.headers;
const { id: topicId } = req.params;
const { content } = req.body;
const result = await forumService.addMessage(userId, topicId, content);
res.status(201).json(result);
} catch (error) {
console.error('Error in addMessage:', error);
res.status(400).json({ error: error.message });
}
}
};

View File

@@ -2,6 +2,11 @@ import { sequelize } from '../../utils/sequelize.js';
import { DataTypes } from 'sequelize';
const Title = sequelize.define('title', {
id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true
},
title: {
type: DataTypes.STRING,
allowNull: false

View File

@@ -6,7 +6,9 @@ const forumRouter = Router();
forumRouter.use(authenticate);
forumRouter.post('/topic/:id/message', forumController.addMessage);
forumRouter.post('/topic', forumController.createTopic);
forumRouter.get('/topic/:id', forumController.getTopic);
forumRouter.post('/', forumController.createForum);
forumRouter.delete('/:forumId', forumController.deleteForum);
forumRouter.get('/:forumId/:page', forumController.getForum);

View File

@@ -196,6 +196,103 @@ class ForumService extends BaseService {
return this.getForum(hashedUserId, forumId, 1);
}
async getTopic(hashedUserId, topicId) {
console.log('[ForumService.getTopic] - start');
const user = await User.findOne({
where: {
hashedId: hashedUserId
}
});
if (!user) {
throw new Error('User not found.');
}
const topic = await Title.findByPk(topicId, {
include: [
{
model: Message,
as:'messages',
include: [
{
model: User,
as: 'lastMessageUser',
attributes: ['hashedId', 'username'],
order: [['createdAt', 'ASC']]
}
]
},
{
model: User,
as: 'createdByUser',
attributes: ['username', 'hashedId']
},
{
model: Forum,
as: 'forum',
attributes: ['id', 'name']
}
]
});
if (!topic) {
throw new Error('Topic not found.');
}
console.log('[ForumService.getTopic] - check user permissions');
const hasAccess = await this.checkForumAccess(topic.forum, user);
if (!hasAccess) {
throw new Error('Access denied.');
}
console.log('[ForumService.getTopic] - return topic');
return topic;
}
async addMessage(hashedUserId, topicId, content) {
console.log('[ForumService.addMessage] - start');
const user = await User.findOne({
where: {
hashedId: hashedUserId
}
});
if (!user) {
throw new Error('User not found.');
}
const topic = await Title.findByPk(topicId, {
include: [
{
model: Message,
as:'messages',
include: [
{
model: User,
as: 'lastMessageUser',
attributes: ['hashedId', 'username'],
order: [['createdAt', 'ASC']]
}
]
},
{
model: User,
as: 'createdByUser',
attributes: ['username', 'hashedId']
},
{
model: Forum,
as: 'forum',
attributes: ['id', 'name']
}
]
});
if (!topic) {
throw new Error('Topic not found.');
}
const hasAccess = await this.checkForumAccess(topic.forum, user);
if (!hasAccess) {
throw new Error('Access denied.');
}
console.log('[ForumService.addMessage] - create new message');
await Message.create({ titleId: topicId, text: content, createdBy: user.id });
console.log('[ForumService.addMessage] - return topic');
return this.getTopic(hashedUserId, topicId);
}
async checkForumAccess(forum, user) {
console.log('[ForumService.checkForumAccess] - start');
const { age } = user;

View File

@@ -229,7 +229,8 @@
"next": "Nächste Seite",
"last": "Letzte Seite",
"page": "Seite <<page>> von <<of>>"
}
},
"createNewMesssage": "Antwort senden"
}
}
}

View File

@@ -16,6 +16,7 @@ import GuestbookView from '../views/social/GuestbookView.vue';
import DiaryView from '../views/social/DiaryView.vue';
import ForumAdminView from '../dialogues/admin/ForumAdminView.vue';
import ForumView from '../views/social/ForumView.vue';
import ForumTopicView from '../views/social/ForumTopicView.vue';
const routes = [
{
@@ -52,6 +53,12 @@ const routes = [
component: ForumView,
meta: { requiresAuth: true }
},
{
path: '/socialnetwork/forumtopic/:id',
name: 'ForumTopic',
component: ForumTopicView,
meta: { requiresAuth: true }
},
{
path: '/socialnetwork/diary',
name: 'Diary',

View File

@@ -0,0 +1,127 @@
<template>
<h2 class="link" @click="openForum()">{{ $t('socialnetwork.forum.title') }} {{ forumName }}</h2>
<h3 v-if="forumTopic">{{ forumTopic }}</h3>
<ul class="messages">
<li v-for="message in messages">
<div v-html="message.text"></div>
<div class="footer">
<span class="link" @click="openProfile(message.lastMessageUser.hashedId)">{{
message.lastMessageUser.username }}</span>
<span>{{ new Date(message.createdAt).toLocaleString() }}</span>
</div>
</li>
</ul>
<editor v-model="newContent" :init="tinymceInitOptions" :api-key="apiKey"
tinymce-script-src="/tinymce/tinymce.min.js"></editor>
<button @click="saveNewMessage">{{ $t('socialnetwork.forum.createNewMesssage') }}</button>
</template>
<script>
import apiClient from '../../utils/axios';
import TinyMCEEditor from '@tinymce/tinymce-vue';
export default {
name: 'ForumTopicView',
components: {
editor: TinyMCEEditor,
},
data() {
return {
forumTopicId: String,
forumTopic: null,
forumName: null,
forumId: 0,
newContent: '',
apiKey: import.meta.env.VITE_TINYMCE_API_KEY ?? '',
tinymceInitOptions: {
script_url: '/tinymce/tinymce.min.js',
height: 300,
menubar: true,
plugins: [
'lists', 'link',
'searchreplace', 'visualblocks', 'code',
'insertdatetime', 'table'
],
toolbar:
'undo redo cut copy paste | bold italic forecolor backcolor fontfamily fontsize| \
alignleft aligncenter alignright alignjustify | \
bullist numlist outdent indent | removeformat | link visualblocks code',
contextmenu: 'link image table',
menubar: 'edit format table',
promotion: false,
},
}
},
async mounted() {
this.forumTopicId = this.$route.params.id;
this.loadForumTopic();
},
methods: {
async loadForumTopic() {
try {
console.log(this.forumTopicId);
const url = `/api/forum/topic/${this.forumTopicId}`;
console.log('url', url);
const response = await apiClient.get(url);
this.setContent(response.data);
} catch (error) {
console.error(error);
}
},
setContent(responseData) {
this.forumTopic = responseData.title;
this.forumName = responseData.forum.name;
this.forumId = responseData.forum.id;
this.messages = responseData.messages;
},
async openProfile(id) {
this.$root.$refs.userProfileDialog.userId = id;
this.$root.$refs.userProfileDialog.open();
},
async saveNewMessage() {
try {
const url = `/api/forum/topic/${this.forumTopicId}/message`;
const response = await apiClient.post(url, {
content: this.newContent,
});
this.newContent = '';
this.setContent(response.data);
} catch (error) {
console.error(error);
}
},
openForum() {
this.$router.push(`/socialnetwork/forum/${this.forumId}`);
}
}
}
</script>
<style lang="scss" scoped>
.messages {
list-style-type: none;
padding: 0;
margin: 0;
}
.messages > li {
border: 1px solid #7BBE55;
margin-bottom: 0.25em;
padding: 0.5em;
}
.messages > li > .footer {
color: #F9A22C;
font-size: 0.7em;
margin-top: 0.5em;
display: flex;
}
.messages>li>.footer>span:first-child {
flex: 1;
}
.messages > li > .footer > span:last-child {
text-align: right;
}
</style>

View File

@@ -11,7 +11,7 @@
<input type="text" v-model="newTitle" />
</label>
</div>
<editor v-model="newEntryContent" :init="tinymceInitOptions" :api-key="apiKey"
<editor v-model="newContent" :init="tinymceInitOptions" :api-key="apiKey"
tinymce-script-src="/tinymce/tinymce.min.js"></editor>
<button @click="saveNewTopic">{{ $t('socialnetwork.forum.createNewTopic') }}</button>
</div>
@@ -148,7 +148,10 @@ export default {
openProfile(id) {
this.$root.$refs.userProfileDialog.userId = id;
this.$root.$refs.userProfileDialog.open();
}
},
openTopic(topicId) {
this.$router.push(`/socialnetwork/forumtopic/${topicId}`);
},
}
}
</script>