1 <?php 2 require_once __DIR__ . '/../includes/bootstrap.php'; 3 must_login(); 4 5 $allCats = DB::rows('SELECT * FROM categories ORDER BY position, id'); 6 $cats = array_filter($allCats, fn($c) => can_post_topic($c)); 7 $selCat = (int)get('cat'); 8 $errs = []; 9 10 if ($_SERVER['REQUEST_METHOD'] === 'POST') { 11 if (!csrf_ok()) { $errs[] = 'Invalid request.'; } 12 else { 13 $title = sanitise(post('title')); 14 $content = sanitise(post('content')); 15 $catId = (int)post('cat_id'); 16 $tagStr = sanitise(post('tags')); 17 18 if (!$title) $errs[] = 'Title is required.'; 19 if (!$content) $errs[] = 'Post content is required.'; 20 if (!$catId) $errs[] = 'Please select a category.'; 21 if (strlen($title) > 200) $errs[] = 'Title too long (max 200 chars).'; 22 if (strlen($content) > 50000) $errs[] = 'Content too long.'; 23 24 if (!$errs && !DB::row('SELECT id FROM categories WHERE id=?', [$catId])) $errs[] = 'Invalid category.'; 25 if (!$errs) { 26 $postCat = DB::row('SELECT * FROM categories WHERE id=?', [$catId]); 27 if (!$postCat || !can_post_topic($postCat)) $errs[] = 'You do not have permission to post in this category.'; 28 } 29 30 if (!$errs && cfg('topic_captcha_enabled', '0') === '1') { 31 if (!post_captcha_verify(post('topic_captcha'))) $errs[] = 'Incorrect security answer.'; 32 } 33 34 if (!$errs) { 35 $rl = rate_check((int)$USER['id'], 'topic'); 36 if (!$rl['ok']) { 37 $wait = (int)$rl['wait']; 38 $errs[] = "Slow down! Please wait {$wait} second" . ($wait !== 1 ? 's' : '') . '.'; 39 } 40 } 41 42 if (!$errs) { 43 $slug = unique_slug($title, 'topics'); 44 $tid = DB::insert('INSERT INTO topics (title,slug,category_id,user_id) VALUES (?,?,?,?)', 45 [$title, $slug, $catId, $USER['id']]); 46 $pid = DB::insert('INSERT INTO posts (topic_id,user_id,content,post_num) VALUES (?,?,?,1)', 47 [$tid, $USER['id'], $content]); 48 49 if ($tagStr) { 50 $names = array_slice(array_filter(array_map('trim', explode(',', mb_strtolower($tagStr)))), 0, 5); 51 foreach ($names as $n) { 52 if (!$n) continue; 53 $tg = DB::row('SELECT id FROM tags WHERE name=?', [$n]); 54 $tgId = $tg ? $tg['id'] : DB::insert('INSERT INTO tags (name) VALUES (?)', [$n]); 55 DB::insertIgnore('topic_tags', ['topic_id', 'tag_id'], [$tid, $tgId]); 56 DB::run('UPDATE tags SET topic_count=topic_count+1 WHERE id=?', [$tgId]); 57 } 58 } 59 DB::run('UPDATE users SET topic_count=topic_count+1,post_count=post_count+1 WHERE id=?', [$USER['id']]); 60 DB::run('UPDATE categories SET topic_count=topic_count+1,post_count=post_count+1 WHERE id=?', [$catId]); 61 process_mentions($content, $pid, $slug, (int)$USER['id'], $USER['username']); 62 rate_record((int)$USER['id'], 'topic'); 63 addon_hook('after_topic_created', ['topic_id'=>$tid,'title'=>$title,'slug'=>$slug,'category_id'=>$catId,'user_id'=>(int)$USER['id']]); 64 go('forum/topic.php?slug=' . urlencode($slug)); 65 } 66 } 67 } 68 69 $PAGE_TITLE = 'New Topic'; 70 include __DIR__ . '/../views/partials/layout.php'; 71 ?> 72 <nav class="bc"><a href="<?=u('/')?>">Home</a> βΊ <span>New Topic</span></nav> 73 <div class="form-card"> 74 <h1>Create a New Topic</h1> 75 <?php if($errs):?><div class="alert err"><?=implode('<br>',array_map('e',$errs))?></div><?php endif;?> 76 <form method="POST" id="ntForm"> 77 <?=csrf_input()?> 78 <div class="fg"> 79 <label>Category <span class="req">*</span></label> 80 <select name="cat_id" class="fi" required> 81 <option value="">β Select a category β</option> 82 <?php foreach($cats as $c):?> 83 <option value="<?=$c['id']?>" <?=($selCat==$c['id']||((int)($_POST['cat_id']??0))==$c['id'])?'selected':''?>> 84 <?=e($c['icon'])?> <?=e($c['name'])?> 85 </option> 86 <?php endforeach;?> 87 </select> 88 </div> 89 <div class="fg"> 90 <label>Title <span class="req">*</span></label> 91 <input type="text" name="title" class="fi" required maxlength="200" 92 value="<?=e($_POST['title']??'')?>" placeholder="What is this about?"> 93 </div> 94 <div class="fg"> 95 <label>Tags <small>(optional, comma-separated)</small></label> 96 <input type="text" name="tags" id="tagsIn" class="fi" value="<?=e($_POST['tags']??'')?>" placeholder="help, tutorial"> 97 <div id="tagPreview" class="tag-preview"></div> 98 </div> 99 <div class="fg"> 100 <label>Content <span class="req">*</span></label> 101 <div class="ed-toolbar"> 102 <button type="button" onclick="fmt('bold','replyTa')"><b>B</b></button> 103 <button type="button" onclick="fmt('italic','replyTa')"><i>I</i></button> 104 <button type="button" onclick="fmt('link','replyTa')">π</button> 105 <button type="button" onclick="fmt('quote','replyTa')" title="Quote"><svg viewBox="0 0 24 24" fill="currentColor" width="13" height="13" style="vertical-align:middle"><path d="M6 17h3l2-4V7H5v6h3zm8 0h3l2-4V7h-6v6h3z"/></svg></button> 106 <button type="button" onclick="fmt('codeblock','replyTa')">π</button> 107 <button type="button" onclick="fmt('heading','replyTa')">H</button> 108 <button type="button" onclick="fmt('ul','replyTa')">β‘</button> 109 <div class="ed-sep"></div> 110 <button type="button" onclick="pickImg('ntImg','replyTa')">πΌ</button> 111 <input type="file" id="ntImg" accept="image/*" style="display:none" onchange="uploadImg(this,'replyTa')"> 112 <div class="ed-sep"></div> 113 <button type="button" id="prevBtn" onclick="togglePreview()">π Preview</button> 114 </div> 115 <div class="ed-panes"> 116 <textarea id="replyTa" name="content" class="reply-ta" rows="14" required 117 placeholder="Write your postβ¦ Markdown supported. Tip: use @username to mention someone!"><?=e($_POST['content']??'')?></textarea> 118 <div id="replyPreview" class="reply-preview hidden"></div> 119 </div> 120 </div> 121 <?php if (cfg('topic_captcha_enabled','0') === '1'): ?> 122 <?php $tc = post_captcha_generate(); ?> 123 <div class="fg captcha-box"> 124 <label class="captcha-label">π Security β What is <strong class="captcha-q"><?= e($tc['q']) ?></strong></label> 125 <input type="number" name="topic_captcha" class="fi captcha-input" required placeholder="Answer" autocomplete="off" min="-99" max="99"> 126 <span class="hint">Solve this math problem to post</span> 127 </div> 128 <?php endif; ?> 129 130 <div class="form-actions"> 131 <a href="<?=u('/')?>" class="btn-ghost">Cancel</a> 132 <button type="submit" class="btn-primary btn-lg">Post Topic</button> 133 </div> 134 </form> 135 </div> 136 <script> 137 document.getElementById('tagsIn').addEventListener('input',function(){ 138 var tags=this.value.split(',').map(function(t){return t.trim();}).filter(Boolean); 139 document.getElementById('tagPreview').innerHTML=tags.map(function(t){return '<span class="tag">'+escH(t)+'</span>';}).join(''); 140 }); 141 function escH(s){return s.replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>');} 142 document.getElementById('replyTa').addEventListener('paste',function(e){ 143 var items=(e.clipboardData||e.originalEvent.clipboardData).items; 144 for(var i=0;i<items.length;i++){if(items[i].type.indexOf('image')!==-1){e.preventDefault();uploadFileToEditor(items[i].getAsFile(),'replyTa');}} 145 }); 146 </script> 147 <?php include __DIR__ . '/../views/partials/layout_end.php'; ?>