deletion reason and email for soft deleted forum posts and comments by admin
This commit is contained in:
@@ -55,6 +55,10 @@ const ForumComment = sequelize.define('ForumComment', {
|
||||
deletedAt: {
|
||||
type: DataTypes.DATE,
|
||||
allowNull: true
|
||||
},
|
||||
deletionReason: {
|
||||
type: DataTypes.TEXT,
|
||||
allowNull: true
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -85,6 +85,10 @@ const ForumPost = sequelize.define('ForumPost', {
|
||||
type: DataTypes.DATE,
|
||||
allowNull: true
|
||||
},
|
||||
deletionReason: {
|
||||
type: DataTypes.TEXT,
|
||||
allowNull: true
|
||||
},
|
||||
closedBy: {
|
||||
type: DataTypes.UUID,
|
||||
allowNull: true,
|
||||
|
||||
@@ -1250,26 +1250,68 @@ router.get('/tags', async (req, res) => {
|
||||
// DELETE /api/forum/admin/posts/:id - Admin soft-delete post
|
||||
router.delete('/admin/posts/:id', authenticateToken, requireAdmin, async (req, res) => {
|
||||
try {
|
||||
const post = await ForumPost.findByPk(req.params.id);
|
||||
const { reason } = req.body;
|
||||
|
||||
if (!reason || !reason.trim()) {
|
||||
return res.status(400).json({ error: "Deletion reason is required" });
|
||||
}
|
||||
|
||||
const post = await ForumPost.findByPk(req.params.id, {
|
||||
include: [
|
||||
{
|
||||
model: User,
|
||||
as: 'author',
|
||||
attributes: ['id', 'username', 'firstName', 'lastName', 'email']
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
if (!post) {
|
||||
return res.status(404).json({ error: 'Post not found' });
|
||||
}
|
||||
|
||||
if (post.isDeleted) {
|
||||
return res.status(400).json({ error: 'Post is already deleted' });
|
||||
}
|
||||
|
||||
// Soft delete the post
|
||||
await post.update({
|
||||
isDeleted: true,
|
||||
deletedBy: req.user.id,
|
||||
deletedAt: new Date()
|
||||
deletedAt: new Date(),
|
||||
deletionReason: reason.trim()
|
||||
});
|
||||
|
||||
const reqLogger = logger.withRequestId(req.id);
|
||||
reqLogger.info("Admin deleted post", {
|
||||
postId: req.params.id,
|
||||
adminId: req.user.id,
|
||||
originalAuthorId: post.authorId
|
||||
originalAuthorId: post.authorId,
|
||||
reason: reason.trim()
|
||||
});
|
||||
|
||||
// Send email notification to post author (non-blocking)
|
||||
(async () => {
|
||||
try {
|
||||
const admin = await User.findByPk(req.user.id, {
|
||||
attributes: ['id', 'username', 'firstName', 'lastName']
|
||||
});
|
||||
|
||||
if (post.author && admin) {
|
||||
await emailServices.forum.sendForumPostDeletionNotification(
|
||||
post.author,
|
||||
admin,
|
||||
post,
|
||||
reason.trim()
|
||||
);
|
||||
console.log(`Forum post deletion notification email sent to author ${post.authorId}`);
|
||||
}
|
||||
} catch (emailError) {
|
||||
// Log but don't fail the deletion
|
||||
console.error('Failed to send forum post deletion notification email:', emailError.message);
|
||||
}
|
||||
})();
|
||||
|
||||
res.status(200).json({ message: 'Post deleted successfully', post });
|
||||
} catch (error) {
|
||||
const reqLogger = logger.withRequestId(req.id);
|
||||
@@ -1292,11 +1334,16 @@ router.patch('/admin/posts/:id/restore', authenticateToken, requireAdmin, async
|
||||
return res.status(404).json({ error: 'Post not found' });
|
||||
}
|
||||
|
||||
if (!post.isDeleted) {
|
||||
return res.status(400).json({ error: 'Post is not deleted' });
|
||||
}
|
||||
|
||||
// Restore the post
|
||||
await post.update({
|
||||
isDeleted: false,
|
||||
deletedBy: null,
|
||||
deletedAt: null
|
||||
deletedAt: null,
|
||||
deletionReason: null
|
||||
});
|
||||
|
||||
const reqLogger = logger.withRequestId(req.id);
|
||||
@@ -1322,25 +1369,44 @@ router.patch('/admin/posts/:id/restore', authenticateToken, requireAdmin, async
|
||||
// DELETE /api/forum/admin/comments/:id - Admin soft-delete comment
|
||||
router.delete('/admin/comments/:id', authenticateToken, requireAdmin, async (req, res) => {
|
||||
try {
|
||||
const comment = await ForumComment.findByPk(req.params.id);
|
||||
const { reason } = req.body;
|
||||
|
||||
if (!reason || !reason.trim()) {
|
||||
return res.status(400).json({ error: "Deletion reason is required" });
|
||||
}
|
||||
|
||||
const comment = await ForumComment.findByPk(req.params.id, {
|
||||
include: [
|
||||
{
|
||||
model: User,
|
||||
as: 'author',
|
||||
attributes: ['id', 'username', 'firstName', 'lastName', 'email']
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
if (!comment) {
|
||||
return res.status(404).json({ error: 'Comment not found' });
|
||||
}
|
||||
|
||||
if (comment.isDeleted) {
|
||||
return res.status(400).json({ error: 'Comment is already deleted' });
|
||||
}
|
||||
|
||||
// Soft delete the comment (preserve original content for potential restoration)
|
||||
await comment.update({
|
||||
isDeleted: true,
|
||||
deletedBy: req.user.id,
|
||||
deletedAt: new Date()
|
||||
deletedAt: new Date(),
|
||||
deletionReason: reason.trim()
|
||||
});
|
||||
|
||||
// Decrement comment count if not already deleted
|
||||
if (!comment.isDeleted) {
|
||||
const post = await ForumPost.findByPk(comment.postId);
|
||||
if (post && post.commentCount > 0) {
|
||||
await post.decrement('commentCount');
|
||||
}
|
||||
// Decrement comment count
|
||||
const post = await ForumPost.findByPk(comment.postId, {
|
||||
attributes: ['id', 'title']
|
||||
});
|
||||
if (post && post.commentCount > 0) {
|
||||
await post.decrement('commentCount');
|
||||
}
|
||||
|
||||
const reqLogger = logger.withRequestId(req.id);
|
||||
@@ -1348,9 +1414,32 @@ router.delete('/admin/comments/:id', authenticateToken, requireAdmin, async (req
|
||||
commentId: req.params.id,
|
||||
adminId: req.user.id,
|
||||
originalAuthorId: comment.authorId,
|
||||
postId: comment.postId
|
||||
postId: comment.postId,
|
||||
reason: reason.trim()
|
||||
});
|
||||
|
||||
// Send email notification to comment author (non-blocking)
|
||||
(async () => {
|
||||
try {
|
||||
const admin = await User.findByPk(req.user.id, {
|
||||
attributes: ['id', 'username', 'firstName', 'lastName']
|
||||
});
|
||||
|
||||
if (comment.author && admin && post) {
|
||||
await emailServices.forum.sendForumCommentDeletionNotification(
|
||||
comment.author,
|
||||
admin,
|
||||
post,
|
||||
reason.trim()
|
||||
);
|
||||
console.log(`Forum comment deletion notification email sent to author ${comment.authorId}`);
|
||||
}
|
||||
} catch (emailError) {
|
||||
// Log but don't fail the deletion
|
||||
console.error('Failed to send forum comment deletion notification email:', emailError.message);
|
||||
}
|
||||
})();
|
||||
|
||||
res.status(200).json({ message: 'Comment deleted successfully', comment });
|
||||
} catch (error) {
|
||||
const reqLogger = logger.withRequestId(req.id);
|
||||
@@ -1381,7 +1470,8 @@ router.patch('/admin/comments/:id/restore', authenticateToken, requireAdmin, asy
|
||||
await comment.update({
|
||||
isDeleted: false,
|
||||
deletedBy: null,
|
||||
deletedAt: null
|
||||
deletedAt: null,
|
||||
deletionReason: null
|
||||
});
|
||||
|
||||
// Increment comment count
|
||||
|
||||
@@ -483,7 +483,8 @@ router.patch("/admin/:id/restore", authenticateToken, requireAdmin, async (req,
|
||||
await item.update({
|
||||
isDeleted: false,
|
||||
deletedBy: null,
|
||||
deletedAt: null
|
||||
deletedAt: null,
|
||||
deletionReason: null
|
||||
});
|
||||
|
||||
const updatedItem = await Item.findByPk(item.id, {
|
||||
|
||||
@@ -65,6 +65,8 @@ class TemplateManager {
|
||||
"forumThreadActivityToParticipant.html",
|
||||
"forumPostClosed.html",
|
||||
"forumItemRequestNotification.html",
|
||||
"forumPostDeletionToAuthor.html",
|
||||
"forumCommentDeletionToAuthor.html",
|
||||
];
|
||||
|
||||
for (const templateFile of templateFiles) {
|
||||
|
||||
@@ -9,6 +9,7 @@ const TemplateManager = require("../core/TemplateManager");
|
||||
* - Sending answer accepted notifications
|
||||
* - Sending thread activity notifications to participants
|
||||
* - Sending location-based item request notifications to nearby users
|
||||
* - Sending post/comment deletion notifications to authors
|
||||
*/
|
||||
class ForumEmailService {
|
||||
constructor() {
|
||||
@@ -386,6 +387,128 @@ class ForumEmailService {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send notification when a forum post is deleted by admin
|
||||
* @param {Object} postAuthor - Post author user object
|
||||
* @param {string} postAuthor.firstName - Post author's first name
|
||||
* @param {string} postAuthor.email - Post author's email
|
||||
* @param {Object} admin - Admin user object who deleted the post
|
||||
* @param {string} admin.firstName - Admin's first name
|
||||
* @param {string} admin.lastName - Admin's last name
|
||||
* @param {Object} post - Forum post object
|
||||
* @param {string} post.title - Post title
|
||||
* @param {string} deletionReason - Reason for deletion
|
||||
* @returns {Promise<{success: boolean, messageId?: string, error?: string}>}
|
||||
*/
|
||||
async sendForumPostDeletionNotification(postAuthor, admin, post, deletionReason) {
|
||||
if (!this.initialized) {
|
||||
await this.initialize();
|
||||
}
|
||||
|
||||
try {
|
||||
const supportEmail = process.env.SUPPORT_EMAIL;
|
||||
const frontendUrl = process.env.FRONTEND_URL || "http://localhost:3000";
|
||||
|
||||
const variables = {
|
||||
postAuthorName: postAuthor.firstName || "there",
|
||||
adminName: `${admin.firstName} ${admin.lastName}`.trim() || "An administrator",
|
||||
postTitle: post.title,
|
||||
deletionReason,
|
||||
supportEmail,
|
||||
forumUrl: `${frontendUrl}/forum`,
|
||||
};
|
||||
|
||||
const htmlContent = await this.templateManager.renderTemplate(
|
||||
"forumPostDeletionToAuthor",
|
||||
variables
|
||||
);
|
||||
|
||||
const subject = `Important: Your forum post "${post.title}" has been removed`;
|
||||
|
||||
const result = await this.emailClient.sendEmail(
|
||||
postAuthor.email,
|
||||
subject,
|
||||
htmlContent
|
||||
);
|
||||
|
||||
if (result.success) {
|
||||
console.log(
|
||||
`Forum post deletion notification email sent to ${postAuthor.email}`
|
||||
);
|
||||
}
|
||||
|
||||
return result;
|
||||
} catch (error) {
|
||||
console.error(
|
||||
"Failed to send forum post deletion notification email:",
|
||||
error
|
||||
);
|
||||
return { success: false, error: error.message };
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send notification when a forum comment is deleted by admin
|
||||
* @param {Object} commentAuthor - Comment author user object
|
||||
* @param {string} commentAuthor.firstName - Comment author's first name
|
||||
* @param {string} commentAuthor.email - Comment author's email
|
||||
* @param {Object} admin - Admin user object who deleted the comment
|
||||
* @param {string} admin.firstName - Admin's first name
|
||||
* @param {string} admin.lastName - Admin's last name
|
||||
* @param {Object} post - Forum post object the comment belongs to
|
||||
* @param {string} post.title - Post title
|
||||
* @param {string} post.id - Post ID
|
||||
* @param {string} deletionReason - Reason for deletion
|
||||
* @returns {Promise<{success: boolean, messageId?: string, error?: string}>}
|
||||
*/
|
||||
async sendForumCommentDeletionNotification(commentAuthor, admin, post, deletionReason) {
|
||||
if (!this.initialized) {
|
||||
await this.initialize();
|
||||
}
|
||||
|
||||
try {
|
||||
const supportEmail = process.env.SUPPORT_EMAIL;
|
||||
const frontendUrl = process.env.FRONTEND_URL || "http://localhost:3000";
|
||||
const postUrl = `${frontendUrl}/forum/posts/${post.id}`;
|
||||
|
||||
const variables = {
|
||||
commentAuthorName: commentAuthor.firstName || "there",
|
||||
adminName: `${admin.firstName} ${admin.lastName}`.trim() || "An administrator",
|
||||
postTitle: post.title,
|
||||
postUrl,
|
||||
deletionReason,
|
||||
supportEmail,
|
||||
};
|
||||
|
||||
const htmlContent = await this.templateManager.renderTemplate(
|
||||
"forumCommentDeletionToAuthor",
|
||||
variables
|
||||
);
|
||||
|
||||
const subject = `Your comment on "${post.title}" has been removed`;
|
||||
|
||||
const result = await this.emailClient.sendEmail(
|
||||
commentAuthor.email,
|
||||
subject,
|
||||
htmlContent
|
||||
);
|
||||
|
||||
if (result.success) {
|
||||
console.log(
|
||||
`Forum comment deletion notification email sent to ${commentAuthor.email}`
|
||||
);
|
||||
}
|
||||
|
||||
return result;
|
||||
} catch (error) {
|
||||
console.error(
|
||||
"Failed to send forum comment deletion notification email:",
|
||||
error
|
||||
);
|
||||
return { success: false, error: error.message };
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send notification to nearby users about an item request
|
||||
* @param {Object} recipient - Recipient user object
|
||||
|
||||
314
backend/templates/emails/forumCommentDeletionToAuthor.html
Normal file
314
backend/templates/emails/forumCommentDeletionToAuthor.html
Normal file
@@ -0,0 +1,314 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<title>Your Forum Comment Has Been Removed</title>
|
||||
<style>
|
||||
/* Reset styles */
|
||||
body, table, td, p, a, li, blockquote {
|
||||
-webkit-text-size-adjust: 100%;
|
||||
-ms-text-size-adjust: 100%;
|
||||
}
|
||||
table, td {
|
||||
mso-table-lspace: 0pt;
|
||||
mso-table-rspace: 0pt;
|
||||
}
|
||||
img {
|
||||
-ms-interpolation-mode: bicubic;
|
||||
}
|
||||
|
||||
/* Base styles */
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
width: 100% !important;
|
||||
min-width: 100%;
|
||||
height: 100%;
|
||||
background-color: #f8f9fa;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
|
||||
line-height: 1.6;
|
||||
color: #212529;
|
||||
}
|
||||
|
||||
/* Container */
|
||||
.email-container {
|
||||
max-width: 600px;
|
||||
margin: 0 auto;
|
||||
background-color: #ffffff;
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
/* Header - Warning red gradient */
|
||||
.header {
|
||||
background: linear-gradient(135deg, #dc3545 0%, #c82333 100%);
|
||||
padding: 40px 30px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.logo {
|
||||
font-size: 32px;
|
||||
font-weight: 700;
|
||||
color: #ffffff;
|
||||
text-decoration: none;
|
||||
letter-spacing: -1px;
|
||||
}
|
||||
|
||||
.tagline {
|
||||
color: #f8d7da;
|
||||
font-size: 16px;
|
||||
margin-top: 8px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
/* Content */
|
||||
.content {
|
||||
padding: 40px 30px;
|
||||
}
|
||||
|
||||
.content h1 {
|
||||
font-size: 28px;
|
||||
font-weight: 600;
|
||||
margin: 0 0 20px 0;
|
||||
color: #212529;
|
||||
}
|
||||
|
||||
.content h2 {
|
||||
font-size: 22px;
|
||||
font-weight: 600;
|
||||
margin: 30px 0 15px 0;
|
||||
color: #495057;
|
||||
}
|
||||
|
||||
.content p {
|
||||
margin: 0 0 16px 0;
|
||||
color: #6c757d;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.content strong {
|
||||
color: #495057;
|
||||
}
|
||||
|
||||
/* Warning box */
|
||||
.warning-box {
|
||||
background-color: #fff3cd;
|
||||
border-left: 4px solid #ffc107;
|
||||
padding: 20px;
|
||||
margin: 20px 0;
|
||||
border-radius: 0 6px 6px 0;
|
||||
}
|
||||
|
||||
.warning-box p {
|
||||
margin: 0 0 10px 0;
|
||||
color: #856404;
|
||||
}
|
||||
|
||||
.warning-box p:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
/* Alert box */
|
||||
.alert-box {
|
||||
background-color: #f8d7da;
|
||||
border-left: 4px solid #dc3545;
|
||||
padding: 20px;
|
||||
margin: 20px 0;
|
||||
border-radius: 0 6px 6px 0;
|
||||
}
|
||||
|
||||
.alert-box p {
|
||||
margin: 0 0 10px 0;
|
||||
color: #721c24;
|
||||
}
|
||||
|
||||
.alert-box p:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
/* Post highlight */
|
||||
.post-highlight {
|
||||
background-color: #f8f9fa;
|
||||
border-radius: 6px;
|
||||
padding: 20px;
|
||||
margin: 20px 0;
|
||||
}
|
||||
|
||||
.post-highlight .post-title {
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: #495057;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.post-highlight .post-link {
|
||||
font-size: 14px;
|
||||
color: #667eea;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.post-highlight .post-link:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
/* Button */
|
||||
.button {
|
||||
display: inline-block;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
color: #ffffff !important;
|
||||
text-decoration: none;
|
||||
padding: 16px 32px;
|
||||
border-radius: 6px;
|
||||
font-weight: 600;
|
||||
margin: 20px 0;
|
||||
text-align: center;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.button:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 12px rgba(102, 126, 234, 0.4);
|
||||
}
|
||||
|
||||
/* Info box */
|
||||
.info-box {
|
||||
background-color: #e7f3ff;
|
||||
border-left: 4px solid #667eea;
|
||||
padding: 20px;
|
||||
margin: 20px 0;
|
||||
border-radius: 0 6px 6px 0;
|
||||
}
|
||||
|
||||
.info-box p {
|
||||
margin: 0 0 10px 0;
|
||||
color: #004085;
|
||||
}
|
||||
|
||||
.info-box p:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
/* Footer */
|
||||
.footer {
|
||||
background-color: #f8f9fa;
|
||||
padding: 30px;
|
||||
text-align: center;
|
||||
border-top: 1px solid #e9ecef;
|
||||
}
|
||||
|
||||
.footer p {
|
||||
margin: 0 0 10px 0;
|
||||
font-size: 14px;
|
||||
color: #6c757d;
|
||||
}
|
||||
|
||||
.footer a {
|
||||
color: #667eea;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.footer a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
/* Responsive */
|
||||
@media only screen and (max-width: 600px) {
|
||||
.email-container {
|
||||
margin: 0;
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.header, .content, .footer {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.logo {
|
||||
font-size: 28px;
|
||||
}
|
||||
|
||||
.content h1 {
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
.content h2 {
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.button {
|
||||
display: block;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.post-highlight .post-title {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="email-container">
|
||||
<div class="header">
|
||||
<div class="logo">RentAll</div>
|
||||
<div class="tagline">⚠️ Important: Comment Removal Notice</div>
|
||||
</div>
|
||||
|
||||
<div class="content">
|
||||
<p>Hi {{commentAuthorName}},</p>
|
||||
|
||||
<h1>Your Comment Has Been Removed</h1>
|
||||
|
||||
<p>We're writing to inform you that your comment has been removed from a forum discussion by {{adminName}}.</p>
|
||||
|
||||
<div class="post-highlight">
|
||||
<p style="margin: 0 0 10px 0; color: #6c757d; font-size: 14px;">Comment on:</p>
|
||||
<div class="post-title">{{postTitle}}</div>
|
||||
<a href="{{postUrl}}" class="post-link">View Discussion →</a>
|
||||
</div>
|
||||
|
||||
<div class="alert-box">
|
||||
<p><strong>Reason for Removal:</strong></p>
|
||||
<p>{{deletionReason}}</p>
|
||||
</div>
|
||||
|
||||
<div class="info-box">
|
||||
<p><strong>What this means:</strong></p>
|
||||
<ul style="margin: 10px 0; padding-left: 20px; color: #004085;">
|
||||
<li>Your comment is no longer visible to other community members</li>
|
||||
<li>The comment content has been preserved in case of appeal</li>
|
||||
<li>The discussion thread remains active for other participants</li>
|
||||
<li>You can still participate in other forum discussions</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<h2>Need Help or Have Questions?</h2>
|
||||
<p>If you believe this removal was made in error or if you have questions about our community guidelines, please don't hesitate to contact our support team:</p>
|
||||
|
||||
<p style="text-align: center;">
|
||||
<a href="mailto:{{supportEmail}}" class="button">Contact Support</a>
|
||||
</p>
|
||||
|
||||
<div class="warning-box">
|
||||
<p><strong>Review Our Community Guidelines:</strong></p>
|
||||
<p>To ensure a positive experience for all members, please review our community guidelines. We appreciate respectful, constructive contributions that help build a supportive community.</p>
|
||||
</div>
|
||||
|
||||
<p>Thank you for your understanding, and we look forward to your continued participation in our community.</p>
|
||||
|
||||
<p><strong>Best regards,</strong><br>
|
||||
The RentAll Team</p>
|
||||
</div>
|
||||
|
||||
<div class="footer">
|
||||
<p><strong>RentAll</strong></p>
|
||||
<p>Building a community of sharing and trust</p>
|
||||
<p>This email was sent because your comment was removed by our moderation team.</p>
|
||||
<p>If you have questions, please contact <a href="mailto:{{supportEmail}}">{{supportEmail}}</a></p>
|
||||
<p>© 2024 RentAll. All rights reserved.</p>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
305
backend/templates/emails/forumPostDeletionToAuthor.html
Normal file
305
backend/templates/emails/forumPostDeletionToAuthor.html
Normal file
@@ -0,0 +1,305 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<title>Your Forum Post Has Been Removed</title>
|
||||
<style>
|
||||
/* Reset styles */
|
||||
body, table, td, p, a, li, blockquote {
|
||||
-webkit-text-size-adjust: 100%;
|
||||
-ms-text-size-adjust: 100%;
|
||||
}
|
||||
table, td {
|
||||
mso-table-lspace: 0pt;
|
||||
mso-table-rspace: 0pt;
|
||||
}
|
||||
img {
|
||||
-ms-interpolation-mode: bicubic;
|
||||
}
|
||||
|
||||
/* Base styles */
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
width: 100% !important;
|
||||
min-width: 100%;
|
||||
height: 100%;
|
||||
background-color: #f8f9fa;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
|
||||
line-height: 1.6;
|
||||
color: #212529;
|
||||
}
|
||||
|
||||
/* Container */
|
||||
.email-container {
|
||||
max-width: 600px;
|
||||
margin: 0 auto;
|
||||
background-color: #ffffff;
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
/* Header - Warning red gradient */
|
||||
.header {
|
||||
background: linear-gradient(135deg, #dc3545 0%, #c82333 100%);
|
||||
padding: 40px 30px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.logo {
|
||||
font-size: 32px;
|
||||
font-weight: 700;
|
||||
color: #ffffff;
|
||||
text-decoration: none;
|
||||
letter-spacing: -1px;
|
||||
}
|
||||
|
||||
.tagline {
|
||||
color: #f8d7da;
|
||||
font-size: 16px;
|
||||
margin-top: 8px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
/* Content */
|
||||
.content {
|
||||
padding: 40px 30px;
|
||||
}
|
||||
|
||||
.content h1 {
|
||||
font-size: 28px;
|
||||
font-weight: 600;
|
||||
margin: 0 0 20px 0;
|
||||
color: #212529;
|
||||
}
|
||||
|
||||
.content h2 {
|
||||
font-size: 22px;
|
||||
font-weight: 600;
|
||||
margin: 30px 0 15px 0;
|
||||
color: #495057;
|
||||
}
|
||||
|
||||
.content p {
|
||||
margin: 0 0 16px 0;
|
||||
color: #6c757d;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.content strong {
|
||||
color: #495057;
|
||||
}
|
||||
|
||||
/* Warning box */
|
||||
.warning-box {
|
||||
background-color: #fff3cd;
|
||||
border-left: 4px solid #ffc107;
|
||||
padding: 20px;
|
||||
margin: 20px 0;
|
||||
border-radius: 0 6px 6px 0;
|
||||
}
|
||||
|
||||
.warning-box p {
|
||||
margin: 0 0 10px 0;
|
||||
color: #856404;
|
||||
}
|
||||
|
||||
.warning-box p:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
/* Alert box */
|
||||
.alert-box {
|
||||
background-color: #f8d7da;
|
||||
border-left: 4px solid #dc3545;
|
||||
padding: 20px;
|
||||
margin: 20px 0;
|
||||
border-radius: 0 6px 6px 0;
|
||||
}
|
||||
|
||||
.alert-box p {
|
||||
margin: 0 0 10px 0;
|
||||
color: #721c24;
|
||||
}
|
||||
|
||||
.alert-box p:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
/* Post highlight */
|
||||
.post-highlight {
|
||||
background-color: #f8f9fa;
|
||||
border-radius: 6px;
|
||||
padding: 20px;
|
||||
margin: 20px 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.post-highlight .post-title {
|
||||
font-size: 20px;
|
||||
font-weight: 600;
|
||||
color: #dc3545;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
/* Button */
|
||||
.button {
|
||||
display: inline-block;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
color: #ffffff !important;
|
||||
text-decoration: none;
|
||||
padding: 16px 32px;
|
||||
border-radius: 6px;
|
||||
font-weight: 600;
|
||||
margin: 20px 0;
|
||||
text-align: center;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.button:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 12px rgba(102, 126, 234, 0.4);
|
||||
}
|
||||
|
||||
/* Info box */
|
||||
.info-box {
|
||||
background-color: #e7f3ff;
|
||||
border-left: 4px solid #667eea;
|
||||
padding: 20px;
|
||||
margin: 20px 0;
|
||||
border-radius: 0 6px 6px 0;
|
||||
}
|
||||
|
||||
.info-box p {
|
||||
margin: 0 0 10px 0;
|
||||
color: #004085;
|
||||
}
|
||||
|
||||
.info-box p:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
/* Footer */
|
||||
.footer {
|
||||
background-color: #f8f9fa;
|
||||
padding: 30px;
|
||||
text-align: center;
|
||||
border-top: 1px solid #e9ecef;
|
||||
}
|
||||
|
||||
.footer p {
|
||||
margin: 0 0 10px 0;
|
||||
font-size: 14px;
|
||||
color: #6c757d;
|
||||
}
|
||||
|
||||
.footer a {
|
||||
color: #667eea;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.footer a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
/* Responsive */
|
||||
@media only screen and (max-width: 600px) {
|
||||
.email-container {
|
||||
margin: 0;
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.header, .content, .footer {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.logo {
|
||||
font-size: 28px;
|
||||
}
|
||||
|
||||
.content h1 {
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
.content h2 {
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.button {
|
||||
display: block;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.post-highlight .post-title {
|
||||
font-size: 18px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="email-container">
|
||||
<div class="header">
|
||||
<div class="logo">RentAll</div>
|
||||
<div class="tagline">⚠️ Important: Forum Post Removal Notice</div>
|
||||
</div>
|
||||
|
||||
<div class="content">
|
||||
<p>Hi {{postAuthorName}},</p>
|
||||
|
||||
<h1>Your Forum Post Has Been Removed</h1>
|
||||
|
||||
<p>We're writing to inform you that your forum post has been removed from RentAll by {{adminName}}.</p>
|
||||
|
||||
<div class="post-highlight">
|
||||
<div class="post-title">{{postTitle}}</div>
|
||||
</div>
|
||||
|
||||
<div class="alert-box">
|
||||
<p><strong>Reason for Removal:</strong></p>
|
||||
<p>{{deletionReason}}</p>
|
||||
</div>
|
||||
|
||||
<div class="info-box">
|
||||
<p><strong>What this means:</strong></p>
|
||||
<ul style="margin: 10px 0; padding-left: 20px; color: #004085;">
|
||||
<li>Your post is no longer visible to other community members</li>
|
||||
<li>All comments on this post are also hidden</li>
|
||||
<li>The post cannot receive new comments or activity</li>
|
||||
<li>You may still see it in your dashboard if viewing as an admin</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<h2>Need Help or Have Questions?</h2>
|
||||
<p>If you believe this removal was made in error or if you have questions about our community guidelines, please don't hesitate to contact our support team:</p>
|
||||
|
||||
<p style="text-align: center;">
|
||||
<a href="mailto:{{supportEmail}}" class="button">Contact Support</a>
|
||||
</p>
|
||||
|
||||
<div class="warning-box">
|
||||
<p><strong>Review Our Community Guidelines:</strong></p>
|
||||
<p>To prevent future removals, please familiarize yourself with our community guidelines and forum standards. Our team is happy to help you understand how to contribute positively to the RentAll community.</p>
|
||||
</div>
|
||||
|
||||
<p>You can continue participating in the forum by visiting our <a href="{{forumUrl}}" style="color: #667eea;">community forum</a>.</p>
|
||||
|
||||
<p>Thank you for your understanding.</p>
|
||||
|
||||
<p><strong>Best regards,</strong><br>
|
||||
The RentAll Team</p>
|
||||
</div>
|
||||
|
||||
<div class="footer">
|
||||
<p><strong>RentAll</strong></p>
|
||||
<p>Building a community of sharing and trust</p>
|
||||
<p>This email was sent because your forum post was removed by our moderation team.</p>
|
||||
<p>If you have questions, please contact <a href="mailto:{{supportEmail}}">{{supportEmail}}</a></p>
|
||||
<p>© 2024 RentAll. All rights reserved.</p>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user