forum.php

main 1149 lines · 51.3 KB Raw
Alex Alex Commit Initial commit 01/10/2026 09:20
1<?php require_once 'engine/init.php';
2znote_csrf_protect_public_post();
3theme_open();
4protect_page();
5error_reporting(E_ALL ^ E_NOTICE);
6if (!$config['forum']['enabled']) admin_only($user_data);
7znote_forum_style();
8echo '<div class="znx-forum">';
9/* -------------------------------
10 --- Znote AAC forum ---
11 -------------------------------
12 Created by Znote.
13 Version 1.4.
14
15 Changelog (1.0 --> 1.2):
16 - Updated to the new date/clock time system
17 - Bootstrap design support.
18
19 Changelog (1.2 --> 1.3):
20 - Show character outfit as avatar
21 - Show in-game position
22
23 Changelog (1.3 -> 1.4):
24 - Fix SQL query error when editing Board name.
25*/
26// BBCode rendering now lives in engine/function/bbcode.php as znote_bbcode().
27
28function znote_forum_editor_assets() {
29 static $loaded = false;
30 if ($loaded) return;
31 $loaded = true;
32 $v = '3.2.1';
33 ?>
34 <link rel="stylesheet" href="assets/sceditor/themes/defaultdark.min.css?v=<?php echo $v; ?>">
35 <link rel="stylesheet" href="assets/sceditor/znote-editor.css?v=<?php echo $v; ?>">
36 <script src="assets/sceditor/sceditor.min.js?v=<?php echo $v; ?>"></script>
37 <script src="assets/sceditor/formats/bbcode.min.js?v=<?php echo $v; ?>"></script>
38 <script src="assets/sceditor/icons/material.min.js?v=<?php echo $v; ?>"></script>
39 <script src="assets/sceditor/plugins/undo.min.js?v=<?php echo $v; ?>"></script>
40 <script src="assets/sceditor/plugins/autoyoutube.min.js?v=<?php echo $v; ?>"></script>
41 <script src="assets/sceditor/znote-editor.js?v=<?php echo $v; ?>"></script>
42 <?php
43}
44
45function znote_forum_editor($name, $value = '', $height = 260) {
46 global $config;
47 znote_forum_style();
48 $max = (int)($config['forum']['maxImagesPerPost'] ?? 1);
49 ?>
50 <div class="znote-editor-wrap">
51 <textarea class="znote-editor" name="<?php echo $name; ?>"
52 data-max-images="<?php echo $max; ?>"
53 data-height="<?php echo (int)$height; ?>"
54 data-asset-base="assets/sceditor/"><?php echo $value; ?></textarea>
55 </div>
56 <p class="znote-editor-note"><?php
57 echo ($max > 0)
58 ? 'Up to ' . $max . ' image' . ($max === 1 ? '' : 's') . ' per post.'
59 : 'Images are not allowed in forum posts.';
60 ?></p>
61 <?php
62 znote_forum_editor_assets();
63}
64
65/**
66 * The "Post as <character>" picker plus the submit button.
67 *
68 * This used to be a <select multiple>, which browsers draw as a scrolling list
69 * box with nothing selected by default - so submitting without first clicking a
70 * name posted no character id at all and the action silently did nothing. A
71 * plain dropdown always has a value, and when the account has a single
72 * character there is nothing to choose, so it is shown as a label instead.
73 */
74function znote_forum_style() {
75 static $done = false;
76 if ($done) return;
77 $done = true;
78 echo '<link rel="stylesheet" href="assets/forum.css?v=4.1.0">' . "\n";
79}
80
81/** Short relative time for the board / thread lists. */
82function znote_forum_ago($ts) {
83 $ts = (int)$ts;
84 if ($ts <= 0) return '';
85 $d = time() - $ts;
86 if ($d < 0) $d = 0;
87 if ($d < 60) return t('forum.ago_now');
88 if ($d < 3600) return t('forum.ago_min', array('n' => (int)floor($d / 60)));
89 if ($d < 86400) return t('forum.ago_hour', array('n' => (int)floor($d / 3600)));
90 if ($d < 2592000) return t('forum.ago_day', array('n' => (int)floor($d / 86400)));
91 return date('M j, Y', $ts);
92}
93function znote_forum_e($s) { return htmlspecialchars((string)$s, ENT_QUOTES, 'UTF-8'); }
94
95function znote_forum_character_picker(array $chars, string $field, string $submitLabel, string $submitClass = 'btn btn-primary') {
96 znote_forum_style();
97 $only = (count($chars) === 1) ? reset($chars) : null;
98 ?>
99 <div class="znote-postas">
100 <span class="znote-postas-label"><?= t('forum.post_as') ?></span>
101
102 <?php if ($only !== null): ?>
103 <input type="hidden" name="<?php echo htmlspecialchars($field, ENT_QUOTES, 'UTF-8'); ?>" value="<?php echo (int)$only['id']; ?>">
104 <span class="znote-postas-single"><?php echo htmlspecialchars($only['name'], ENT_QUOTES, 'UTF-8'); ?></span>
105 <?php else: ?>
106 <select class="znote-postas-select form-control" name="<?php echo htmlspecialchars($field, ENT_QUOTES, 'UTF-8'); ?>">
107 <?php foreach ($chars as $char): ?>
108 <option value="<?php echo (int)$char['id']; ?>"><?php echo htmlspecialchars($char['name'], ENT_QUOTES, 'UTF-8'); ?></option>
109 <?php endforeach; ?>
110 </select>
111 <?php endif; ?>
112
113 <button type="submit" class="znote-postas-btn <?php echo htmlspecialchars($submitClass, ENT_QUOTES, 'UTF-8'); ?>">
114 <?php echo htmlspecialchars($submitLabel, ENT_QUOTES, 'UTF-8'); ?>
115 </button>
116 </div>
117 <?php
118}
119
120Function PlayerHaveAccess($yourChars, $playerName){
121 $access = false;
122 foreach($yourChars as $char) {
123 if ($char['name'] == $playerName) $access = true;
124 }
125 return $access;
126}
127
128// Start page init
129$admin = is_admin($user_data);
130if ($admin) $yourChars = db()->fetchAll("SELECT `id`, `name`, `group_id` FROM `players` WHERE `level` >= 1 AND `account_id` = ?;", [$user_data['id']]);
131else $yourChars = db()->fetchAll("SELECT `id`, `name`, `group_id` FROM `players` WHERE `level` >= ? AND `account_id` = ?;", [(int)$config['forum']['level'], $user_data['id']]);
132if (!$yourChars) $yourChars = array();
133$charCount = count($yourChars);
134$yourAccess = accountAccess($user_data['id'], znote_server_adapter()->normalizedEngine());
135if ($admin) {
136 if (!empty($_POST)) {
137 $guilds = db()->fetchAll("SELECT `id`, `name` FROM `guilds` ORDER BY `name`;");
138 $guilds[] = array('id' => '0', 'name' => 'No guild');
139 }
140 $yourAccess = 100;
141}
142
143// Your characters, indexed by char_id
144$charData = array();
145foreach ($yourChars as $char) {
146 $charData[$char['id']] = $char;
147 if (get_character_guild_rank($char['id']) > 0) {
148 $guild = get_player_guild_data($char['id']);
149 $charData[$char['id']]['guild'] = $guild['guild_id'];
150 $charData[$char['id']]['guild_rank'] = $guild['rank_level'];
151 } else $charData[$char['id']]['guild'] = '0';
152}
153$cooldownw = array(
154 $user_znote_data['cooldown'],
155 time(),
156 $user_znote_data['cooldown'] - time()
157 );
158
159/////////////////
160// Guild Leader & admin
161$leader = false;
162foreach($charData as $char) {
163 if ($char['guild'] > 0 && $char['guild_rank'] == 3) $leader = true;
164}
165if ($admin && !empty($_POST) || $leader && !empty($_POST)) {
166 $admin_thread_delete = getValue($_POST['admin_thread_delete'] ?? null);
167 $admin_thread_close = getValue($_POST['admin_thread_close'] ?? null);
168 $admin_thread_open = getValue($_POST['admin_thread_open'] ?? null);
169 $admin_thread_sticky = getValue($_POST['admin_thread_sticky'] ?? null);
170 $admin_thread_unstick = getValue($_POST['admin_thread_unstick'] ?? null);
171 $admin_thread_id = getValue($_POST['admin_thread_id'] ?? null);
172
173 // delete thread
174 if ($admin_thread_delete !== false) {
175 $admin_thread_id = (int)$admin_thread_id;
176 $access = false;
177 if (!$admin) {
178 $thread = db()->fetchOne("SELECT `forum_id` FROM `znote_forum_threads` WHERE `id` = ?;", [$admin_thread_id]);
179 $forum = db()->fetchOne("SELECT `guild_id` FROM `znote_forum` WHERE `id` = ?;", [$thread['forum_id']]);
180 foreach($charData as $char) if ($char['guild'] == $forum['guild_id'] && $char['guild_rank'] == 3) $access = true;
181 } else $access = true;
182
183 if ($access) {
184 // Delete all associated posts
185 db()->execute("DELETE FROM `znote_forum_posts` WHERE `thread_id` = ?;", [$admin_thread_id]);
186 // Delete thread itself
187 db()->execute("DELETE FROM `znote_forum_threads` WHERE `id` = ? LIMIT 1;", [$admin_thread_id]);
188 acp_log('forum.thread_delete', '#' . $admin_thread_id, ['scope' => $admin ? 'owner' : 'guild_leader']);
189 echo '<h1>'. t('forum.thread_deleted'). '</h1>';
190 } else echo '<p><b><font color="red">'. t('forum.perm_denied'). '</font></b></p>';
191 }
192
193 // Close thread
194 if ($admin_thread_close !== false) {
195 $admin_thread_id = (int)$admin_thread_id;
196 $access = false;
197 if (!$admin) {
198 $thread = db()->fetchOne("SELECT `forum_id` FROM `znote_forum_threads` WHERE `id` = ?;", [$admin_thread_id]);
199 $forum = db()->fetchOne("SELECT `guild_id` FROM `znote_forum` WHERE `id` = ?;", [$thread['forum_id']]);
200 foreach($charData as $char) if ($char['guild'] == $forum['guild_id'] && $char['guild_rank'] == 3) $access = true;
201 } else $access = true;
202 if ($access) {
203 db()->execute("UPDATE `znote_forum_threads` SET `closed` = 1 WHERE `id` = ? LIMIT 1;", [$admin_thread_id]);
204 acp_log('forum.thread_close', '#' . $admin_thread_id, ['scope' => $admin ? 'owner' : 'guild_leader']);
205 echo '<h1>'. t('forum.thread_closed'). '</h1>';
206 } else echo '<p><b><font color="red">'. t('forum.perm_denied'). '</font></b></p>';
207 }
208
209 // open thread
210 if ($admin_thread_open !== false) {
211 $admin_thread_id = (int)$admin_thread_id;
212 $access = false;
213 if (!$admin) {
214 $thread = db()->fetchOne("SELECT `forum_id` FROM `znote_forum_threads` WHERE `id` = ?;", [$admin_thread_id]);
215 $forum = db()->fetchOne("SELECT `guild_id` FROM `znote_forum` WHERE `id` = ?;", [$thread['forum_id']]);
216 foreach($charData as $char) if ($char['guild'] == $forum['guild_id'] && $char['guild_rank'] == 3) $access = true;
217 } else $access = true;
218 if ($access) {
219 db()->execute("UPDATE `znote_forum_threads` SET `closed` = 0 WHERE `id` = ? LIMIT 1;", [$admin_thread_id]);
220 acp_log('forum.thread_open', '#' . $admin_thread_id, ['scope' => $admin ? 'owner' : 'guild_leader']);
221 echo '<h1>'. t('forum.thread_opened'). '</h1>';
222 } else echo '<p><b><font color="red">'. t('forum.perm_denied2') .'</font></b></p>';
223 }
224
225 // stick thread
226 if ($admin_thread_sticky !== false) {
227 $admin_thread_id = (int)$admin_thread_id;
228 $access = false;
229 if (!$admin) {
230 $thread = db()->fetchOne("SELECT `forum_id` FROM `znote_forum_threads` WHERE `id` = ?;", [$admin_thread_id]);
231 $forum = db()->fetchOne("SELECT `guild_id` FROM `znote_forum` WHERE `id` = ?;", [$thread['forum_id']]);
232 foreach($charData as $char) if ($char['guild'] == $forum['guild_id'] && $char['guild_rank'] == 3) $access = true;
233 } else $access = true;
234 if ($access) {
235 db()->execute("UPDATE `znote_forum_threads` SET `sticky` = 1 WHERE `id` = ? LIMIT 1;", [$admin_thread_id]);
236 acp_log('forum.thread_sticky', '#' . $admin_thread_id, ['scope' => $admin ? 'owner' : 'guild_leader']);
237 echo '<h1>'. t('forum.thread_stuck2'). '</h1>';
238 } else echo '<p><b><font color="red">'. t('forum.perm_denied2') .'</font></b></p>';
239 }
240
241 // unstick thread
242 if ($admin_thread_unstick !== false) {
243 $admin_thread_id = (int)$admin_thread_id;
244 $access = false;
245 if (!$admin) {
246 $thread = db()->fetchOne("SELECT `forum_id` FROM `znote_forum_threads` WHERE `id` = ?;", [$admin_thread_id]);
247 $forum = db()->fetchOne("SELECT `guild_id` FROM `znote_forum` WHERE `id` = ?;", [$thread['forum_id']]);
248 foreach($charData as $char) if ($char['guild'] == $forum['guild_id'] && $char['guild_rank'] == 3) $access = true;
249 } else $access = true;
250 if ($access) {
251 db()->execute("UPDATE `znote_forum_threads` SET `sticky` = 0 WHERE `id` = ? LIMIT 1;", [$admin_thread_id]);
252 acp_log('forum.thread_unsticky', '#' . $admin_thread_id, ['scope' => $admin ? 'owner' : 'guild_leader']);
253 echo '<h1>'. t('forum.thread_unstuck2'). '</h1>';
254 } else echo '<p><b><font color="red">'. t('forum.perm_denied2') .'</font></b></p>';
255 }
256}
257
258/////////////////
259// ADMIN FUNCT
260if ($admin && !empty($_POST)) {
261 $admin_post_id = getValue($_POST['admin_post_id'] ?? null);
262 $admin_post_delete = getValue($_POST['admin_post_delete'] ?? null);
263
264 $admin_category_delete = getValue($_POST['admin_category_delete'] ?? null);
265 $admin_category_edit = getValue($_POST['admin_category_edit'] ?? null);
266 $admin_category_id = getValue($_POST['admin_category_id'] ?? null);
267
268 $admin_update_category = getValue($_POST['admin_update_category'] ?? null);
269 $admin_category_name = getValue($_POST['admin_category_name'] ?? null);
270
271 $admin_category_access = getValue($_POST['admin_category_access'] ?? null);
272 $admin_category_closed = getValue($_POST['admin_category_closed'] ?? null);
273 $admin_category_hidden = getValue($_POST['admin_category_hidden'] ?? null);
274 $admin_category_guild_id = getValue($_POST['admin_category_guild_id'] ?? null);
275
276 if ($admin_category_access === false) $admin_category_access = 0;
277 if ($admin_category_closed === false) $admin_category_closed = 0;
278 if ($admin_category_hidden === false) $admin_category_hidden = 0;
279 if ($admin_category_guild_id === false) $admin_category_guild_id = 0;
280
281 $admin_board_create_name = getValue($_POST['admin_board_create_name'] ?? null);
282 $admin_board_create_access = getValue($_POST['admin_board_create_access'] ?? null);
283 $admin_board_create_closed = getValue($_POST['admin_board_create_closed'] ?? null);
284 $admin_board_create_hidden = getValue($_POST['admin_board_create_hidden'] ?? null);
285 $admin_board_create_guild_id = getValue($_POST['admin_board_create_guild_id'] ?? null);
286
287 if ($admin_board_create_access === false) $admin_board_create_access = 0;
288 if ($admin_board_create_closed === false) $admin_board_create_closed = 0;
289 if ($admin_board_create_hidden === false) $admin_board_create_hidden = 0;
290 if ($admin_board_create_guild_id === false) $admin_board_create_guild_id = 0;
291
292 // Create board
293 if ($admin_board_create_name !== false) {
294
295 // Insert data
296 db()->execute("INSERT INTO `znote_forum` (`name`, `access`, `closed`, `hidden`, `guild_id`)
297 VALUES (?, ?, ?, ?, ?);", [
298 $admin_board_create_name,
299 $admin_board_create_access,
300 $admin_board_create_closed,
301 $admin_board_create_hidden,
302 $admin_board_create_guild_id,
303 ]);
304 acp_log('forum.board_create', (string)$admin_board_create_name, ['access' => $admin_board_create_access, 'guild_id' => $admin_board_create_guild_id]);
305 echo '<h1>'. t('forum.board_created'). '</h1>';
306 }
307
308 //////////////////
309 // update category
310 if ($admin_update_category !== false) {
311 $admin_category_id = (int)$admin_category_id;
312
313 // Update the category
314 db()->execute("UPDATE `znote_forum` SET
315 `name` = ?,
316 `access` = ?,
317 `closed` = ?,
318 `hidden` = ?,
319 `guild_id` = ?
320 WHERE `id` = ? LIMIT 1;", [
321 $admin_category_name,
322 $admin_category_access,
323 $admin_category_closed,
324 $admin_category_hidden,
325 $admin_category_guild_id,
326 $admin_category_id,
327 ]);
328 acp_log('forum.board_update', '#' . $admin_category_id, ['access' => $admin_category_access, 'guild_id' => $admin_category_guild_id]);
329 echo '<h1>'. t('forum.board_updated'). '</h1>';
330 }
331
332 //////////////////
333 // edit category
334 if ($admin_category_edit !== false) {
335 $admin_category_id = (int)$admin_category_id;
336 $category = db()->fetchOne("SELECT `id`, `name`, `access`, `closed`, `hidden`, `guild_id`
337 FROM `znote_forum` WHERE `id` = ? LIMIT 1;", [$admin_category_id]);
338 if ($category !== false) {
339 ?>
340 <form action="" method="post">
341 <input type="hidden" name="admin_category_id" value="<?php echo $category['id']; ?>">
342 <table class="updateTable table table-striped">
343 <tr>
344 <td><label for="admin_category_name"><?= t('forum.board_name') ?></label></td>
345 <td><input name="admin_category_name" value="<?php echo $category['name']; ?>" class="span12"></td>
346
347 </tr>
348 <tr>
349 <td><label for="admin_category_access"><?= t('forum.required_access') ?></label></td>
350 <td>
351 <select name="admin_category_access" class="span12">
352 <?php
353 foreach($config['ingame_positions'] as $access => $name) {
354 if ($access == $category['access']) echo "<option value='$access' selected>$name</option>";
355 else echo "<option value='$access'>$name</option>";
356 }
357 ?>
358 </select>
359 </td>
360 </tr>
361 <tr>
362 <td><label for="admin_category_closed"><?= t('forum.closed') ?></label></td>
363 <td>
364 <select name="admin_category_closed" class="span12">
365 <?php
366 if ($category['closed'] == 1) echo '<option value="1" selected>' . t('common.yes') . '</option>';
367 else echo '<option value="1">' . t('common.yes') . '</option>';
368 if ($category['closed'] == 0) echo '<option value="0" selected>' . t('common.no') . '</option>';
369 else echo '<option value="0">' . t('common.no') . '</option>';
370 ?>
371 </select>
372 </td>
373 </tr>
374 <tr>
375 <td><label for="admin_category_hidden"><?= t('forum.hidden') ?></label></td>
376 <td>
377 <select name="admin_category_hidden" class="span12">
378 <?php
379 if ($category['hidden'] == 1) echo '<option value="1" selected>' . t('common.yes') . '</option>';
380 else echo '<option value="1">' . t('common.yes') . '</option>';
381 if ($category['hidden'] == 0) echo '<option value="0" selected>' . t('common.no') . '</option>';
382 else echo '<option value="0">' . t('common.no') . '</option>';
383 ?>
384 </select>
385 </td>
386 </tr>
387 <tr>
388 <td><label for="admin_category_guild_id"><?= t('forum.guild_id') ?></label></td>
389 <td>
390 <select name="admin_category_guild_id" class="span12">
391 <?php foreach($guilds as $guild) {
392 if ($category['guild_id'] == $guild['id']) echo "<option value='". $guild['id'] ."' selected>". $guild['name'] ."</option>";
393 else echo "<option value='". $guild['id'] ."'>". $guild['name'] ."</option>";
394 } ?>
395 </select>
396 </td>
397 </tr>
398 <tr>
399 <td colspan="2"><input type="submit" name="admin_update_category" value="<?= t('forum.update_board') ?>" style="width: 100%; height: 30px;" class="btn btn-success"></td>
400 </tr>
401 </table>
402 </form>
403 <?php
404 } else echo '<h2>'. t('forum.cat_not_found'). '</h2>';
405
406 }
407
408 // delete category
409 if ($admin_category_delete !== false) {
410 $admin_category_id = (int)$admin_category_id;
411
412 // find all threads in category
413 $threads = db()->fetchAll("SELECT `id` FROM `znote_forum_threads` WHERE `forum_id` = ?;", [$admin_category_id]);
414
415 // Then loop through all threads, and delete all associated posts:
416 foreach($threads as $thread) {
417 db()->execute("DELETE FROM `znote_forum_posts` WHERE `thread_id` = ?;", [$thread['id']]);
418 }
419 // Then delete all threads
420 db()->execute("DELETE FROM `znote_forum_threads` WHERE `forum_id` = ?;", [$admin_category_id]);
421 // Then delete the category
422 db()->execute("DELETE FROM `znote_forum` WHERE `id` = ? LIMIT 1;", [$admin_category_id]);
423 acp_log('forum.board_delete', '#' . $admin_category_id);
424 echo '<h1>' . t('forum.board_and_threads_deleted') . '</h1>';
425 }
426
427 // delete post
428 if ($admin_post_delete !== false) {
429 $admin_post_id = (int)$admin_post_id;
430
431 // Delete the post
432 db()->execute("DELETE FROM `znote_forum_posts` WHERE `id` = ? LIMIT 1;", [$admin_post_id]);
433 acp_log('forum.post_delete', '#' . $admin_post_id);
434 echo '<h1>'. t('forum.post_deleted'). '</h1>';
435 }
436}
437// End admin function
438
439// Fetching get values
440if (!empty($_GET)) {
441 $getCat = getValue($_GET['cat'] ?? null);
442 $getForum = getValue($_GET['forum'] ?? null);
443 $getThread = getValue($_GET['thread'] ?? null);
444
445 $new_thread_category = getValue($_POST['new_thread_category'] ?? null);
446 $new_thread_cid = getValue($_POST['new_thread_cid'] ?? null);
447
448 $create_thread_cid = getValue($_POST['create_thread_cid'] ?? null);
449 $create_thread_title = getValue($_POST['create_thread_title'] ?? null);
450 $create_thread_text = getValue($_POST['create_thread_text'] ?? null);
451 $create_thread_category = getValue($_POST['create_thread_category'] ?? null);
452
453 $update_thread_id = getValue($_POST['update_thread_id'] ?? null);
454 $update_thread_title = getValue($_POST['update_thread_title'] ?? null);
455 $update_thread_text = getValue($_POST['update_thread_text'] ?? null);
456
457 $edit_thread = getValue($_POST['edit_thread'] ?? null);
458 $edit_thread_id = getValue($_POST['edit_thread_id'] ?? null);
459
460 $reply_thread = getValue($_POST['reply_thread'] ?? null);
461 $reply_text = getValue($_POST['reply_text'] ?? null);
462 $reply_cid = getValue($_POST['reply_cid'] ?? null);
463
464 $edit_post = getValue($_POST['edit_post'] ?? null);
465 $edit_post_id = getValue($_POST['edit_post_id'] ?? null);
466
467 $update_post_id = getValue($_POST['update_post_id'] ?? null);
468 $update_post_text = getValue($_POST['update_post_text'] ?? null);
469
470 // Image cap. The editor checks this too, but that check is a convenience -
471 // this one is the rule, because a POST does not have to come from the editor.
472 // Blanking the field makes the save branches below skip, since each requires
473 // its text to be !== false.
474 $forumImageLimit = (int)($config['forum']['maxImagesPerPost'] ?? 1);
475 $forumImageDenied = false;
476 foreach (array('create_thread_text', 'update_thread_text', 'reply_text', 'update_post_text') as $forumField) {
477 if ($$forumField !== false && znote_bbcode_count_images($$forumField) > $forumImageLimit) {
478 $forumImageDenied = true;
479 $$forumField = false;
480 }
481 }
482 if ($forumImageDenied) {
483 echo '<p><b><font color="red">'
484 . ($forumImageLimit > 0
485 ? 'Too many images: only ' . $forumImageLimit . ' allowed per post. Nothing was saved.'
486 : 'Images are not allowed in forum posts. Nothing was saved.')
487 . '</font></b></p>';
488 }
489
490 /////////////////////
491 // When you are POSTING in an existing thread
492 if ($reply_thread !== false && $reply_text !== false && $reply_cid !== false) {
493 $reply_cid = (int)$reply_cid;
494
495 if ($user_znote_data['cooldown'] < time()) {
496 user_update_znote_account(array('cooldown'=>(time() + $config['forum']['cooldownPost'])));
497
498 $thread = db()->fetchOne("SELECT `closed` FROM `znote_forum_threads` WHERE `id` = ? LIMIT 1;", [$reply_thread]);
499
500 if (!is_array($thread) || !isset($charData[$reply_cid])) $access = false;
501 else if ($thread['closed'] == 1 && $admin === false) $access = false;
502 else $access = true;
503
504 if ($access) {
505 db()->execute("INSERT INTO `znote_forum_posts` (`thread_id`, `player_id`, `player_name`, `text`, `created`, `updated`) VALUES (?, ?, ?, ?, ?, ?);", [$reply_thread, $reply_cid, $charData[$reply_cid]['name'], $reply_text, time(), time()]);
506 if ($config['forum']['newPostsBumpThreads']) db()->execute("UPDATE `znote_forum_threads` SET `updated` = ? WHERE `id` = ?;", [time(), $reply_thread]);
507 } else echo '<p><b><font color="red">You don\'t have permission to post on this thread. [Thread: Closed]</font></b></p>';
508 } else {
509 ?>
510 <p class="forumCooldown"><?= t('forum.antispam') ?> <?php echo ($user_znote_data['cooldown'] - time()); ?> seconds before you can create or post.</p>
511 <?php
512 }
513 }
514
515 /////////////////////
516 // When you ARE creating new thread
517 if ($create_thread_cid !== false && $create_thread_title !== false && $create_thread_text !== false && $create_thread_category !== false) {
518 if ($user_znote_data['cooldown'] < time()) {
519 user_update_znote_account(array('cooldown'=>(time() + $config['forum']['cooldownCreate'])));
520
521 $category = db()->fetchOne("SELECT `access`, `closed`, `guild_id` FROM `znote_forum` WHERE `id` = ? LIMIT 1;", [$create_thread_category]);
522 if ($category !== false) {
523 $access = true;
524 if (!$admin) {
525 if ($category['access'] > $yourAccess) $access = false;
526 if ($category['guild_id'] > 0) {
527 $status = false;
528 foreach($charData as $char) {
529 if ($char['guild'] == $category['guild_id']) $status = true;
530 }
531 if (!$status) $access = false;
532 }
533 if ($category['closed'] > 0) $access = false;
534 }
535
536 if ($access) {
537 db()->execute("INSERT INTO `znote_forum_threads`
538 (`forum_id`, `player_id`, `player_name`, `title`, `text`, `created`, `updated`, `sticky`, `hidden`, `closed`)
539 VALUES (?, ?, ?, ?, ?, ?, ?, 0, 0, 0);", [
540 $create_thread_category,
541 $create_thread_cid,
542 $charData[$create_thread_cid]['name'],
543 $create_thread_title,
544 $create_thread_text,
545 time(),
546 time(),
547 ]);
548 SendGet(array('cat'=>$create_thread_category), 'forum.php');
549 } else echo '<p><b><font color="red">'. t('forum.perm_thread'). '</font></b></p>';
550 } else echo t('forum.cat_missing');
551 } else {
552 ?>
553 <p class="forumCooldown"><?= t('forum.antispam') ?> <?php echo ($user_znote_data['cooldown'] - time()); ?> seconds before you can create or post.</p>
554 <?php
555 }
556 }
557
558 /////////////////////
559 // When you ARE updating post
560 if ($update_post_id !== false && $update_post_text !== false) {
561 // Fetch the post data
562 $post = db()->fetchOne("SELECT `id`, `player_name`, `text`, `thread_id` FROM `znote_forum_posts` WHERE `id` = ? LIMIT 1;", [$update_post_id]);
563 if (!is_array($post)) $post = array('id' => 0, 'player_name' => '', 'text' => '', 'thread_id' => 0);
564 $thread = db()->fetchOne("SELECT `closed` FROM `znote_forum_threads` WHERE `id` = ? LIMIT 1;", [$post['thread_id']]);
565
566 // Verify access
567 $access = PlayerHaveAccess($yourChars, $post['player_name']);
568 if ($thread !== false && $thread['closed'] == 1 && $admin === false) $access = false;
569 if ($admin) $access = true;
570 //if ($thread === false) $access = false;
571
572 if ($access) {
573 db()->execute("UPDATE `znote_forum_posts` SET `text` = ?, `updated` = ? WHERE `id` = ?;", [$update_post_text, time(), $update_post_id]);
574 if ($admin) acp_log('forum.post_update', '#' . (int)$update_post_id);
575 echo '<h1>' . t('forum.post_updated') . '</h1>';
576 } else echo "<p class='znf-alert'>" . t('forum.edit_post_denied') . "</p>";
577 }
578
579 /////////////////////
580 // When you ARE updating thread
581 if ($update_thread_id !== false && $update_thread_title !== false && $update_thread_text !== false) {
582 // Fetch the thread data
583 $thread = db()->fetchOne("SELECT `id`, `player_name`, `title`, `text`, `closed` FROM `znote_forum_threads` WHERE `id` = ? LIMIT 1;", [$update_thread_id]);
584 if (!is_array($thread)) $thread = array('id' => 0, 'player_name' => '', 'title' => '', 'text' => '', 'closed' => 0);
585
586 // Verify access
587 $access = PlayerHaveAccess($yourChars, $thread['player_name']);
588 if ($thread['closed'] == 1 && $admin === false) $access = false;
589 if ($admin) $access = true;
590
591 if ($access) {
592 db()->execute("UPDATE `znote_forum_threads` SET `title` = ?, `text` = ? WHERE `id` = ?;", [$update_thread_title, $update_thread_text, $update_thread_id]);
593 if ($admin) acp_log('forum.thread_update', '#' . (int)$update_thread_id, ['title' => (string)$update_thread_title]);
594 echo '<h1>'. t('forum.thread_updated'). '</h1>';
595 } else echo "<p class='znf-alert'>" . t('forum.edit_thread_denied') . "</p>";
596 }
597
598 /////////////////////
599 // When you want to edit a post
600 if ($edit_post_id !== false && $edit_post !== false) {
601 // Fetch the post data
602 $post = db()->fetchOne("SELECT `id`, `thread_id`, `text`, `player_name` FROM `znote_forum_posts` WHERE `id` = ? LIMIT 1;", [$edit_post_id]);
603 if (!is_array($post)) $post = array('id' => 0, 'thread_id' => 0, 'text' => '', 'player_name' => '');
604 $thread = db()->fetchOne("SELECT `closed` FROM `znote_forum_threads` WHERE `id` = ? LIMIT 1;", [$post['thread_id']]);
605 if (!is_array($thread)) $thread = array('closed' => 0);
606 // Verify access
607 $access = PlayerHaveAccess($yourChars, $post['player_name']);
608 if ($thread['closed'] == 1 && $admin === false) $access = false;
609 if ($admin) $access = true;
610
611 if ($access) {
612 ?>
613 <h1><?= t('forum.edit_post') ?></h1>
614 <form type="" method="post">
615 <input name="update_post_id" type="hidden" value="<?php echo $post['id']; ?>">
616 <?php znote_forum_editor('update_post_text', $post['text'], 300); ?>
617 <input type="submit" value="<?= t('forum.update_post') ?>" class="btn btn-success">
618 </form>
619 <?php
620 } else echo '<p><b><font color="red">'. t('forum.no_edit_post') .'</font></b></p>';
621 } else
622
623 /////////////////////
624 // When you want to edit a thread
625 if ($edit_thread_id !== false && $edit_thread !== false) {
626 // Fetch the thread data
627 $thread = db()->fetchOne("SELECT `id`, `title`, `text`, `player_name`, `closed` FROM `znote_forum_threads` WHERE `id` = ? LIMIT 1;", [$edit_thread_id]);
628
629 $access = PlayerHaveAccess($yourChars, $thread['player_name']);
630 if ($thread['closed'] == 1) $access = false;
631 if ($admin) $access = true;
632
633 if ($access) {
634 ?>
635 <h1><?= t('forum.edit_thread') ?></h1>
636 <form type="" method="post">
637 <input name="update_thread_id" type="hidden" value="<?php echo $thread['id']; ?>">
638 <input name="update_thread_title" type="text" value="<?php echo $thread['title']; ?>" style="width: 500px;"><br><br>
639 <?php znote_forum_editor('update_thread_text', $thread['text'], 300); ?>
640 <input type="submit" value="<?= t('forum.update_thread') ?>" class="btn btn-success">
641 </form>
642 <?php
643 } else echo '<p><b><font color="red">'. t('forum.edit_denied'). '</font></b></p>';
644 } else
645
646 /////////////////////
647 // When you want to view a thread
648 if ($getThread !== false) {
649 $getThread = (int)$getThread;
650 $threadData = db()->fetchOne("SELECT `id`, `forum_id`, `player_id`, `player_name`, `title`, `text`, `created`, `updated`, `sticky`, `hidden`, `closed` FROM `znote_forum_threads` WHERE `id` = ? LIMIT 1;", [$getThread]);
651
652 if ($threadData !== false) {
653
654 $category = db()->fetchOne("SELECT `hidden`, `access`, `guild_id` FROM `znote_forum` WHERE `id` = ? LIMIT 1;", [$threadData['forum_id']]);
655 if ($category === false) die("Thread category does not exist.");
656
657 $access = true;
658 $leader = false;
659 if ($category['hidden'] == 1 || $category['access'] > 1 || $category['guild_id'] > 0) {
660 $access = false;
661 if ($category['hidden'] == 1) $access = PlayerHaveAccess($yourChars, $threadData['player_name']);
662 if ($category['access'] > 1 && $yourAccess >= $category['access']) $access = true;
663 foreach($charData as $char) {
664 if ($category['guild_id'] == $char['guild']) $access = true;
665 if ($char['guild_rank'] == 3) $leader = true;
666 }
667 if ($admin) $access = true;
668 }
669
670
671 if ($access) {
672 $threadPlayer = ($config['forum']['outfit_avatars'] || $config['forum']['player_position']) ? db()->fetchOne("SELECT `id`, `group_id`, `sex`, `lookbody`, `lookfeet`, `lookhead`, `looklegs`, `looktype`, `lookaddons` FROM `players` WHERE `id` = ?;", [$threadData['player_id']]) : false;
673 ?>
674 <nav class="znf-crumbs"><a href="forum.php"><?= t('forum.title') ?></a> <span>/</span> <a href="?cat=<?php echo $getCat; ?>"><?php echo $getForum; ?></a></nav>
675 <h1 id="ThreadTitle" class="znf-title"><?php echo "<a href='?forum=". $getForum ."&cat=". $getCat ."&thread=". $threadData['id'] ."'>". $threadData['title'] ."</a>"; ?></h1>
676 <article class="znf-post is-op">
677 <div class="znf-post__side avatar">
678 <a class="znf-post__author" href="characterprofile.php?name=<?php echo $threadData['player_name']; ?>"><?php echo $threadData['player_name']; ?></a>
679 <?php if ($threadPlayer !== false && $config['forum']['outfit_avatars']): ?>
680 <img class="znf-post__outfit" src="<?php echo $config['show_outfits']['imageServer']; ?>?id=<?php echo $threadPlayer['looktype']; ?>&addons=<?php echo $threadPlayer['lookaddons']; ?>&head=<?php echo $threadPlayer['lookhead']; ?>&body=<?php echo $threadPlayer['lookbody']; ?>&legs=<?php echo $threadPlayer['looklegs']; ?>&feet=<?php echo $threadPlayer['lookfeet']; ?>" alt="img">
681 <?php endif; ?>
682 <?php if ($threadPlayer !== false && $config['forum']['player_position']): ?>
683 <span class="znf-post__rank"><?php echo group_id_to_name($threadPlayer['group_id']); ?></span>
684 <?php endif; ?>
685 </div>
686 <div class="znf-post__body">
687 <div class="znf-post__meta"><span class="znf-tag znf-tag--sticky"><?= t('forum.op_tag') ?></span> <?php echo getClock($threadData['created'], true); ?></div>
688 <div class="znf-post__text"><?php echo znote_bbcode($threadData['text']); ?></div>
689 <?php if ($charCount > 0 && ($threadData['closed'] == 0 || $yourAccess > 3)): ?>
690 <div class="znf-post__actions">
691 <button type="button" class="btn btn-info znf-quote-btn" data-author="<?php echo htmlspecialchars($threadData['player_name'], ENT_QUOTES, 'UTF-8'); ?>"><?= t('forum.quote') ?></button>
692 </div>
693 <?php endif; ?>
694 <template class="znf-quote-source"><?php echo htmlspecialchars($threadData['text'], ENT_QUOTES, 'UTF-8'); ?></template>
695 </div>
696 </article>
697 <?php
698 if ($admin || $leader) {
699 ?>
700 <div class="znf-mod">
701 <form action="" method="post">
702 <input type="hidden" name="admin_thread_id" value="<?php echo $threadData['id']; ?>">
703 <input type="submit" name="admin_thread_delete" value="<?= t('forum.delete_thread') ?>" class="btn btn-danger">
704 </form>
705 <?php if ($threadData['closed'] == 0) { ?>
706 <form action="" method="post">
707 <input type="hidden" name="admin_thread_id" value="<?php echo $threadData['id']; ?>">
708 <input type="submit" name="admin_thread_close" value="<?= t('forum.close_thread') ?>" class="btn btn-warning">
709 </form>
710 <?php } else { ?>
711 <form action="" method="post">
712 <input type="hidden" name="admin_thread_id" value="<?php echo $threadData['id']; ?>">
713 <input type="submit" name="admin_thread_open" value="<?= t('forum.open_thread') ?>" class="btn btn-success">
714 </form>
715 <?php } ?>
716 <?php if ($threadData['sticky'] == 0) { ?>
717 <form action="" method="post">
718 <input type="hidden" name="admin_thread_id" value="<?php echo $threadData['id']; ?>">
719 <input type="submit" name="admin_thread_sticky" value="<?= t('forum.stick') ?>" class="btn btn-info">
720 </form>
721 <?php } else { ?>
722 <form action="" method="post">
723 <input type="hidden" name="admin_thread_id" value="<?php echo $threadData['id']; ?>">
724 <input type="submit" name="admin_thread_unstick" value="<?= t('forum.unstick') ?>" class="btn btn-primary">
725 </form>
726 <?php } ?>
727 <form action="" method="post">
728 <input type="hidden" name="edit_thread_id" value="<?php echo $threadData['id']; ?>">
729 <input type="submit" name="edit_thread" value="<?= t('forum.edit_thread') ?>" class="btn btn-warning">
730 </form>
731 </div>
732 <?php
733 } else {
734 if ($threadData['closed'] == 0 && PlayerHaveAccess($yourChars, $threadData['player_name'])) {
735 ?>
736 <div class="znf-mod">
737 <form action="" method="post">
738 <input type="hidden" name="edit_thread_id" value="<?php echo $threadData['id']; ?>">
739 <input type="submit" name="edit_thread" value="<?= t('forum.edit_thread') ?>" class="btn btn-info">
740 </form>
741 </div>
742 <?php
743 }
744 }
745 ?>
746 <?php
747 // Display replies... (copy table above and edit each post)
748 $posts = db()->fetchAll("SELECT `id`, `player_id`, `player_name`, `text`, `created`, `updated` FROM `znote_forum_posts` WHERE `thread_id` = ? ORDER BY `created`;", [$threadData['id']]);
749 if ($posts !== false) {
750 // Load extra data (like outfit avatars?)
751 $players = array();
752 $extra = false;
753 if ($config['forum']['outfit_avatars'] || $config['forum']['player_position']) {
754 $extra = true;
755
756 foreach($posts as $post)
757 if (!isset($players[$post['player_id']]))
758 $players[$post['player_id']] = array();
759
760 $playerIds = array_keys($players);
761 $playerIdPlaceholders = implode(',', array_fill(0, count($playerIds), '?'));
762 $sql_players = db()->fetchAll("SELECT `id`, `group_id`, `sex`, `lookbody`, `lookfeet`, `lookhead`, `looklegs`, `looktype`, `lookaddons` FROM `players` WHERE `id` IN ({$playerIdPlaceholders});", $playerIds);
763
764 foreach ($sql_players as $player)
765 $players[$player['id']] = $player;
766
767 }
768
769 foreach($posts as $post) {
770 ?>
771 <article class="znf-post<?php echo ($post['player_name'] === $threadData['player_name']) ? ' is-op' : ''; ?>">
772 <div class="znf-post__side avatar">
773 <a class="znf-post__author" href="characterprofile.php?name=<?php echo $post['player_name']; ?>"><?php echo $post['player_name']; ?></a>
774 <?php if ($extra && $config['forum']['outfit_avatars']): ?>
775 <img class="znf-post__outfit" src="<?php echo $config['show_outfits']['imageServer']; ?>?id=<?php echo $players[$post['player_id']]['looktype']; ?>&addons=<?php echo $players[$post['player_id']]['lookaddons']; ?>&head=<?php echo $players[$post['player_id']]['lookhead']; ?>&body=<?php echo $players[$post['player_id']]['lookbody']; ?>&legs=<?php echo $players[$post['player_id']]['looklegs']; ?>&feet=<?php echo $players[$post['player_id']]['lookfeet']; ?>" alt="img">
776 <?php endif; ?>
777 <?php if ($extra && $config['forum']['player_position']): ?>
778 <span class="znf-post__rank"><?php echo group_id_to_name($players[$post['player_id']]['group_id']); ?></span>
779 <?php endif; ?>
780 </div>
781 <div class="znf-post__body">
782 <div class="znf-post__meta"><?php echo getClock($post['created'], true); ?></div>
783 <div class="znf-post__text"><?php echo znote_bbcode($post['text']); ?></div>
784 <div class="znf-post__actions">
785 <?php
786 if ($charCount > 0 && ($threadData['closed'] == 0 || $yourAccess > 3)) {
787 ?>
788 <button type="button" class="btn btn-info znf-quote-btn" data-author="<?php echo htmlspecialchars($post['player_name'], ENT_QUOTES, 'UTF-8'); ?>"><?= t('forum.quote') ?></button>
789 <?php
790 }
791 if (PlayerHaveAccess($yourChars, $post['player_name']) || $admin) {
792 if ($admin) {
793 ?>
794 <form action="" method="post" class="postButton">
795 <input type="hidden" name="admin_post_id" value="<?php echo $post['id']; ?>">
796 <input type="submit" name="admin_post_delete" value="<?= t('forum.delete_post') ?>" class="btn btn-danger">
797 </form>
798 <?php
799 }
800 if ($threadData['closed'] == 0 || $admin) {
801 ?>
802 <form action="" method="post" class="postButton">
803 <input type="hidden" name="edit_post_id" value="<?php echo $post['id']; ?>">
804 <input type="submit" name="edit_post" value="<?= t('forum.edit_post') ?>" class="btn btn-info">
805 </form>
806 <?php
807 }
808 }
809 ?>
810 </div>
811 <template class="znf-quote-source"><?php echo htmlspecialchars($post['text'], ENT_QUOTES, 'UTF-8'); ?></template>
812 </div>
813 </article>
814 <?php
815 }
816 }
817
818 // Quick Reply
819 if ($charCount > 0) {
820 if ($threadData['closed'] == 0 || $yourAccess > 3) {
821 ?>
822 <div class="znf-reply">
823 <h3 class="znf-reply__title"><?= t('forum.reply_title') ?></h3>
824 <form action="" method="post">
825 <input name="reply_thread" type="hidden" value="<?php echo $threadData['id']; ?>">
826 <?php znote_forum_editor('reply_text', '', 200); ?>
827 <?php znote_forum_character_picker($yourChars, 'reply_cid', t('forum.reply_btn'), 'btn btn-primary'); ?>
828 </form>
829 </div>
830 <?php
831 } else echo '<p class="znf-note">' . t('forum.thread_closed_note') . '</p>';
832 } else {
833 ?><p class="znf-note"><?= t('forum.level_required', ['level' => (int)$config['forum']['level']]) ?></p><?php
834 }
835 } else echo '<p class="znf-alert">' . t('forum.perm_denied3') . '</p>';
836 } else {
837 ?>
838 <h1><?= t('forum.thread_unavailable') ?></h1>
839 <p><?= t('forum.thread_unavailable_text') ?></p>
840 <p>
841 <?php
842 if ($_GET['cat'] > 0 && !empty($_GET['forum'])) {
843 $tmpCat = getValue($_GET['cat'] ?? null);
844 $tmpCatName = getValue($_GET['forum'] ?? null);
845 ?>
846 <br><a href="forum.php?forum=<?php echo $tmpCatName; ?>&cat=<?php echo $tmpCat; ?>"><?= t('forum.go_back_to') ?> <?php echo $tmpCatName; ?></a></p>
847 <?php
848 } else {
849 ?>
850 <br><a href="forum.php"><?= t('forum.go_back') ?></a></p>
851 <?php
852 }
853 ?>
854 <?php
855 }
856
857 } else
858
859 /////////////////////
860 // When you want to create a new thread
861 if ($new_thread_category !== false && $new_thread_cid !== false) {
862 // Verify we got access to this category
863 $category = db()->fetchOne("SELECT `access`, `closed`, `guild_id` FROM `znote_forum` WHERE `id` = ? LIMIT 1;", [$new_thread_category]);
864 if ($category !== false) {
865 $access = true;
866 if (!$admin) {
867 if ($category['access'] > $yourAccess) $access = false;
868 if ($category['guild_id'] > 0) {
869 $status = false;
870 foreach($charData as $char) {
871 if ($char['guild'] == $category['guild_id']) $status = true;
872 }
873 if (!$status) $access = false;
874 }
875 if ($category['closed'] > 0) $access = false;
876 }
877
878 if ($access) {
879 ?>
880 <h1><?= t('forum.create_thread_new') ?></h1>
881 <form type="" method="post" class="znote-newthread">
882 <input name="create_thread_cid" type="hidden" value="<?php echo $new_thread_cid; ?>">
883 <input name="create_thread_category" type="hidden" value="<?php echo $new_thread_category; ?>">
884
885 <div class="znote-newthread-head">
886 <span class="znote-postas-label"><?= t('forum.post_as') ?></span>
887 <span class="znote-postas-single"><?php echo htmlspecialchars($charData[$new_thread_cid]['name'], ENT_QUOTES, 'UTF-8'); ?></span>
888 </div>
889
890 <input class="znote-newthread-title form-control" name="create_thread_title" type="text" placeholder="<?= t('forum.thread_title_placeholder') ?>" maxlength="60" required>
891
892 <?php znote_forum_editor('create_thread_text', '', 300); ?>
893
894 <div class="znote-newthread-actions">
895 <button type="submit" class="btn btn-success"><?= t('forum.create_thread') ?></button>
896 <a class="btn btn-default" href="forum.php?cat=<?php echo (int)$new_thread_category; ?>">Cancel</a>
897 </div>
898 </form>
899 <?php
900 } else echo '<p><b><font color="red">'. t('forum.perm_thread'). '</font></b></p>';
901 }
902 } else
903
904 /////////////////////
905 // When category is specified
906 if ($getCat !== false) {
907 $getCat = (int)$getCat;
908
909 // Fetch category rules
910 $category = db()->fetchOne("SELECT `name`, `access`, `closed`, `hidden`, `guild_id` FROM `znote_forum` WHERE `id` = ? AND `access` <= ? LIMIT 1;", [$getCat, $yourAccess]);
911
912 if ($category !== false && $category['guild_id'] > 0 && !$admin) {
913 $access = false;
914 foreach($charData as $char) if ($category['guild_id'] == $char['guild']) $access = true;
915 if ($access !== true) $category = false;
916 }
917
918 if ($category !== false) {
919 $getCatInt = (int)$getCat;
920 echo '<nav class="znf-crumbs"><a href="forum.php">' . t('forum.boards') . '</a> <span>/</span> ' . znote_forum_e($category['name']) . '</nav>';
921 echo '<div class="znf-bhead"><h1 class="znf-h1">' . znote_forum_e($category['name']) . '</h1></div>';
922
923 $threads = db()->fetchAll(
924 "SELECT `t`.`id`, `t`.`player_name`, `t`.`title`, `t`.`sticky`, `t`.`closed`, `t`.`created`, `t`.`updated`, "
925 . "COUNT(`p`.`id`) AS `reply_count`, COALESCE(MAX(`p`.`created`), 0) AS `last_reply_at` "
926 . "FROM `znote_forum_threads` `t` LEFT JOIN `znote_forum_posts` `p` ON `p`.`thread_id` = `t`.`id` "
927 . "WHERE `t`.`forum_id` = ? "
928 . "GROUP BY `t`.`id`, `t`.`player_name`, `t`.`title`, `t`.`sticky`, `t`.`closed`, `t`.`created`, `t`.`updated` "
929 . "ORDER BY `t`.`sticky` DESC, `t`.`updated` DESC;",
930 [$getCatInt]
931 );
932
933 $lastReplyBy = array();
934 $lrRows = db()->fetchAll(
935 "SELECT `x`.`thread_id`, `x`.`player_name` FROM `znote_forum_posts` `x` JOIN ("
936 . "SELECT `p2`.`thread_id`, MAX(`p2`.`created`) AS `mc` FROM `znote_forum_posts` `p2` "
937 . "JOIN `znote_forum_threads` `t2` ON `t2`.`id` = `p2`.`thread_id` WHERE `t2`.`forum_id` = ? "
938 . "GROUP BY `p2`.`thread_id`) `y` ON `y`.`thread_id` = `x`.`thread_id` AND `y`.`mc` = `x`.`created`;",
939 [$getCatInt]
940 );
941 foreach ((array)$lrRows as $row) if (!isset($lastReplyBy[$row['thread_id']])) $lastReplyBy[$row['thread_id']] = $row['player_name'];
942
943 $visibleThreads = array();
944 foreach ((array)$threads as $thread) {
945 $access = true;
946 if ($category['hidden'] == 1) {
947 $access = PlayerHaveAccess($yourChars, $thread['player_name']);
948 if ($yourAccess > 3 || $admin) $access = true;
949 }
950 if ($access) $visibleThreads[] = $thread;
951 }
952 ?>
953 <div class="znf-threads">
954 <?php if (!$visibleThreads): ?>
955 <p class="znf-empty"><?php echo t('forum.board_empty'); ?></p>
956 <?php endif; ?>
957 <?php foreach ($visibleThreads as $thread):
958 $e = 'znote_forum_e';
959 $turl = 'forum.php?forum=' . urlencode($category['name']) . '&cat=' . $getCatInt . '&thread=' . (int)$thread['id'];
960 $replyBy = $lastReplyBy[$thread['id']] ?? '';
961 ?>
962 <div class="znf-thread<?php echo $thread['sticky'] ? ' is-sticky' : ''; echo $thread['closed'] ? ' is-locked' : ''; ?>" data-href="<?php echo $e($turl); ?>" onclick="if(!event.target.closest('a,button,form,input'))window.location.href=this.dataset.href">
963 <span class="znf-thread__ico"><?php echo $thread['sticky'] ? '&#128204;' : ($thread['closed'] ? '&#128274;' : '&#128172;'); ?></span>
964 <span class="znf-thread__main">
965 <a class="znf-thread__title" href="<?php echo $e($turl); ?>"><?php echo $e($thread['title']); ?></a>
966 <span class="znf-thread__meta"><?php echo t('forum.started_by', array('name' => $e($thread['player_name']), 'ago' => znote_forum_ago($thread['created']))); ?></span>
967 </span>
968 <span class="znf-thread__stats">
969 <b><?php echo (int)$thread['reply_count']; ?></b> <?php echo t('forum.replies_word'); ?><br>
970 <?php echo (int)$thread['last_reply_at'] > 0
971 ? t('forum.last_by', array('name' => $e($replyBy), 'ago' => znote_forum_ago($thread['last_reply_at'])))
972 : t('forum.no_replies'); ?>
973 </span>
974 </div>
975 <?php endforeach; ?>
976 </div>
977 <?php
978
979 ///////////
980 // Create thread button
981 if ($charCount > 0) {
982 if ($category['closed'] == 0 || $admin) {
983 ?>
984 <div class="znf-newwrap">
985 <form action="" method="post">
986 <input type="hidden" value="<?php echo $getCat; ?>" name="new_thread_category">
987 <?php znote_forum_character_picker($yourChars, 'new_thread_cid', t('forum.create_thread_new'), 'btn btn-primary'); ?>
988 </form>
989 </div>
990 <?php
991 } else echo '<p class="znf-note">'. t('forum.board_closed'). '</p>';
992 } else echo '<p class="znf-note">'. t('forum.level_required_new_thread', ['level' => (int)$config['forum']['level']]) .'</p>';
993 } else echo '<p class="znf-alert">'. t('forum.board_access_denied', ['level' => (int)$config['forum']['level']]) .'</p>';
994
995 }
996} else {
997
998 //////////////////////
999 // No category specified, show list of available categories
1000 $boardWhereParams = [];
1001 if ($admin) {
1002 $boardWhere = '';
1003 } else {
1004 $boardWhere = " WHERE `f`.`access` <= ?";
1005 $boardWhereParams[] = (int)$yourAccess;
1006 }
1007 $categories = db()->fetchAll(
1008 "SELECT `f`.`id`, `f`.`name`, `f`.`access`, `f`.`closed`, `f`.`hidden`, `f`.`guild_id`, "
1009 . "COUNT(`t`.`id`) AS `thread_count`, COALESCE(MAX(`t`.`updated`), 0) AS `last_time` "
1010 . "FROM `znote_forum` `f` LEFT JOIN `znote_forum_threads` `t` ON `t`.`forum_id` = `f`.`id`"
1011 . $boardWhere
1012 . " GROUP BY `f`.`id`, `f`.`name`, `f`.`access`, `f`.`closed`, `f`.`hidden`, `f`.`guild_id` ORDER BY `f`.`name`;",
1013 $boardWhereParams
1014 );
1015
1016 $lastThreads = array();
1017 $ltRows = db()->fetchAll(
1018 "SELECT `x`.`forum_id`, `x`.`player_name`, `x`.`title`, `x`.`id` FROM `znote_forum_threads` `x` "
1019 . "JOIN (SELECT `forum_id`, MAX(`updated`) AS `mu` FROM `znote_forum_threads` GROUP BY `forum_id`) `y` "
1020 . "ON `y`.`forum_id` = `x`.`forum_id` AND `y`.`mu` = `x`.`updated`;"
1021 );
1022 foreach ((array)$ltRows as $row) if (!isset($lastThreads[$row['forum_id']])) $lastThreads[$row['forum_id']] = $row;
1023
1024 $guild = false;
1025 foreach ($charData as $char) if ($char['guild'] > 0) $guild = true;
1026 if (!isset($guilds)) {
1027 $guilds = db()->fetchAll("SELECT `id`, `name` FROM `guilds` ORDER BY `name`;");
1028 $guilds[] = array('id' => '0', 'name' => 'No guild');
1029 }
1030 $guildName = array();
1031 foreach ((array)$guilds as $g) $guildName[$g['id']] = $g['name'];
1032
1033 $mainBoards = array();
1034 $guildboard = array();
1035 foreach ((array)$categories as $b) {
1036 $li = $lastThreads[$b['id']] ?? null;
1037 $b['last_author'] = $li['player_name'] ?? '';
1038 $b['last_title'] = $li['title'] ?? '';
1039 if ((int)$b['guild_id'] > 0) {
1040 $inGuild = $admin;
1041 foreach ($charData as $char) if ((int)$b['guild_id'] === (int)$char['guild']) $inGuild = true;
1042 if ($inGuild) $guildboard[] = $b;
1043 } else {
1044 $mainBoards[] = $b;
1045 }
1046 }
1047
1048 $znfBoardRow = static function (array $b) use ($admin, $guildName) {
1049 $e = 'znote_forum_e';
1050 $id = (int)$b['id'];
1051 ?>
1052 <div class="znf-board" data-href="forum.php?cat=<?php echo $id; ?>" onclick="if(!event.target.closest('a,button,form,input'))window.location.href=this.dataset.href">
1053 <span class="znf-board__icon"><?php echo (int)$b['guild_id'] > 0 ? '&#9876;' : '&#128172;'; ?></span>
1054 <span class="znf-board__main">
1055 <span class="znf-board__name">
1056 <a href="forum.php?cat=<?php echo $id; ?>"><?php echo $e($b['name']); ?></a>
1057 <?php if ($b['closed']): ?><span class="znf-tag znf-tag--lock"><?php echo t('forum.locked_tag'); ?></span><?php endif; ?>
1058 <?php if ($b['hidden']): ?><span class="znf-tag znf-tag--hidden"><?php echo t('forum.hidden_tag'); ?></span><?php endif; ?>
1059 <?php if ((int)$b['guild_id'] > 0): ?><span class="znf-tag znf-tag--guild"><?php echo $e($guildName[$b['guild_id']] ?? ''); ?></span><?php endif; ?>
1060 </span>
1061 <?php if (!empty($b['last_time'])): ?>
1062 <span class="znf-board__last"><?php echo t('forum.lastpost'); ?> <b><?php echo $e($b['last_title']); ?></b> &middot; <?php echo $e($b['last_author']); ?> &middot; <?php echo znote_forum_ago($b['last_time']); ?></span>
1063 <?php else: ?>
1064 <span class="znf-board__last"><?php echo t('forum.board_empty'); ?></span>
1065 <?php endif; ?>
1066 </span>
1067 <span class="znf-board__count"><b><?php echo (int)$b['thread_count']; ?></b> <?php echo t('forum.threads_word'); ?></span>
1068 <?php if ($admin): ?>
1069 <span class="znf-board__admin">
1070 <form method="post"><input type="hidden" name="admin_category_id" value="<?php echo $id; ?>"><button class="btn btn-warning btn--sm" type="submit" name="admin_category_edit" value="Edit"><?php echo t('forum.edit'); ?></button></form>
1071 <form method="post"><input type="hidden" name="admin_category_id" value="<?php echo $id; ?>"><button class="btn btn-danger btn--sm" type="submit" name="admin_category_delete" value="1"><?php echo t('forum.delete_btn'); ?></button></form>
1072 </span>
1073 <?php endif; ?>
1074 </div>
1075 <?php
1076 };
1077 ?>
1078
1079 <?php if (!$mainBoards && !$guildboard): ?>
1080 <p class="znf-empty"><?php echo t('forum.no_boards'); ?></p>
1081 <?php endif; ?>
1082
1083 <?php if ($mainBoards): ?>
1084 <div class="znf-boards">
1085 <div class="znf-boards__title"><?php echo t('forum.boards'); ?></div>
1086 <?php foreach ($mainBoards as $b) $znfBoardRow($b); ?>
1087 </div>
1088 <?php endif; ?>
1089
1090 <?php if ($guildboard || ($guild && $admin)): ?>
1091 <div class="znf-boards">
1092 <div class="znf-boards__title"><?php echo t('forum.guild_boards'); ?></div>
1093 <?php if ($guildboard) { foreach ($guildboard as $b) $znfBoardRow($b); } else { ?>
1094 <p class="znf-empty"><?php echo t('forum.no_guildboards2'); ?></p>
1095 <?php } ?>
1096 </div>
1097 <?php endif; ?>
1098 <hr class="bighr">
1099 <?php
1100 if ($admin) {
1101 ?>
1102 <form action="" method="post" class="znf-form">
1103 <h2 class="znf-form__title"><?= t('forum.create_board') ?></h2>
1104 <div class="znf-field">
1105 <label for="admin_board_create_name"><?= t('forum.board_name') ?></label>
1106 <input type="text" id="admin_board_create_name" name="admin_board_create_name" placeholder="<?= t('forum.board_name') ?>">
1107 </div>
1108 <div class="znf-field">
1109 <label for="admin_board_create_access"><?= t('forum.required_access') ?></label>
1110 <select id="admin_board_create_access" name="admin_board_create_access">
1111 <?php foreach($config['ingame_positions'] as $access => $name) echo "<option value='$access'>$name</option>"; ?>
1112 </select>
1113 </div>
1114 <div class="znf-field">
1115 <label for="admin_board_create_closed"><?= t('forum.closed') ?></label>
1116 <select id="admin_board_create_closed" name="admin_board_create_closed">
1117 <option value="0"><?= t('common.no') ?></option>
1118 <option value="1"><?= t('common.yes') ?></option>
1119 </select>
1120 </div>
1121 <div class="znf-field">
1122 <label for="admin_board_create_hidden"><?= t('forum.hidden') ?></label>
1123 <select id="admin_board_create_hidden" name="admin_board_create_hidden">
1124 <option value="0"><?= t('common.no') ?></option>
1125 <option value="1"><?= t('common.yes') ?></option>
1126 </select>
1127 </div>
1128 <div class="znf-field">
1129 <label for="admin_board_create_guild_id"><?= t('forum.guild_boards') ?></label>
1130 <select id="admin_board_create_guild_id" name="admin_board_create_guild_id">
1131 <?php foreach($guilds as $guild) {
1132 $sel = ($guild['id'] == 0) ? ' selected' : '';
1133 echo "<option value='". $guild['id'] ."'$sel>". $guild['name'] ."</option>";
1134 } ?>
1135 </select>
1136 </div>
1137 <div class="znf-field znf-field--submit">
1138 <input type="submit" value="<?= t('forum.create_board_btn') ?>" class="btn btn-primary">
1139 </div>
1140 </form>
1141 <?php
1142 }
1143
1144}
1145
1146
1147echo '</div>';
1148theme_close(); ?>
1149
Top