Initial commit - adding comments functionality to HTMLy.

This commit is contained in:
Emidio Reggiani 2025-11-08 09:38:22 +01:00
commit eb7eda6da6
11 changed files with 1613 additions and 3 deletions

View file

@ -21,7 +21,9 @@
"system/includes/functions.php",
"system/admin/admin.php",
"system/includes/session.php",
"system/includes/opml.php"
"system/includes/opml.php",
"system/includes/comments.php",
"system/includes/comments-frontend.php"
]
}
}

View file

@ -336,3 +336,66 @@ custom_fields = "Custom fields"
views_counter = "Views counter"
themes = "Themes"
version = "Version"
comments_management = "Comments Management"
all_comments = "All Comments"
pending_moderation = "Pending Moderation"
comment = "Comment"
post_page = "Post/Page"
date = "Date"
status = "Status"
actions = "Actions"
published = "Published"
pending = "Pending"
reply_to_comment = "Reply to comment"
notifications_enabled = "Notifications enabled"
modified = "Modified"
publish = "Publish"
edit = "Edit"
delete = "Delete"
confirm_publish_comment = "Are you sure you want to publish this comment?"
confirm_delete_comment = "Are you sure you want to delete this comment? This will also delete all replies to this comment."
no_comments_found = "No comments found"
edit_comment = "Edit Comment"
name = "Name"
email = "Email"
update_comment = "Update Comment"
cancel = "Cancel"
comments_settings = "Comments Settings"
general_settings = "General Settings"
note = "Note"
enable_comments_in_main_config = "To enable local comments, set comment.system = \"local\" in config/config.ini"
comment_moderation = "Comment Moderation"
require_admin_approval = "Comments must be approved by an administrator before being published"
comments_moderation_desc = "When enabled, new comments will be held for moderation and won't be visible until approved"
anti_spam_protection = "Anti-Spam Protection"
enable_honeypot = "Enable honeypot anti-spam protection"
honeypot_desc = "Honeypot is an invisible field that only bots fill out, helping prevent spam without user interaction"
email_notifications = "Email Notifications"
enable_notifications = "Enable Notifications"
send_email_notifications = "Send email notifications for new comments"
admin_email = "Administrator Email"
admin_email_desc = "Email address to receive notifications about new comments"
smtp_settings = "SMTP Settings"
enable_smtp = "Enable SMTP"
enable_smtp_for_emails = "Enable SMTP for sending email notifications"
smtp_host = "SMTP Host"
smtp_port = "SMTP Port"
encryption = "Encryption"
smtp_username = "SMTP Username"
smtp_password = "SMTP Password"
enter_password = "Enter password"
from_email = "From Email"
from_name = "From Name"
save_settings = "Save Settings"
leave_a_comment = "Leave a Comment"
email_not_published = "Your email will not be published"
comment_formatting_help = "You can use **bold text** for formatting. Line breaks are preserved."
notify_new_comments = "Notify me of new comments in this thread"
post_reply = "Post Reply"
post_comment = "Post Comment"
reply = "Reply"
comment_submitted_success = "Your comment has been posted successfully!"
comment_submitted_moderation = "Your comment has been submitted and is awaiting moderation."
comment_submission_error = "There was an error submitting your comment. Please try again."
pending_comments = "Pending Comments"
level = "Level"

View file

@ -0,0 +1,297 @@
<?php if (!defined('HTMLY')) die('HTMLy'); ?>
<h2><?php echo i18n('Comments_Management');?></h2>
<br>
<?php if (isset($message)): ?>
<div class="alert alert-<?php echo $message['type']; ?> alert-dismissible fade show">
<?php echo $message['text']; ?>
<button type="button" class="close" data-dismiss="alert">&times;</button>
</div>
<?php endif; ?>
<nav>
<div class="nav nav-tabs" id="nav-tab">
<a class="nav-item nav-link <?php echo (!isset($tab) || $tab === 'all') ? 'active' : ''; ?>"
href="<?php echo site_url();?>admin/comments"><?php echo i18n('All_Comments');?></a>
<a class="nav-item nav-link <?php echo (isset($tab) && $tab === 'pending') ? 'active' : ''; ?>"
href="<?php echo site_url();?>admin/comments/pending"><?php echo i18n('Pending_Moderation');?>
<?php if (isset($pendingCount) && $pendingCount > 0): ?>
<span class="badge badge-warning"><?php echo $pendingCount; ?></span>
<?php endif; ?>
</a>
<a class="nav-item nav-link <?php echo (isset($tab) && $tab === 'settings') ? 'active' : ''; ?>"
href="<?php echo site_url();?>admin/comments/settings"><?php echo i18n('Settings');?></a>
</div>
</nav>
<br><br>
<?php if (!isset($tab) || $tab === 'all' || $tab === 'pending'): ?>
<!-- Comments List -->
<?php if (!empty($comments)): ?>
<table class="table table-striped">
<thead>
<tr>
<th><?php echo i18n('Author');?></th>
<th><?php echo i18n('Comment');?></th>
<th><?php echo i18n('Post_Page');?></th>
<th><?php echo i18n('Date');?></th>
<th><?php echo i18n('Status');?></th>
<th><?php echo i18n('Actions');?></th>
</tr>
</thead>
<tbody>
<?php foreach ($comments as $comment): ?>
<tr id="comment-<?php echo $comment['id']; ?>">
<td>
<strong><?php echo _h($comment['name']); ?></strong><br>
<small><?php echo _h($comment['email']); ?></small><br>
<small class="text-muted">IP: <?php echo _h($comment['ip']); ?></small>
</td>
<td>
<?php
$preview = mb_substr($comment['comment'], 0, 100);
echo _h($preview);
if (mb_strlen($comment['comment']) > 100) echo '...';
?>
<?php if (!empty($comment['parent_id'])): ?>
<br><small class="text-info"><em><?php echo i18n('Reply_to_comment');?></em></small>
<?php endif; ?>
<?php if ($comment['notify']): ?>
<br><small class="text-success"><em><?php echo i18n('Notifications_enabled');?></em></small>
<?php endif; ?>
</td>
<td>
<a href="<?php echo site_url() . $comment['post_id']; ?>" target="_blank">
<?php echo _h($comment['post_id']); ?>
</a>
</td>
<td>
<?php echo format_date($comment['timestamp']); ?>
<?php if (isset($comment['modified'])): ?>
<br><small class="text-muted"><?php echo i18n('Modified');?>: <?php echo $comment['modified']; ?></small>
<?php endif; ?>
</td>
<td>
<?php if ($comment['published']): ?>
<span class="badge badge-success"><?php echo i18n('Published');?></span>
<?php else: ?>
<span class="badge badge-warning"><?php echo i18n('Pending');?></span>
<?php endif; ?>
</td>
<td>
<?php if (!$comment['published']): ?>
<a class="btn btn-success btn-xs"
href="<?php echo site_url(); ?>admin/comments/publish/<?php echo $comment['post_id']; ?>/<?php echo $comment['id']; ?>"
onclick="return confirm('<?php echo i18n('Confirm_publish_comment'); ?>');">
<?php echo i18n('Publish');?>
</a>
<?php endif; ?>
<a class="btn btn-primary btn-xs"
href="<?php echo site_url(); ?>admin/comments/edit/<?php echo $comment['post_id']; ?>/<?php echo $comment['id']; ?>">
<?php echo i18n('Edit');?>
</a>
<a class="btn btn-danger btn-xs"
href="<?php echo site_url(); ?>admin/comments/delete/<?php echo $comment['post_id']; ?>/<?php echo $comment['id']; ?>"
onclick="return confirm('<?php echo i18n('Confirm_delete_comment'); ?>');">
<?php echo i18n('Delete');?>
</a>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<?php else: ?>
<p><?php echo i18n('No_comments_found'); ?>.</p>
<?php endif; ?>
<?php elseif ($tab === 'settings'): ?>
<!-- Settings Form -->
<form method="POST" action="<?php echo site_url(); ?>admin/comments/settings">
<input type="hidden" name="csrf_token" value="<?php echo get_csrf(); ?>">
<!-- // removed by Emidio 20251105
<div class="alert alert-info">
<strong><?php echo i18n('Note'); ?>:</strong> <?php echo i18n('Enable_comments_in_main_config'); ?>
<br>
<code>config/config.ini</code> <code>comment.system = "local"</code>
</div>
-->
<h4><?php echo i18n('General_Settings');?></h4>
<hr>
<div class="form-group row">
<label class="col-sm-3 col-form-label"><?php echo i18n('Comment_Moderation');?></label>
<div class="col-sm-9">
<div class="form-check">
<input type="checkbox" class="form-check-input" name="comments.moderation" value="true"
<?php echo comments_config('comments.moderation') === 'true' ? 'checked' : ''; ?>>
<label class="form-check-label"><?php echo i18n('Require_admin_approval');?></label>
</div>
<small class="form-text text-muted"><?php echo i18n('Comments_moderation_desc');?></small>
</div>
</div>
<div class="form-group row">
<label class="col-sm-3 col-form-label"><?php echo i18n('Anti_Spam_Protection');?></label>
<div class="col-sm-9">
<div class="form-check">
<input type="checkbox" class="form-check-input" name="comments.honeypot" value="true"
<?php echo comments_config('comments.honeypot') === 'true' ? 'checked' : ''; ?>>
<label class="form-check-label"><?php echo i18n('Enable_honeypot');?></label>
</div>
<small class="form-text text-muted"><?php echo i18n('Honeypot_desc');?></small>
</div>
</div>
<h4><?php echo i18n('Email_Notifications');?></h4>
<hr>
<div class="form-group row">
<label class="col-sm-3 col-form-label"><?php echo i18n('Enable_Notifications');?></label>
<div class="col-sm-9">
<div class="form-check">
<input type="checkbox" class="form-check-input" name="comments.notify" value="true"
<?php echo comments_config('comments.notify') === 'true' ? 'checked' : ''; ?>>
<label class="form-check-label"><?php echo i18n('Send_email_notifications');?></label>
</div>
</div>
</div>
<div class="form-group row">
<label for="admin-email" class="col-sm-3 col-form-label"><?php echo i18n('Admin_Email');?></label>
<div class="col-sm-9">
<input type="email" class="form-control" id="admin-email" name="comments.admin.email"
value="<?php echo _h(comments_config('comments.admin.email')); ?>"
placeholder="admin@example.com">
<small class="form-text text-muted"><?php echo i18n('Admin_email_desc');?></small>
</div>
</div>
<h4><?php echo i18n('SMTP_Settings');?></h4>
<hr>
<div class="form-group row">
<label class="col-sm-3 col-form-label"><?php echo i18n('Enable_SMTP');?></label>
<div class="col-sm-9">
<div class="form-check">
<input type="checkbox" class="form-check-input" name="comments.mail.enabled" value="true"
<?php echo comments_config('comments.mail.enabled') === 'true' ? 'checked' : ''; ?>>
<label class="form-check-label"><?php echo i18n('Enable_SMTP_for_emails');?></label>
</div>
</div>
</div>
<div class="form-group row">
<label for="mail-host" class="col-sm-3 col-form-label"><?php echo i18n('SMTP_Host');?></label>
<div class="col-sm-9">
<input type="text" class="form-control" id="mail-host" name="comments.mail.host"
value="<?php echo _h(comments_config('comments.mail.host')); ?>"
placeholder="smtp.gmail.com">
</div>
</div>
<div class="form-group row">
<label for="mail-port" class="col-sm-3 col-form-label"><?php echo i18n('SMTP_Port');?></label>
<div class="col-sm-9">
<input type="number" class="form-control" id="mail-port" name="comments.mail.port"
value="<?php echo _h(comments_config('comments.mail.port')); ?>"
placeholder="587">
<small class="form-text text-muted">587 (TLS) or 465 (SSL)</small>
</div>
</div>
<div class="form-group row">
<label for="mail-encryption" class="col-sm-3 col-form-label"><?php echo i18n('Encryption');?></label>
<div class="col-sm-9">
<select class="form-control" id="mail-encryption" name="comments.mail.encryption">
<option value="tls" <?php echo comments_config('comments.mail.encryption') === 'tls' ? 'selected' : ''; ?>>TLS</option>
<option value="ssl" <?php echo comments_config('comments.mail.encryption') === 'ssl' ? 'selected' : ''; ?>>SSL</option>
</select>
</div>
</div>
<div class="form-group row">
<label for="mail-username" class="col-sm-3 col-form-label"><?php echo i18n('SMTP_Username');?></label>
<div class="col-sm-9">
<input type="text" class="form-control" id="mail-username" name="comments.mail.username"
value="<?php echo _h(comments_config('comments.mail.username')); ?>"
placeholder="your-email@gmail.com">
</div>
</div>
<div class="form-group row">
<label for="mail-password" class="col-sm-3 col-form-label"><?php echo i18n('SMTP_Password');?></label>
<div class="col-sm-9">
<input type="password" class="form-control" id="mail-password" name="comments.mail.password"
value="<?php echo _h(comments_config('comments.mail.password')); ?>"
placeholder="<?php echo i18n('Enter_password');?>">
</div>
</div>
<div class="form-group row">
<label for="mail-from-email" class="col-sm-3 col-form-label"><?php echo i18n('From_Email');?></label>
<div class="col-sm-9">
<input type="email" class="form-control" id="mail-from-email" name="comments.mail.from.email"
value="<?php echo _h(comments_config('comments.mail.from.email')); ?>"
placeholder="noreply@example.com">
</div>
</div>
<div class="form-group row">
<label for="mail-from-name" class="col-sm-3 col-form-label"><?php echo i18n('From_Name');?></label>
<div class="col-sm-9">
<input type="text" class="form-control" id="mail-from-name" name="comments.mail.from.name"
value="<?php echo _h(comments_config('comments.mail.from.name')); ?>"
placeholder="<?php echo config('blog.title'); ?>">
</div>
</div>
<div class="form-group row">
<div class="col-sm-9 offset-sm-3">
<button type="submit" class="btn btn-primary"><?php echo i18n('Save_Settings');?></button>
</div>
</div>
</form>
<?php endif; ?>
<?php if (isset($editComment)): ?>
<!-- Edit Comment Modal/Page -->
<h3><?php echo i18n('Edit_Comment');?></h3>
<hr>
<form method="POST" action="<?php echo site_url(); ?>admin/comments/update/<?php echo $editComment['post_id']; ?>/<?php echo $editComment['id']; ?>">
<input type="hidden" name="csrf_token" value="<?php echo get_csrf(); ?>">
<div class="form-group">
<label for="edit-name"><?php echo i18n('Name');?></label>
<input type="text" class="form-control" id="edit-name" name="name"
value="<?php echo _h($editComment['name']); ?>" required>
</div>
<div class="form-group">
<label for="edit-email"><?php echo i18n('Email');?></label>
<input type="email" class="form-control" id="edit-email" name="email"
value="<?php echo _h($editComment['email']); ?>" required>
</div>
<div class="form-group">
<label for="edit-comment"><?php echo i18n('Comment');?></label>
<textarea class="form-control" id="edit-comment" name="comment" rows="6" required><?php echo _h($editComment['comment']); ?></textarea>
</div>
<div class="form-group">
<div class="form-check">
<input type="checkbox" class="form-check-input" id="edit-published" name="published" value="1"
<?php echo $editComment['published'] ? 'checked' : ''; ?>>
<label class="form-check-label" for="edit-published"><?php echo i18n('Published');?></label>
</div>
</div>
<div class="form-group">
<button type="submit" class="btn btn-primary"><?php echo i18n('Update_Comment');?></button>
<a href="<?php echo site_url(); ?>admin/comments" class="btn btn-secondary"><?php echo i18n('Cancel');?></a>
</div>
</form>
<?php endif; ?>

View file

@ -158,6 +158,12 @@
Facebook
</label>
</div>
<div class="form-check">
<input class="form-check-input" type="radio" name="-config-comment.system" id="comment.system3" value="local" <?php if (config('comment.system') === 'local'):?>checked<?php endif;?>>
<label class="form-check-label" for="comment.system3">
Local
</label>
</div>
</div>
</div>
</div>

View file

@ -150,6 +150,50 @@ if (isset($author[0])) {
</ul>
</li>
<?php if ($role === 'editor' || $role === 'admin'):?>
<?php if (local()): ?>
<li class="nav-item has-treeview menu-open">
<a href="#" class="nav-link">
<i class="nav-icon fa fa-comments"></i>
<p>
<?php echo i18n('Comments'); ?>
<?php
$pendingCount = getPendingCommentsCount();
if ($pendingCount > 0): ?>
<span class="badge badge-warning right"><?php echo $pendingCount; ?></span>
<?php endif; ?>
<i class="right fa fa-angle-left"></i>
</p>
</a>
<ul class="nav nav-treeview">
<li class="nav-item">
<a href="<?php echo site_url();?>admin/comments" class="nav-link">
<p>
<?php echo i18n('All_Comments'); ?>
</p>
</a>
</li>
<li class="nav-item">
<a href="<?php echo site_url();?>admin/comments/pending" class="nav-link">
<p>
<?php echo i18n('Pending_Moderation'); ?>
<?php if ($pendingCount > 0): ?>
<span class="badge badge-warning right"><?php echo $pendingCount; ?></span>
<?php endif; ?>
</p>
</a>
</li>
<?php if ($role === 'admin'):?>
<li class="nav-item">
<a href="<?php echo site_url();?>admin/comments/settings" class="nav-link">
<p>
<?php echo i18n('Settings'); ?>
</p>
</a>
</li>
<?php endif;?>
</ul>
</li>
<?php endif;?>
<li class="nav-item has-treeview menu-open">
<a href="#" class="nav-link">
<i class="nav-icon fa fa-cogs"></i>

View file

@ -3098,6 +3098,254 @@ get('/admin/categories/:category', function ($category) {
}
});
// Show admin/comments - All comments
get('/admin/comments', function () {
$user = $_SESSION[site_url()]['user'];
$role = user('role', $user);
if (login() && ($role === 'admin' || $role === 'editor')) {
config('views.root', 'system/admin/views');
$comments = getAllComments();
$pendingCount = getPendingCommentsCount();
render('comments', array(
'title' => generate_title('is_default', i18n('Comments_Management')),
'description' => safe_html(strip_tags(blog_description())),
'canonical' => site_url(),
'metatags' => generate_meta(null, null),
'type' => 'is_admin-comments',
'is_admin' => true,
'bodyclass' => 'admin-comments',
'breadcrumb' => '<a href="' . site_url() . '">' . config('breadcrumb.home') . '</a> &#187; ' . i18n('Comments'),
'tab' => 'all',
'comments' => $comments,
'pendingCount' => $pendingCount
));
} else {
$login = site_url() . 'login';
header("location: $login");
}
});
// Show admin/comments/pending - Pending comments
get('/admin/comments/pending', function () {
$user = $_SESSION[site_url()]['user'];
$role = user('role', $user);
if (login() && ($role === 'admin' || $role === 'editor')) {
config('views.root', 'system/admin/views');
$allComments = getAllComments();
$comments = array_filter($allComments, function($comment) {
return !$comment['published'];
});
$pendingCount = count($comments);
render('comments', array(
'title' => generate_title('is_default', i18n('Pending_Comments')),
'description' => safe_html(strip_tags(blog_description())),
'canonical' => site_url(),
'metatags' => generate_meta(null, null),
'type' => 'is_admin-comments',
'is_admin' => true,
'bodyclass' => 'admin-comments',
'breadcrumb' => '<a href="' . site_url() . '">' . config('breadcrumb.home') . '</a> &#187; ' . i18n('Comments') . ' &#187; ' . i18n('Pending'),
'tab' => 'pending',
'comments' => $comments,
'pendingCount' => $pendingCount
));
} else {
$login = site_url() . 'login';
header("location: $login");
}
});
// Show admin/comments/settings - Settings page
get('/admin/comments/settings', function () {
$user = $_SESSION[site_url()]['user'];
$role = user('role', $user);
if (login() && $role === 'admin') {
config('views.root', 'system/admin/views');
$pendingCount = getPendingCommentsCount();
render('comments', array(
'title' => generate_title('is_default', i18n('Comments_Settings')),
'description' => safe_html(strip_tags(blog_description())),
'canonical' => site_url(),
'metatags' => generate_meta(null, null),
'type' => 'is_admin-comments',
'is_admin' => true,
'bodyclass' => 'admin-comments',
'breadcrumb' => '<a href="' . site_url() . '">' . config('breadcrumb.home') . '</a> &#187; ' . i18n('Comments') . ' &#187; ' . i18n('Settings'),
'tab' => 'settings',
'pendingCount' => $pendingCount
));
} else {
$login = site_url() . 'login';
header("location: $login");
}
});
// Save comments settings
post('/admin/comments/settings', function () {
$proper = is_csrf_proper(from($_REQUEST, 'csrf_token'));
if (login() && $proper) {
$user = $_SESSION[site_url()]['user'];
$role = user('role', $user);
if ($role === 'admin') {
$config = array();
// Get checkbox values (they are not sent if unchecked)
// Note: HTML forms convert dots to underscores in POST data
$config['comments.moderation'] = isset($_POST['comments_moderation']) ? 'true' : 'false';
$config['comments.honeypot'] = isset($_POST['comments_honeypot']) ? 'true' : 'false';
$config['comments.notify'] = isset($_POST['comments_notify']) ? 'true' : 'false';
$config['comments.mail.enabled'] = isset($_POST['comments_mail_enabled']) ? 'true' : 'false';
// Get text fields (using underscores because HTML forms convert dots)
$config['comments.admin.email'] = from($_POST, 'comments_admin_email');
$config['comments.mail.host'] = from($_POST, 'comments_mail_host');
$config['comments.mail.port'] = from($_POST, 'comments_mail_port');
$config['comments.mail.username'] = from($_POST, 'comments_mail_username');
$config['comments.mail.encryption'] = from($_POST, 'comments_mail_encryption');
$config['comments.mail.from.email'] = from($_POST, 'comments_mail_from_email');
$config['comments.mail.from.name'] = from($_POST, 'comments_mail_from_name');
// Only update password if provided
$password = from($_POST, 'comments_mail_password');
if (!empty($password)) {
$config['comments.mail.password'] = $password;
}
// Debug: log to file (remove after debugging)
file_put_contents('content/comments-debug.log',
date('Y-m-d H:i:s') . "\n" .
"POST data: " . print_r($_POST, true) . "\n" .
"Config array: " . print_r($config, true) . "\n\n",
FILE_APPEND
);
$result = save_comments_config($config);
// Log result
file_put_contents('content/comments-debug.log',
"Save result: " . ($result ? "SUCCESS ($result bytes)" : "FAILED") . "\n" .
"File content after save:\n" . file_get_contents('config/comments.ini') . "\n\n",
FILE_APPEND
);
$redir = site_url() . 'admin/comments/settings';
header("location: $redir");
} else {
$redir = site_url();
header("location: $redir");
}
} else {
$login = site_url() . 'login';
header("location: $login");
}
});
// Show edit comment form
get('/admin/comments/edit/:postid/:commentid', function ($postid, $commentid) {
$user = $_SESSION[site_url()]['user'];
$role = user('role', $user);
if (login() && ($role === 'admin' || $role === 'editor')) {
config('views.root', 'system/admin/views');
$comments = getComments($postid, true);
$editComment = null;
foreach ($comments as $comment) {
if ($comment['id'] === $commentid) {
$comment['post_id'] = $postid;
$editComment = $comment;
break;
}
}
if ($editComment) {
$pendingCount = getPendingCommentsCount();
render('comments', array(
'title' => generate_title('is_default', i18n('Edit_Comment')),
'description' => safe_html(strip_tags(blog_description())),
'canonical' => site_url(),
'metatags' => generate_meta(null, null),
'type' => 'is_admin-comments',
'is_admin' => true,
'bodyclass' => 'admin-comments',
'breadcrumb' => '<a href="' . site_url() . '">' . config('breadcrumb.home') . '</a> &#187; <a href="' . site_url() . 'admin/comments">' . i18n('Comments') . '</a> &#187; ' . i18n('Edit'),
'editComment' => $editComment,
'pendingCount' => $pendingCount
));
} else {
$redir = site_url() . 'admin/comments';
header("location: $redir");
}
} else {
$login = site_url() . 'login';
header("location: $login");
}
});
// Update comment
post('/admin/comments/update/:postid/:commentid', function ($postid, $commentid) {
$proper = is_csrf_proper(from($_REQUEST, 'csrf_token'));
if (login() && $proper) {
$user = $_SESSION[site_url()]['user'];
$role = user('role', $user);
if ($role === 'admin' || $role === 'editor') {
$data = array(
'name' => from($_POST, 'name'),
'email' => from($_POST, 'email'),
'comment' => from($_POST, 'comment'),
'published' => isset($_POST['published'])
);
if (commentModify($postid, $commentid, $data)) {
$redir = site_url() . 'admin/comments';
header("location: $redir");
} else {
$redir = site_url() . 'admin/comments/edit/' . $postid . '/' . $commentid;
header("location: $redir");
}
} else {
$redir = site_url();
header("location: $redir");
}
} else {
$login = site_url() . 'login';
header("location: $login");
}
});
// Publish comment
get('/admin/comments/publish/:postid/:commentid', function ($postid, $commentid) {
if (login()) {
$user = $_SESSION[site_url()]['user'];
$role = user('role', $user);
if ($role === 'admin' || $role === 'editor') {
commentPublish($postid, $commentid);
}
}
$redir = site_url() . 'admin/comments';
header("location: $redir");
});
// Delete comment
get('/admin/comments/delete/:postid/:commentid', function ($postid, $commentid) {
if (login()) {
$user = $_SESSION[site_url()]['user'];
$role = user('role', $user);
if ($role === 'admin' || $role === 'editor') {
commentDelete($postid, $commentid);
}
}
$redir = site_url() . 'admin/comments';
header("location: $redir");
});
// Show admin/field
get('/admin/field', function () {
if (login()) {
@ -5790,6 +6038,46 @@ post('/:year/:month/:name/delete', function () {
}
});
// Submit comment from public form
post('/comments/submit', function () {
if (!local()) {
$redir = site_url();
header("location: $redir");
return;
}
$postId = from($_POST, 'post_id');
$name = from($_POST, 'name');
$email = from($_POST, 'email');
$comment = from($_POST, 'comment');
$parentId = from($_POST, 'parent_id');
$notify = from($_POST, 'notify');
$website = from($_POST, 'website'); // honeypot field
$data = array(
'name' => $name,
'email' => $email,
'comment' => $comment,
'parent_id' => $parentId,
'notify' => $notify,
'website' => $website
);
$result = commentInsert($postId, $data);
if ($result['success']) {
// Redirect back to post with success anchor
$redir = site_url() . $postId . '#comment-success';
// $redir = site_url() . $postId . '#comment-' . $result['comment_id'];
} else {
// Redirect back to post with error
$redir = site_url() . $postId . '#comment-error';
// $redir = site_url() . $postId . '#comment-error?' . $result['message'];
}
header("location: $redir");
});
// If we get here, it means that
// nothing has been matched above
get('.*', function () {

View file

@ -0,0 +1,263 @@
<?php
if (!defined('HTMLY')) die('HTMLy');
/**
* Display comments form
*
* @param string $postId Post or page ID
* @param string $parentId Parent comment ID for replies (optional)
* @return void
*/
function displayCommentsForm($postId, $parentId = null)
{
if (!local()) {
return;
}
$formId = $parentId ? 'reply-form-' . $parentId : 'comment-form';
$submitUrl = site_url() . 'comments/submit';
?>
<form id="<?php echo $formId; ?>" method="POST" action="<?php echo $submitUrl; ?>" class="comment-form">
<input type="hidden" name="post_id" value="<?php echo _h($postId); ?>">
<?php if ($parentId): ?>
<input type="hidden" name="parent_id" value="<?php echo _h($parentId); ?>">
<?php endif; ?>
<!-- Honeypot field (hidden from users) -->
<div style="position:absolute;left:-5000px;" aria-hidden="true">
<input type="text" name="website" tabindex="-1" value="" autocomplete="off">
</div>
<div class="form-group" style="width: 100%">
<label for="name-<?php echo $formId; ?>"><?php echo i18n('Name'); ?> <span class="required">*</span></label>
<input type="text" class="form-control" id="name-<?php echo $formId; ?>" name="name" required>
<label for="email-<?php echo $formId; ?>"><?php echo i18n('Email'); ?> <span class="required">*</span></label>
<input type="email" class="form-control" id="email-<?php echo $formId; ?>" name="email" required>
<br><small class="form-text text-muted"><?php echo i18n('Email_not_published'); ?></small>
</div>
<br clear="all">
<div class="form-group">
<label for="comment-<?php echo $formId; ?>"><?php echo i18n('Comment'); ?> <span class="required">*</span></label>
<textarea class="form-control" id="comment-<?php echo $formId; ?>" name="comment" rows="5" required></textarea>
<small class="form-text text-muted"><?php echo i18n('Comment_formatting_help'); ?></small>
</div>
<!-- Emidio 20251105 - temporarily disabled
<div class="form-group form-check">
<input type="checkbox" class="form-check-input" id="notify-<?php echo $formId; ?>" name="notify" value="1">
<label class="form-check-label" for="notify-<?php echo $formId; ?>">
<?php echo i18n('Notify_new_comments'); ?>
</label>
</div>
-->
<br>
<div class="form-group">
<button type="submit" class="btn btn-primary submit-comment"><?php echo $parentId ? i18n('Post_Reply') : i18n('Post_Comment'); ?></button>
<?php if ($parentId): ?>
<button type="button" class="btn btn-secondary cancel-reply" onclick="cancelReply('<?php echo $parentId; ?>')"><?php echo i18n('Cancel'); ?></button>
<?php endif; ?>
</div>
</form>
<?php
}
/**
* Display single comment
*
* @param array $comment Comment data
* @param string $postId Post ID
* @return void
*/
function displayComment($comment, $postId)
{
$indent = isset($comment['level']) ? $comment['level'] : 0;
$marginLeft = $indent * 0; // 40px per level - changed to 0 Emidio 20251106
// Add visual depth indicator
$depthClass = 'comment-level-' . min($indent, 5); // Max 5 for styling
$borderColor = $indent > 0 ? '#ddd' : '#007bff';
?>
<div id="comment-<?php echo $comment['id']; ?>" class="comment-item <?php echo $depthClass; ?>"
style="margin-left: <?php echo $marginLeft; ?>px; border-left: 3px solid <?php echo $borderColor; ?>;"
data-level="<?php echo $indent; ?>">
<div class="comment-header">
<strong class="comment-author"><?php echo _h($comment['name']); ?></strong>
<span class="comment-date"><?php echo format_date($comment['timestamp']); ?></span>
<!---
<?php if ($indent > 0): ?>
<span class="comment-level-badge"><?php echo i18n('Level'); ?> <?php echo $indent; ?></span>
<?php endif; ?>
--->
</div>
<div class="comment-body">
<?php echo formatCommentText($comment['comment']); ?>
</div>
<div class="comment-footer">
<button class="btn btn-sm btn-link reply-button" onclick="showReplyForm('<?php echo $comment['id']; ?>', '<?php echo $postId; ?>')">
<i class="fa fa-reply"></i> <?php echo i18n('Reply'); ?>
</button>
</div>
<div id="reply-container-<?php echo $comment['id']; ?>" class="reply-container" style="display:none; margin-top:15px;">
<!-- Reply form will be inserted here via JavaScript -->
</div>
<?php
// Display child comments (recursive - unlimited depth)
if (!empty($comment['children'])) {
echo '<div class="comment-children">';
foreach ($comment['children'] as $child) {
displayComment($child, $postId);
}
echo '</div>';
}
?>
</div>
<?php
}
/**
* Display all comments for a post
*
* @param string $postId Post or page ID
* @return void
*/
function displayComments($postId)
{
if (!local()) {
return;
}
$comments = getComments($postId);
if (empty($comments)) {
return;
}
// Build comment tree
$commentTree = buildCommentTree($comments);
?>
<div class="comments-list">
<!--- <h4><?php echo i18n('Comments'); ?> (<?php echo count($comments); ?>)</h4> --->
<?php
foreach ($commentTree as $comment) {
displayComment($comment, $postId);
}
?>
</div>
<?php
}
/**
* Display complete comments section (list + form)
*
* @param string $postId Post or page ID
* @return void
*/
function displayCommentsSection($postId)
{
if (!local()) {
return;
}
?>
<section class="comments comment-box" id="comments">
<!---
<div class="comments-number">
<h3><?php echo i18n("Comments"); ?></h3>
</div>
--->
<?php
// Show success/error messages
$hash = isset($_SERVER['REQUEST_URI']) ? parse_url($_SERVER['REQUEST_URI'], PHP_URL_FRAGMENT) : '';
if ($hash === 'comment-success'):
?>
<div class="alert alert-success">
<?php
if (comments_config('comments.moderation') === 'true') {
echo i18n('Comment_submitted_moderation');
} else {
echo i18n('Comment_submitted_success');
}
?>
</div>
<?php elseif ($hash === 'comment-error'): ?>
<div class="alert alert-danger">
<?php echo i18n('Comment_submission_error'); ?>
</div>
<?php endif; ?>
<?php displayComments($postId); ?>
<div class="comment-form-section">
<h4><?php echo i18n('Leave_a_comment'); ?></h4>
<?php displayCommentsForm($postId); ?>
</div>
</section>
<script>
function showReplyForm(commentId, postId) {
// Hide all other reply forms
document.querySelectorAll('.reply-container').forEach(function(el) {
el.style.display = 'none';
el.innerHTML = '';
});
// Show this reply form
var container = document.getElementById('reply-container-' + commentId);
if (container) {
container.style.display = 'block';
// Build form HTML
var submitUrl = '<?php echo site_url(); ?>comments/submit';
var formId = 'reply-form-' + commentId;
var formHtml = '<form id="' + formId + '" method="POST" action="' + submitUrl + '" class="comment-form">' +
'<input type="hidden" name="post_id" value="' + postId + '">' +
'<input type="hidden" name="parent_id" value="' + commentId + '">' +
'<div style="position:absolute;left:-5000px;" aria-hidden="true">' +
'<input type="text" name="website" tabindex="-1" value="" autocomplete="off">' +
'</div>' +
'<div class="form-group">' +
'<label for="name-' + formId + '"><?php echo i18n("Name"); ?> <span class="required">*</span></label>' +
'<input type="text" class="form-control" id="name-' + formId + '" name="name" required>' +
'</div>' +
'<div class="form-group">' +
'<label for="email-' + formId + '"><?php echo i18n("Email"); ?> <span class="required">*</span></label>' +
'<input type="email" class="form-control" id="email-' + formId + '" name="email" required>' +
'<small class="form-text text-muted"><?php echo i18n("Email_not_published"); ?></small>' +
'</div>' +
'<div class="form-group">' +
'<label for="comment-' + formId + '"><?php echo i18n("Comment"); ?> <span class="required">*</span></label>' +
'<textarea class="form-control" id="comment-' + formId + '" name="comment" rows="5" required></textarea>' +
'<small class="form-text text-muted"><?php echo i18n("Comment_formatting_help"); ?></small>' +
'</div>' +
'<!-- Emidio 20251105 - temporarily disabled ' +
'<div class="form-group form-check">' +
'<input type="checkbox" class="form-check-input" id="notify-' + formId + '" name="notify" value="1">' +
'<label class="form-check-label" for="notify-' + formId + '"><?php echo i18n("Notify_new_comments"); ?></label>' +
'</div>' +
' -->' +
'<br><div class="form-group">' +
'<button type="submit" class="btn btn-primary submit-reply"><?php echo i18n("Post_Reply"); ?></button> ' +
'<button type="button" class="btn btn-secondary cancel-reply" onclick="cancelReply(\'' + commentId + '\')"><?php echo i18n("Cancel"); ?></button>' +
'</div>' +
'</form>';
container.innerHTML = formHtml;
}
}
function cancelReply(commentId) {
var container = document.getElementById('reply-container-' + commentId);
if (container) {
container.style.display = 'none';
container.innerHTML = '';
}
}
</script>
<?php
}
?>

View file

@ -0,0 +1,643 @@
<?php
if (!defined('HTMLY')) die('HTMLy');
use PHPMailer\PHPMailer\PHPMailer;
use PHPMailer\PHPMailer\Exception;
/**
* Check if local comments system is enabled
*
* @return bool
*/
function local()
{
return config('comment.system') === 'local';
}
/**
* Get comments configuration value
*
* @param string $key Configuration key (use 'reload' to force cache reload)
* @return mixed Configuration value or null
*/
function comments_config($key)
{
static $_config = array();
$config_file = 'config/comments.ini';
// Allow cache reload
if ($key === 'reload') {
$_config = array();
return null;
}
if (empty($_config) && file_exists($config_file)) {
$_config = parse_ini_file($config_file, false);
}
return isset($_config[$key]) ? $_config[$key] : null;
}
/**
* Save comments configuration
*
* @param array $data Configuration data to save
* @return bool Success status
*/
function save_comments_config($data = array())
{
$config_file = 'config/comments.ini';
if (!file_exists($config_file)) {
return false;
}
$string = file_get_contents($config_file);
foreach ($data as $word => $value) {
// Ensure null and empty values are saved as empty strings
if ($value === null || $value === '') {
$value = '""';
} else {
// Encode value
$value = json_encode($value, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
}
$map = array('\r\n' => ' \n ', '\r' => ' \n ');
$value = trim(strtr($value, $map));
// Escape dots in the key for regex
$escapedWord = str_replace('.', '\.', $word);
// Try to replace existing line
$pattern = "/^" . $escapedWord . " = .*/m";
if (preg_match($pattern, $string)) {
$string = preg_replace($pattern, $word . ' = ' . $value, $string);
} else {
// If line doesn't exist, add it at the end
$string = rtrim($string) . "\n" . $word . ' = ' . $value . "\n";
}
}
$string = rtrim($string) . "\n";
$result = file_put_contents($config_file, $string, LOCK_EX);
// Clear PHP opcache for this file
if (function_exists('opcache_invalidate')) {
opcache_invalidate($config_file, true);
}
// Clear cache after saving
if ($result !== false) {
comments_config('reload');
}
return $result;
}
/**
* Get comments file path for a post/page
*
* @param string $postId Post or page ID
* @return string File path
*/
function get_comments_file($postId)
{
$dir = 'content/comments/';
if (!is_dir($dir)) {
mkdir($dir, 0775, true);
}
return $dir . sanitize_filename($postId) . '.json';
}
/**
* Sanitize filename
*
* @param string $filename
* @return string Sanitized filename
*/
function sanitize_filename($filename)
{
return preg_replace('/[^a-z0-9\-_]/i', '_', $filename);
}
/**
* Get all comments for a post/page
*
* @param string $postId Post or page ID
* @param bool $includeUnpublished Include unpublished comments (for admin)
* @return array Comments array
*/
function getComments($postId, $includeUnpublished = false)
{
$file = get_comments_file($postId);
if (!file_exists($file)) {
return array();
}
$content = file_get_contents($file);
$comments = json_decode($content, true);
if (!is_array($comments)) {
return array();
}
// Filter unpublished comments if not admin view
if (!$includeUnpublished) {
$comments = array_filter($comments, function($comment) {
return isset($comment['published']) && $comment['published'] === true;
});
}
// Sort by date (oldest first)
usort($comments, function($a, $b) {
return $a['timestamp'] - $b['timestamp'];
});
return $comments;
}
/**
* Get all comments across all posts (for admin)
*
* @return array All comments with post info
*/
function getAllComments()
{
$commentsDir = 'content/comments/';
if (!is_dir($commentsDir)) {
return array();
}
$files = glob($commentsDir . '*.json');
$allComments = array();
foreach ($files as $file) {
$postId = basename($file, '.json');
$comments = getComments($postId, true);
foreach ($comments as $comment) {
$comment['post_id'] = $postId;
$allComments[] = $comment;
}
}
// Sort by date (newest first)
usort($allComments, function($a, $b) {
return $b['timestamp'] - $a['timestamp'];
});
return $allComments;
}
/**
* Generate unique comment ID
*
* @return string Unique comment ID
*/
function generateCommentId()
{
return uniqid('comment_', true);
}
/**
* Build comment tree for nested display
*
* @param array $comments Flat array of comments
* @param string $parentId Parent comment ID (null for root)
* @param int $level Nesting level
* @return array Tree structure
*/
function buildCommentTree($comments, $parentId = null, $level = 0)
{
$tree = array();
foreach ($comments as $comment) {
$commentParent = isset($comment['parent_id']) ? $comment['parent_id'] : null;
if ($commentParent === $parentId) {
$comment['level'] = $level;
$comment['children'] = buildCommentTree($comments, $comment['id'], $level + 1);
$tree[] = $comment;
}
}
return $tree;
}
/**
* Validate comment data
*
* @param array $data Comment data
* @return array Array with 'valid' boolean and 'errors' array
*/
function validateComment($data)
{
$errors = array();
// Validate name
if (empty($data['name']) || strlen(trim($data['name'])) < 2) {
$errors[] = 'Name is required and must be at least 2 characters';
}
// Validate email
if (empty($data['email']) || !filter_var($data['email'], FILTER_VALIDATE_EMAIL)) {
$errors[] = 'Valid email is required';
}
// Validate comment text
if (empty($data['comment']) || strlen(trim($data['comment'])) < 3) {
$errors[] = 'Comment is required and must be at least 3 characters';
}
// Validate honeypot (if enabled)
if (comments_config('comments.honeypot') === 'true') {
if (!empty($data['website'])) {
$errors[] = 'Spam detected';
}
}
return array(
'valid' => empty($errors),
'errors' => $errors
);
}
/**
* Insert a new comment
*
* @param string $postId Post or page ID
* @param array $data Comment data (name, email, comment, parent_id, notify)
* @return array Result with 'success' boolean and 'message' or 'comment_id'
*/
function commentInsert($postId, $data)
{
// Validate comment
$validation = validateComment($data);
if (!$validation['valid']) {
return array(
'success' => false,
'message' => implode(', ', $validation['errors'])
);
}
// Get existing comments
$file = get_comments_file($postId);
$comments = array();
if (file_exists($file)) {
$content = file_get_contents($file);
$comments = json_decode($content, true);
if (!is_array($comments)) {
$comments = array();
}
}
// Create new comment
$commentId = generateCommentId();
$timestamp = time();
$comment = array(
'id' => $commentId,
'name' => trim($data['name']),
'email' => trim($data['email']),
'comment' => trim($data['comment']),
'timestamp' => $timestamp,
'date' => date('Y-m-d H:i:s', $timestamp),
'parent_id' => isset($data['parent_id']) && !empty($data['parent_id']) ? $data['parent_id'] : null,
'notify' => isset($data['notify']) && $data['notify'] === '1',
'published' => comments_config('comments.moderation') !== 'true', // Auto-publish if moderation disabled
'ip' => $_SERVER['REMOTE_ADDR'] ?? 'unknown'
);
// Add comment to array
$comments[] = $comment;
// Save to file
$json = json_encode($comments, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
if (file_put_contents($file, $json, LOCK_EX) === false) {
return array(
'success' => false,
'message' => 'Failed to save comment'
);
}
// Send notifications
sendCommentNotifications($postId, $comment, $comments);
return array(
'success' => true,
'comment_id' => $commentId,
'message' => $comment['published'] ? 'Comment published successfully' : 'Comment submitted for moderation'
);
}
/**
* Publish a comment (approve from moderation)
*
* @param string $postId Post or page ID
* @param string $commentId Comment ID
* @return bool Success status
*/
function commentPublish($postId, $commentId)
{
$file = get_comments_file($postId);
if (!file_exists($file)) {
return false;
}
$content = file_get_contents($file);
$comments = json_decode($content, true);
if (!is_array($comments)) {
return false;
}
$updated = false;
foreach ($comments as &$comment) {
if ($comment['id'] === $commentId) {
$comment['published'] = true;
$updated = true;
// Send notifications to other commenters
sendCommentNotifications($postId, $comment, $comments, false);
break;
}
}
if (!$updated) {
return false;
}
$json = json_encode($comments, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
return file_put_contents($file, $json, LOCK_EX) !== false;
}
/**
* Delete a comment
*
* @param string $postId Post or page ID
* @param string $commentId Comment ID
* @return bool Success status
*/
function commentDelete($postId, $commentId)
{
$file = get_comments_file($postId);
if (!file_exists($file)) {
return false;
}
$content = file_get_contents($file);
$comments = json_decode($content, true);
if (!is_array($comments)) {
return false;
}
// Remove comment and its children
$comments = array_filter($comments, function($comment) use ($commentId) {
return $comment['id'] !== $commentId &&
(empty($comment['parent_id']) || $comment['parent_id'] !== $commentId);
});
// Reindex array
$comments = array_values($comments);
$json = json_encode($comments, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
return file_put_contents($file, $json, LOCK_EX) !== false;
}
/**
* Modify a comment
*
* @param string $postId Post or page ID
* @param string $commentId Comment ID
* @param array $data New comment data
* @return bool Success status
*/
function commentModify($postId, $commentId, $data)
{
$file = get_comments_file($postId);
if (!file_exists($file)) {
return false;
}
$content = file_get_contents($file);
$comments = json_decode($content, true);
if (!is_array($comments)) {
return false;
}
$updated = false;
foreach ($comments as &$comment) {
if ($comment['id'] === $commentId) {
// Update fields
if (isset($data['name'])) {
$comment['name'] = trim($data['name']);
}
if (isset($data['email'])) {
$comment['email'] = trim($data['email']);
}
if (isset($data['comment'])) {
$comment['comment'] = trim($data['comment']);
}
if (isset($data['published'])) {
$comment['published'] = (bool)$data['published'];
}
$comment['modified'] = date('Y-m-d H:i:s');
$updated = true;
break;
}
}
if (!$updated) {
return false;
}
$json = json_encode($comments, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
return file_put_contents($file, $json, LOCK_EX) !== false;
}
/**
* Send comment notifications
*
* @param string $postId Post or page ID
* @param array $newComment The new comment
* @param array $allComments All comments for this post
* @param bool $notifyAdmin Notify admin (default true)
* @return void
*/
function sendCommentNotifications($postId, $newComment, $allComments, $notifyAdmin = true)
{
// Check if notifications are enabled
if (comments_config('comments.notify') !== 'true' ||
comments_config('comments.mail.enabled') !== 'true') {
return;
}
$recipients = array();
// Add admin email
if ($notifyAdmin) {
$adminEmail = comments_config('comments.admin.email');
if (!empty($adminEmail) && filter_var($adminEmail, FILTER_VALIDATE_EMAIL)) {
$recipients[$adminEmail] = array(
'name' => 'Administrator',
'type' => 'admin'
);
}
}
// Add parent comment author (if replying)
if (!empty($newComment['parent_id'])) {
foreach ($allComments as $comment) {
if ($comment['id'] === $newComment['parent_id'] &&
$comment['notify'] &&
$comment['email'] !== $newComment['email']) {
$recipients[$comment['email']] = array(
'name' => $comment['name'],
'type' => 'parent'
);
}
}
}
// Add other commenters in same thread who want notifications
foreach ($allComments as $comment) {
if ($comment['notify'] &&
$comment['email'] !== $newComment['email'] &&
$comment['id'] !== $newComment['id']) {
// Same thread = same parent or no parent
if ($comment['parent_id'] === $newComment['parent_id']) {
$recipients[$comment['email']] = array(
'name' => $comment['name'],
'type' => 'thread'
);
}
}
}
// Send emails
foreach ($recipients as $email => $info) {
sendCommentEmail($email, $info['name'], $postId, $newComment, $info['type']);
}
}
/**
* Send a single comment notification email
*
* @param string $to Recipient email
* @param string $toName Recipient name
* @param string $postId Post ID
* @param array $comment Comment data
* @param string $type Notification type (admin, parent, thread)
* @return bool Success status
*/
function sendCommentEmail($to, $toName, $postId, $comment, $type = 'admin')
{
try {
$mail = new PHPMailer(true);
// Server settings
$mail->isSMTP();
$mail->Host = comments_config('comments.mail.host');
$mail->SMTPAuth = true;
$mail->Username = comments_config('comments.mail.username');
$mail->Password = comments_config('comments.mail.password');
$mail->Port = comments_config('comments.mail.port');
$encryption = comments_config('comments.mail.encryption');
if ($encryption === 'tls') {
$mail->SMTPSecure = PHPMailer::ENCRYPTION_STARTTLS;
} elseif ($encryption === 'ssl') {
$mail->SMTPSecure = PHPMailer::ENCRYPTION_SMTPS;
}
// Recipients
$mail->setFrom(
comments_config('comments.mail.from.email'),
comments_config('comments.mail.from.name')
);
$mail->addAddress($to, $toName);
// Content
$mail->isHTML(true);
$mail->CharSet = 'UTF-8';
if ($type === 'admin') {
$mail->Subject = 'New comment awaiting moderation';
$mail->Body = "
<h3>New comment on: $postId</h3>
<p><strong>From:</strong> {$comment['name']} ({$comment['email']})</p>
<p><strong>Comment:</strong></p>
<p>" . nl2br(htmlspecialchars($comment['comment'])) . "</p>
<p><a href='" . site_url() . "admin/comments'>Moderate comments</a></p>
";
} else {
$mail->Subject = 'New reply to your comment';
$mail->Body = "
<h3>Someone replied to your comment on: $postId</h3>
<p><strong>From:</strong> {$comment['name']}</p>
<p><strong>Comment:</strong></p>
<p>" . nl2br(htmlspecialchars($comment['comment'])) . "</p>
<p><a href='" . site_url() . "$postId#comment-{$comment['id']}'>View comment</a></p>
";
}
$mail->send();
return true;
} catch (Exception $e) {
error_log("Comment notification email failed: {$mail->ErrorInfo}");
return false;
}
}
/**
* Get comment count for a post
*
* @param string $postId Post or page ID
* @param bool $includeUnpublished Include unpublished comments
* @return int Comment count
*/
function getCommentCount($postId, $includeUnpublished = false)
{
$comments = getComments($postId, $includeUnpublished);
return count($comments);
}
/**
* Get pending comments count (for admin)
*
* @return int Pending comments count
*/
function getPendingCommentsCount()
{
$allComments = getAllComments();
$pending = array_filter($allComments, function($comment) {
return !$comment['published'];
});
return count($pending);
}
/**
* Format comment text (allow basic formatting)
*
* @param string $text Comment text
* @return string Formatted text
*/
function formatCommentText($text)
{
// Convert line breaks
$text = nl2br(htmlspecialchars($text, ENT_QUOTES, 'UTF-8'));
// Allow simple bold with **text** or __text__
$text = preg_replace('/\*\*(.+?)\*\*/', '<strong>$1</strong>', $text);
$text = preg_replace('/__(.+?)__/', '<strong>$1</strong>', $text);
return $text;
}
?>

View file

@ -3535,7 +3535,7 @@ function is_index()
}
} else {
$req = strtok($_SERVER["REQUEST_URI"], '?');
if (stripos($req, '/category/') !== false || stripos($req, '/archive/') !== false || stripos($req, '/tag/') !== false || stripos($req, '/search/') !== false || stripos($req, '/type/') !== false || stripos($req, '/' . blog_path()) !== false || $req == site_path() . '/') {
if (stripos($req, '/category/') !== false || stripos($req, '/archive/') !== false || stripos($req, '/tag/') !== false || stripos($req, '/search/') !== false || stripos($req, '/type/') !== false || stripos($req, '/' . blog_path()) !== false || $req == site_path() . '/' || $req == '/') {
return true;
} else {
return false;

View file

@ -12,4 +12,6 @@ return array(
'8432047aca7938f88a2098a2f7770228' => $baseDir . '/system/admin/admin.php',
'1b9bf2d9d029f1364c3d7262b5375c41' => $baseDir . '/system/includes/session.php',
'62f038defb1b29aab3998eb437e01df9' => $baseDir . '/system/includes/opml.php',
'7d7ae14f7bb933ac50b7f6e42f6a7d60' => $baseDir . '/system/includes/comments.php',
'2591226a04118e81824e40ae6712427b' => $baseDir . '/system/includes/comments-frontend.php',
);

View file

@ -13,6 +13,8 @@ class ComposerStaticInitd88c6c25320034df85dd42f1462fbda7
'8432047aca7938f88a2098a2f7770228' => __DIR__ . '/../../..' . '/system/admin/admin.php',
'1b9bf2d9d029f1364c3d7262b5375c41' => __DIR__ . '/../../..' . '/system/includes/session.php',
'62f038defb1b29aab3998eb437e01df9' => __DIR__ . '/../../..' . '/system/includes/opml.php',
'7d7ae14f7bb933ac50b7f6e42f6a7d60' => __DIR__ . '/../../..' . '/system/includes/comments.php',
'2591226a04118e81824e40ae6712427b' => __DIR__ . '/../../..' . '/system/includes/comments-frontend.php',
);
public static $prefixLengthsPsr4 = array (