I did this rushing, must run to work so i hardcoded
open includes/functions.php look for
function check_system_requirements()
and before that add
function get_live_forum_activity() {
global $db, $system;
$activity = [
'latest_reply' => null,
'recent_threads' => [],
'active_thread' => null
];
// Get latest reply with thread and user info
$get_latest_reply = $db->query("
SELECT
fr.reply_id,
fr.text as reply_text,
fr.time as reply_time,
ft.thread_id,
ft.title as thread_title,
ft.forum_id,
f.forum_name,
u.user_id,
u.user_name,
u.user_firstname,
u.user_lastname,
u.user_picture,
u.user_gender
FROM forums_replies fr
INNER JOIN forums_threads ft ON fr.thread_id = ft.thread_id
INNER JOIN forums f ON ft.forum_id = f.forum_id
INNER JOIN users u ON fr.user_id = u.user_id
ORDER BY fr.reply_id DESC
LIMIT 1
");
if ($get_latest_reply->num_rows > 0) {
$reply = $get_latest_reply->fetch_assoc();
$activity['latest_reply'] = [
'user_picture' => get_picture($reply['user_picture'], $reply['user_gender']),
'user_name' => ($system['show_usernames_enabled']) ? $reply['user_name'] : $reply['user_firstname'] . " " . $reply['user_lastname'],
'thread_id' => $reply['thread_id'],
'thread_title' => $reply['thread_title'],
'thread_slug' => get_url_text($reply['thread_title']),
'forum_id' => $reply['forum_id'],
'forum_name' => $reply['forum_name'],
'forum_slug' => get_url_text($reply['forum_name']),
'reply_text' => html_entity_decode(strip_tags($reply['reply_text']), ENT_QUOTES, 'UTF-8'),
'time_ago' => get_time_text($reply['reply_time'])
];
}
// Get 2 recent threads
$get_recent_threads = $db->query("
SELECT
ft.thread_id,
ft.title as thread_title,
ft.text as thread_text,
ft.time as thread_time,
ft.forum_id,
f.forum_name,
u.user_id,
u.user_name,
u.user_firstname,
u.user_lastname,
u.user_picture,
u.user_gender
FROM forums_threads ft
INNER JOIN forums f ON ft.forum_id = f.forum_id
INNER JOIN users u ON ft.user_id = u.user_id
ORDER BY ft.thread_id DESC
LIMIT 1
");
while ($thread = $get_recent_threads->fetch_assoc()) {
$activity['recent_threads'][] = [
'user_picture' => get_picture($thread['user_picture'], $thread['user_gender']),
'user_name' => ($system['show_usernames_enabled']) ? $thread['user_name'] : $thread['user_firstname'] . " " . $thread['user_lastname'],
'thread_id' => $thread['thread_id'],
'thread_title' => $thread['thread_title'],
'thread_slug' => get_url_text($thread['thread_title']),
'thread_text' => strip_tags($thread['thread_text']),
'forum_id' => $thread['forum_id'],
'forum_name' => $thread['forum_name'],
'forum_slug' => get_url_text($thread['forum_name']),
'time_ago' => get_time_text($thread['thread_time'])
];
}
// Get most active thread (most replies in last 24 hours)
$get_active_thread = $db->query("
SELECT
ft.thread_id,
ft.title as thread_title,
ft.text as thread_text,
ft.forum_id,
f.forum_name,
u.user_id,
u.user_name,
u.user_firstname,
u.user_lastname,
u.user_picture,
u.user_gender,
COUNT(fr.reply_id) as reply_count,
MAX(fr.time) as last_activity
FROM forums_threads ft
INNER JOIN forums f ON ft.forum_id = f.forum_id
INNER JOIN users u ON ft.user_id = u.user_id
LEFT JOIN forums_replies fr ON ft.thread_id = fr.thread_id AND fr.time >= DATE_SUB(NOW(), INTERVAL 24 HOUR)
WHERE ft.time >= DATE_SUB(NOW(), INTERVAL 7 DAY)
GROUP BY ft.thread_id
ORDER BY reply_count DESC, last_activity DESC
LIMIT 1
");
if ($get_active_thread->num_rows > 0) {
$thread = $get_active_thread->fetch_assoc();
$activity['active_thread'] = [
'user_picture' => get_picture($thread['user_picture'], $thread['user_gender']),
'user_name' => ($system['show_usernames_enabled']) ? $thread['user_name'] : $thread['user_firstname'] . " " . $thread['user_lastname'],
'thread_id' => $thread['thread_id'],
'thread_title' => $thread['thread_title'],
'thread_slug' => get_url_text($thread['thread_title']),
'thread_text' => strip_tags($thread['thread_text']),
'forum_id' => $thread['forum_id'],
'forum_name' => $thread['forum_name'],
'forum_slug' => get_url_text($thread['forum_name']),
'reply_count' => $thread['reply_count'],
'time_ago' => get_time_text($thread['last_activity'])
];
}
return $activity;
}
/**
* Get time ago text
*
* @param string $timestamp
* @return string
*/
function get_time_text($timestamp) {
// null/empty -> avoid strtotime(null)
if ($timestamp === null) {
return "";
}
// allow unix timestamps
if (is_int($timestamp) || (is_string($timestamp) && ctype_digit($timestamp))) {
$ts = (int) $timestamp;
} else {
$timestamp = trim((string) $timestamp);
if ($timestamp === "") {
return "";
}
$ts = strtotime($timestamp);
if ($ts === false) {
return "";
}
}
$time_diff = time() - $ts;
// future dates
if ($time_diff < 0) {
$time_diff = abs($time_diff);
if ($time_diff < 60) return "in a moment";
if ($time_diff < 3600) { $m = (int) floor($time_diff/60); return "in {$m} minute" . ($m > 1 ? "s" : ""); }
if ($time_diff < 86400) { $h = (int) floor($time_diff/3600); return "in {$h} hour" . ($h > 1 ? "s" : ""); }
$d = (int) floor($time_diff/86400); return "in {$d} day" . ($d > 1 ? "s" : "");
}
if ($time_diff < 60) return "just now";
if ($time_diff < 3600) { $m = (int) floor($time_diff/60); return $m . " minute" . ($m > 1 ? "s" : "") . " ago"; }
if ($time_diff < 86400) { $h = (int) floor($time_diff/3600); return $h . " hour" . ($h > 1 ? "s" : "") . " ago"; }
$d = (int) floor($time_diff/86400); return $d . " day" . ($d > 1 ? "s" : "") . " ago";
}
Create content/themes/default/templates/_widgets.live_forum.tpl
and add
<style>
/* ===============================
Live Forum — Anti Overflow
=============================== */
#live-forum-widget .live-forum-row{
display:flex;
gap:10px;
max-width:100%;
overflow:hidden;
align-items:flex-start;
margin-bottom:10px;
}
#live-forum-widget .live-forum-avatar{
width:38px;
height:38px;
border-radius:50%;
overflow:hidden;
flex-shrink:0;
}
#live-forum-widget .live-forum-avatar img{
width:100%;
height:100%;
object-fit:cover;
display:block;
}
#live-forum-widget .live-forum-content{
flex:1;
min-width:0; /* required for ellipsis/clamp inside flex */
max-width:100%;
overflow:hidden;
}
#live-forum-widget .live-forum-label{
font-size:12px;
color:#6c757d;
margin-bottom:2px;
}
#live-forum-widget .live-forum-title{
font-weight:600;
max-width:100%;
overflow:hidden;
white-space:nowrap;
text-overflow:ellipsis;
}
#live-forum-widget .live-forum-title a{
color:inherit;
text-decoration:none;
}
#live-forum-widget .live-forum-title a:hover{
text-decoration:underline;
}
#live-forum-widget .live-forum-meta{
font-size:11px;
color:#6c757d;
margin-bottom:3px;
max-width:100%;
overflow:hidden;
text-overflow:ellipsis;
white-space:nowrap;
}
#live-forum-widget .live-forum-excerpt{
font-size:12px;
color:#6c757d;
line-height:1.35;
/* 2-line clamp */
display:-webkit-box;
-webkit-box-orient:vertical;
-webkit-line-clamp:2;
overflow:hidden;
/* hard anti-overflow */
word-break:break-word;
overflow-wrap:anywhere;
max-width:100%;
}
</style>
<!-- Live Forum Widget -->
<!-- Live Forum Widget -->
{assign var="forum_activity" value=$user->get_live_forum_activity()}
<div class="card shadow-sm mb20 border-start border-primary" style="border-width:4px !important;">
<div class="card-header d-flex align-items-center justify-content-between">
<h6 class="mb-0">π₯ Live Forum — What's happening now</h6>
<a href="{$system['system_url']}/forums" class="small link-primary">Open forum</a>
</div>
<div class="card-body p-3" id="live-forum-widget">
<!-- Latest Reply -->
{if $forum_activity.latest_reply}
<div class="live-forum-row">
<div class="live-forum-avatar">
<img src="{$forum_activity.latest_reply.user_picture}" alt="{$forum_activity.latest_reply.user_name}">
</div>
<div class="live-forum-content">
<div class="live-forum-label">π¬ Latest reply</div>
<div class="live-forum-title">
<a href="{$system['system_url']}/forums/thread/{$forum_activity.latest_reply.thread_id}/{$forum_activity.latest_reply.thread_slug}">
{$forum_activity.latest_reply.thread_title}
</a>
</div>
<div class="live-forum-meta">
{$forum_activity.latest_reply.user_name} • {$forum_activity.latest_reply.time_ago}
</div>
<div class="live-forum-excerpt">
{html_entity_decode($forum_activity.latest_reply.reply_text, ENT_QUOTES, 'UTF-8')|strip_tags|truncate:100}
</div>
</div>
</div>
{/if}
<!-- Recent Threads -->
{foreach $forum_activity.recent_threads as $thread}
<div class="live-forum-row {if $thread@last && !$forum_activity.active_thread}mb-0{/if}">
<div class="live-forum-avatar">
<img src="{$thread.user_picture}" alt="{$thread.user_name}">
</div>
<div class="live-forum-content">
<div class="live-forum-label">π Recent thread</div>
<div class="live-forum-title">
<a href="{$system['system_url']}/forums/thread/{$thread.thread_id}/{$thread.thread_slug}">
{$thread.thread_title}
</a>
</div>
<div class="live-forum-meta">
{$thread.user_name} • {$thread.time_ago}
</div>
<div class="live-forum-excerpt">
{html_entity_decode($thread.thread_text, ENT_QUOTES, 'UTF-8')|strip_tags|truncate:100}
</div>
</div>
</div>
{/foreach}
<!-- Most Active Thread -->
{if $forum_activity.active_thread}
<div class="live-forum-row mb-0">
<div class="live-forum-avatar">
<img src="{$forum_activity.active_thread.user_picture}" alt="{$forum_activity.active_thread.user_name}">
</div>
<div class="live-forum-content">
<div class="live-forum-label">π₯ Active now ({$forum_activity.active_thread.reply_count} replies today)</div>
<div class="live-forum-title">
<a href="{$system['system_url']}/forums/thread/{$forum_activity.active_thread.thread_id}/{$forum_activity.active_thread.thread_slug}">
{$forum_activity.active_thread.thread_title}
</a>
</div>
<div class="live-forum-meta">
{$forum_activity.active_thread.user_name} • {$forum_activity.active_thread.time_ago}
</div>
<div class="live-forum-excerpt">
{html_entity_decode($forum_activity.active_thread.thread_text, ENT_QUOTES, 'UTF-8')|strip_tags|truncate:100}
</div>
</div>
</div>
{/if}
</div>
<div class="card-footer text-center p-2">
<a href="{$system['system_url']}/forums" class="btn btn-sm btn-primary rounded-pill px-4">
π¬ Go to forum
</a>
</div>
</div>
Open index.newsfeed.tpl , I added after the publisher
look for
<!-- publisher -->
{include file='_publisher.tpl' _handle="me" _node_can_monetize_content=$user->_data['can_monetize_content'] _node_monetization_enabled=$user->_data['user_monetization_enabled'] _node_monetization_plans=$user->_data['user_monetization_plans'] _privacy=true}
<!-- publisher -->
and after that add
{include file='_widgets.live_forum.tpl'}