xgit simple git

nexus

nexus

clone git clone https://kb.hax.al/nexus

forum/search.php

1 <?php
2 require_once __DIR__ . '/../includes/bootstrap.php';
3 
4 $q    = get('q');
5 $type = get('type', 'topics'); // topics | users | posts
6 if (!in_array($type, ['topics','users','posts'])) $type = 'topics';
7 
8 $topicResults = [];
9 $postResults  = [];
10 $userResults  = [];
11 
12 if (mb_strlen($q) >= 2) {
13     $like = '%' . $q . '%';
14 
15     if ($type === 'topics' || $type === 'posts') {
16         // Topics matching title
17         $topicResults = DB::rows("
18             SELECT t.*, u.username, c.name AS cat_name, c.slug AS cat_slug, c.color AS cat_color,
19                    NULL AS match_post_id
20             FROM topics t
21             JOIN users u ON u.id = t.user_id
22             JOIN categories c ON c.id = t.category_id
23             WHERE t.title LIKE ?
24             ORDER BY t.last_post_at DESC LIMIT 20
25         ", [$like]);
26     }
27 
28     if ($type === 'posts') {
29         // Posts matching content β€” include post ID so we can anchor to it
30         $postResults = DB::rows("
31             SELECT p.id AS post_id, p.content, p.post_num, p.created_at AS post_date,
32                    t.id AS topic_id, t.title, t.slug,
33                    u.username, u.avatar,
34                    c.name AS cat_name, c.slug AS cat_slug, c.color AS cat_color
35             FROM posts p
36             JOIN topics t ON t.id = p.topic_id
37             JOIN users u ON u.id = p.user_id
38             JOIN categories c ON c.id = t.category_id
39             WHERE p.content LIKE ? AND p.deleted = 0
40             ORDER BY p.created_at DESC LIMIT 30
41         ", [$like]);
42     }
43 
44     if ($type === 'users') {
45         $userResults = DB::rows("
46             SELECT id, username, avatar, role, karma, post_count, bio, joined_at
47             FROM users
48             WHERE (username LIKE ? OR bio LIKE ?) AND suspended = 0
49             ORDER BY post_count DESC LIMIT 30
50         ", [$like, $like]);
51     }
52 }
53 
54 $allCount = count($topicResults) + count($postResults) + count($userResults);
55 $PAGE_TITLE = 'Search';
56 include __DIR__ . '/../views/partials/layout.php';
57 ?>
58 <div style="max-width:800px">
59 
60   <!-- Search form with type tabs -->
61   <form method="GET" id="searchForm">
62     <div style="display:flex;gap:10px;margin-bottom:16px">
63       <div style="position:relative;flex:1;max-width:560px">
64         <svg style="position:absolute;left:12px;top:50%;transform:translateY(-50%);color:var(--faint);pointer-events:none" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="11" cy="11" r="8"/><path d="m21 21-4.35-4.35"/></svg>
65         <input type="text" name="q" value="<?= e($q) ?>" class="fi"
66                style="padding-left:34px" placeholder="Search topics, posts, users…"
67                autofocus autocomplete="off" id="mainSearchInput">
68       </div>
69       <button type="submit" class="btn-primary">Search</button>
70     </div>
71 
72     <!-- Type tabs -->
73     <div style="display:flex;gap:0;margin-bottom:22px;border-bottom:2px solid var(--border)">
74       <?php foreach (['topics'=>'πŸ“‹ Topics','posts'=>'πŸ’¬ Posts','users'=>'πŸ‘₯ Users'] as $t => $lbl): ?>
75         <button type="submit" name="type" value="<?= $t ?>"
76                 class="srch-tab <?= $type===$t?'active':'' ?>"><?= $lbl ?></button>
77       <?php endforeach; ?>
78     </div>
79   </form>
80 
81   <?php if (mb_strlen($q) >= 2): ?>
82     <p class="search-info">
83       <?= count($type==='posts'?$postResults:($type==='users'?$userResults:$topicResults)) ?>
84       result<?= count($type==='posts'?$postResults:($type==='users'?$userResults:$topicResults))!==1?'s':'' ?>
85       for "<strong><?= e($q) ?></strong>"
86     </p>
87 
88     <?php if ($type === 'topics'): ?>
89       <?php if (empty($topicResults)): ?>
90         <div class="empty-state"><p>No topics found matching "<?= e($q) ?>".</p></div>
91       <?php else: ?>
92         <?php foreach ($topicResults as $t): ?>
93           <div class="topic-row">
94             <div class="tr-body">
95               <a href="<?= u('forum/topic.php?slug=' . urlencode($t['slug'])) ?>" class="tr-title"><?= e($t['title']) ?></a>
96               <div class="tr-meta">
97                 <a href="<?= u('forum/category.php?slug=' . urlencode($t['cat_slug'])) ?>"
98                    class="cat-tag" style="--cc:<?= e($t['cat_color']) ?>"><?= e($t['cat_name']) ?></a>
99                 by <a href="<?= u('users/profile.php?u=' . urlencode($t['username'])) ?>">@<?= e($t['username']) ?></a>
100                 <span class="ago" data-ts="<?= e($t['created_at']) ?>"></span>
101               </div>
102             </div>
103             <div class="tr-counts"><span>πŸ’¬ <?= $t['reply_count'] ?></span><span>πŸ‘ <?= $t['views'] ?></span></div>
104           </div>
105         <?php endforeach; ?>
106       <?php endif; ?>
107 
108     <?php elseif ($type === 'posts'): ?>
109       <?php if (empty($postResults)): ?>
110         <div class="empty-state"><p>No posts found matching "<?= e($q) ?>".</p></div>
111       <?php else: ?>
112         <?php foreach ($postResults as $p): ?>
113           <?php
114             // Build URL with post anchor so user lands on the exact post
115             $postUrl = u('forum/topic.php?slug=' . urlencode($p['slug']) . '&goto=' . $p['post_id']) . '#post-' . $p['post_id'];
116             // Highlight the search term in content snippet
117             $snippet = mb_substr($p['content'], 0, 200);
118             $pos     = mb_stripos($p['content'], $q);
119             if ($pos !== false) {
120                 $start   = max(0, $pos - 60);
121                 $snippet = ($start > 0 ? '…' : '') . mb_substr($p['content'], $start, 200) . (mb_strlen($p['content']) > $start+200 ? '…' : '');
122             }
123           ?>
124           <div class="post-search-card">
125             <div class="psc-head">
126               <div style="display:flex;align-items:center;gap:8px;flex:1;min-width:0">
127                 <?php if ($p['avatar']): ?>
128                   <img src="<?= e($p['avatar']) ?>" class="av-sm" alt="">
129                 <?php else: ?>
130                   <span class="av-sm av-init"><?= strtoupper($p['username'][0]) ?></span>
131                 <?php endif; ?>
132                 <div style="min-width:0">
133                   <a href="<?= u('users/profile.php?u='.urlencode($p['username'])) ?>" style="font-weight:600;font-size:13px">@<?= e($p['username']) ?></a>
134                   <span style="color:var(--faint);font-size:12px;margin-left:6px">
135                     in <a href="<?= u('forum/topic.php?slug='.urlencode($p['slug'])) ?>" style="color:var(--muted)"><?= e(mb_substr($p['title'],0,60)) ?><?= mb_strlen($p['title'])>60?'…':'' ?></a>
136                   </span>
137                 </div>
138               </div>
139               <div style="display:flex;align-items:center;gap:8px;flex-shrink:0">
140                 <span class="ago" data-ts="<?= e($p['post_date']) ?>" style="font-size:12px;color:var(--faint)"></span>
141                 <a href="<?= e($postUrl) ?>" class="btn-ghost btn-sm">View Post β†’</a>
142               </div>
143             </div>
144             <div class="psc-body"><?= e($snippet) ?></div>
145           </div>
146         <?php endforeach; ?>
147       <?php endif; ?>
148 
149     <?php elseif ($type === 'users'): ?>
150       <?php if (empty($userResults)): ?>
151         <div class="empty-state"><p>No users found matching "<?= e($q) ?>".</p></div>
152       <?php else: ?>
153         <div class="user-search-grid">
154           <?php foreach ($userResults as $u):
155             $kt = karma_tier((int)$u['karma']); ?>
156             <a href="<?= u('users/profile.php?u='.urlencode($u['username'])) ?>" class="user-search-card">
157               <div class="usc-av">
158                 <?php if ($u['avatar']): ?>
159                   <img src="<?= e($u['avatar']) ?>" class="av-lg" alt="">
160                 <?php else: ?>
161                   <span class="av-lg av-init"><?= strtoupper($u['username'][0]) ?></span>
162                 <?php endif; ?>
163               </div>
164               <div class="usc-body">
165                 <div class="usc-name">@<?= e($u['username']) ?> <span class="role-tag role-<?= e($u['role']) ?>"><?= e($u['role']) ?></span></div>
166                 <?php if ($u['bio']): ?><div class="usc-bio"><?= e(mb_substr($u['bio'],0,80)) ?></div><?php endif; ?>
167                 <div class="usc-stats">
168                   <span><?= number_format($u['post_count']) ?> posts</span>
169                   <span>Β·</span>
170                   <span style="color:<?= e($kt['color']) ?>"><?= $kt['icon'] ?> <?= number_format((int)$u['karma']) ?> karma</span>
171                 </div>
172               </div>
173             </a>
174           <?php endforeach; ?>
175         </div>
176       <?php endif; ?>
177     <?php endif; ?>
178 
179   <?php elseif ($q): ?>
180     <div class="alert warn">Please enter at least 2 characters to search.</div>
181   <?php endif; ?>
182 </div>
183 
184 <script>
185 // Auto-submit on tab change keeps query
186 document.querySelectorAll('.srch-tab').forEach(function(btn){
187   btn.addEventListener('click',function(){
188     document.getElementById('mainSearchInput').form.submit();
189   });
190 });
191 </script>
192 <?php include __DIR__ . '/../views/partials/layout_end.php'; ?>