public function __construct() { parent::__construct("Report Queue", el('h2', "Reports"), Config::getCfg('permissions')['delete']); $reports = Model::get()->getReports(); $html = "<table class='reportTable'><tr><th colspan='3'>Report Queue</th></tr><tr><th style='width:3em;'>Times</th><th>Post</th><th style='width:20em;'>Options</th></tr>"; foreach ($reports as $report) { $hash = bin2hex($report['md5']); $html .= "<tr id='report{$report['no']}'>"; $html .= "<td>" . $report['count'] . "</td>"; $html .= "<td><a href='{$report['threadid']}#p{$report['no']}' data-board='{$report['board']}' data-thread='{$report['threadid']}' data-post='{$report['no']}' class='quotelink noEmbed'>>>{$report['no']}</a></td>"; $html .= "<td><a class='button' href='javascript:deletePost({$report['no']},\"{$report['board']}\");' >Delete Post</a> "; $html .= "<a class='button' href='javascript:banImage(\"{$hash}\");' id='ban{$hash}'>Ban Image</a> "; $html .= "<a class='button' href='javascript:deleteReport({$report['no']},\"{$report['board']}\");'>Delete Report</a> "; $html .= "<a class='button' href='javascript:banReporter({$report['no']},\"{$report['board']}\");'>Ban Reporter</a></td>"; $html .= "</tr>"; } $html .= "</table>"; if (Site::getUser()->getPrivilege() >= Config::getCfg('permissions')['owner']) { $html .= "<br><table class='reportTable'><tr><th colspan='3'>Last Few Deleted Posts</th></tr><tr><th style='width:3em;'>Board</th><th>Post</th><th style='width:7em;'>Options</th></tr>"; foreach (Model::get()->getBoards() as $board) { $lastFew = OldModel::getLastNDeletedPosts($board->getName(), 5); foreach ($lastFew as $report) { $html .= "<tr id='report{$report['no']}'>"; $html .= "<td>" . $board->getName() . "</td>"; $html .= "<td>>>{$report['no']} ({$report['name']}{$report['trip']})</td>"; $html .= "<td><a class='button' href='javascript:restorePost({$report['no']},\"{$board->getName()}\");' >Restore Post</a></td>"; $html .= "</tr>"; } } $html .= "</table>"; } $this->appendToBody($html); }
function __construct() { parent::__construct(Config::getCfg('site')['pagetitle'], "", 0); $boards = Model::get()->getBoards(); $archiveBoards = array_filter($boards, function ($b) { return $b->isArchive(); }); $plainBoards = array_filter($boards, function ($b) { return !$b->isArchive(); }); $html = "<div class='boardlist_big'><h1>Archived Boards</h1><hr style='width:64px;'>"; foreach ($archiveBoards as $b) { $html .= Site::parseHtmlFragment("indexArchiveBoard.html", ["%ago%", "%crawltime%", "%shortname%", "%longname%", "%posts%", "%threads%", "%firstcrawl%"], [ago(time() - $b->getLastCrawl()), $b->getLastCrawl(), $b->getName(), $b->getLongName(), $b->getNoPosts(), $b->getNoThreads(), date("j F Y", $b->getFirstCrawl())]); } $html .= "</div>"; $html .= "<script type='text/javascript' src='/script/boardUpdate.js'></script>"; if (count($plainBoards) > 0) { $html .= "<div class='boardlist_big'><h1>Boards</h1><hr style='width:64px;'>"; foreach ($plainBoards as $b) { $html .= Site::parseHtmlFragment("indexBoard.html", ["%ago%", "%crawltime%", "%shortname%", "%longname%", "%posts%", "%threads%", "%firstcrawl%"], [ago(time() - $b->getLastCrawl()), $b->getLastCrawl(), $b->getName(), $b->getLongName(), $b->getNoPosts(), $b->getNoThreads(), date("j F Y", $b->getFirstCrawl())]); } $html .= "</div>"; } $this->setBody($html); }
public static function post(array $path) : array { $model = Model::get(); $board = $model->getBoard(get('board')); $num = (int) get('num'); $post = $model->getPost($board, $num); return self::fuukaFormat($post); }
/** * Logs in the session user. Throws exception if username * and password don't match any users. * * @param string $username * @param string $password * @throws NotFoundException */ static function logIn(string $username, string $password) { $user = Model::get()->getUser($username, $password); $_SESSION['user'] = $user; $uid = $user->getUID(); $time = time(); $ip = $_SERVER['REMOTE_ADDR']; Config::getPDOConnectionRW()->query("INSERT INTO `logins` (`uid`,`time`,`ip`) VALUES ({$uid},{$time},'{$ip}')"); }
function __construct() { parent::__construct("b-stats news", "", 0); $this->appendToBody("<h2>News</h2>"); $articles = Model::get()->getAllNewsArticles(); foreach ($articles as $article) { $date = date("Y-m-d g:i a", $article['time']); $content = nl2br($article['content']); $this->appendToBody(Site::parseHtmlFragment("article.html", ['_author_', '_id_', '_title_', '_content_', '_date_'], [$article['username'], $article['article_id'], $article['title'], $content, $date])); } }
/** * Naiive attempt at making an imageboard. * * @todo: Make this not as spaghetti * * @return string * @throws Exception * @throws \NotFoundException */ static function post() : string { srand((int) (microtime(true) * 1000)); if (post('captcha') != $_SESSION['captcha']) { $_SESSION['captcha'] = rand(100000, 999999); throw new Exception("Invalid Captcha"); } $_SESSION['captcha'] = rand(100000, 999999); $model = Model::get(); if (post('mode') != 'regist') { throw new Exception("invalid mode"); } $board = $model->getBoard(post('board')); if ($board->isArchive()) { throw new Exception("Board is an archive"); } $name = post('name', 'Anonymous'); if ($name == '') { $name = 'Anonymous'; } $trip = Yotsuba::parseTripcode($name); if ($trip == false) { $trip = null; } else { $trip = '!' . $trip; } $name = strtok($name, '#'); $com = post('com'); if ($com == '') { $com = null; } $file = self::checkUploadedFile(); if ($com == null && $file == null) { throw new Exception("Post must contain image"); } $post = $model->addPost($board, post('resto', 0), htmlspecialchars($name), $trip, htmlspecialchars(post('email')), htmlspecialchars(post('sub')), $com, $file); // auto-noko return "/{$board->getName()}/thread/{$post->getThreadId()}"; }
/** * Loads the last (n) posts from the DB * @param type $n * @return \Thread reference to self */ function loadLastN($n) { try { $posts = Model::get()->getLastNReplies($this, $n); foreach ($posts as $p) { $this->addPost($p); } } catch (Exception $e) { } return $this; }
if (!$db->query(file_get_contents("../sql/init.sql"))) { echo "Could not set up all the tables for some reason! Start over!" . PHP_EOL; echo "Error(s): " . PHP_EOL; foreach ($db->error_list as $error) { print_r($error); echo PHP_EOL; } exit; } echo PHP_EOL . PHP_EOL . PHP_EOL . "[ SITE CUSTOMIZATION ]" . PHP_EOL . PHP_EOL; $site = []; $site['name'] = readline("Enter a site name to show up on all pages (e.g., 'b-stats archive'): "); $date = date('j F Y'); $site['subtitle'] = readline("Enter a subtitle to show up below the name (e.g. 'since {$date}'): "); $site['pagetitle'] = readline("Enter a default HTML page title (e.g., 'archive'): "); $site['ga_id'] = readline("If you have a Google Analytics ID, enter it, otherwise press enter: "); if ($site['ga_id'] == '') { unset($site['ga_id']); } echo "Writing site configuration to ../cfg/site.json ... "; $json = json_encode($site, JSON_PRETTY_PRINT); file_put_contents("../cfg/site.json", json_encode($site, JSON_PRETTY_PRINT)); echo "Done!" . PHP_EOL . PHP_EOL; echo "One last thing, before your site is ready. You must create" . PHP_EOL; echo "an admin user account." . PHP_EOL; $username = readline("Enter admin username: "******"Enter admin password: "******"yotsuba"); echo "That's it! Your site is ready to go (hopefully)!" . PHP_EOL; echo "Thank you for choosing my shitty PHP scripts!" . PHP_EOL; echo PHP_EOL . "~terrance" . PHP_EOL;
* @param array $arr * @param array ...$key Keys to ensure the existence */ function require_keys(array $arr, ...$key) { foreach ($key as $k) { if (!array_key_exists($k, $arr)) { throw new RuntimeException("`{$k}` not set"); } } } error_reporting(E_ALL); define("MAX_TIME", 200); //Board config is stored in the database. try { $boardObj = Model::get()->getBoard($board); define("EXEC_TIME", $boardObj->getArchiveTime()); } catch (Exception $ex) { log_error("Board has not been configured. Add it to the boards table."); log_exception($ex); die; } file_put_contents("{$board}.pid", getmypid()); if (file_exists("{$board}.kill")) { unlink("{$board}.kill"); } $startTime = time(); function o($msg, $newline = true) { global $startTime, $logToFile, $board; $line = time() - $startTime . ' : ' . $msg . ($newline ? PHP_EOL : '');
<?php require_once '../inc/config.php'; use Site\Archivers; use Model\Model; $boards = Model::get()->getBoards(true); $log = function ($msg) { echo $msg . PHP_EOL; file_put_contents("check.log", $msg . PHP_EOL, FILE_APPEND); }; $log("Checking at " . date('c')); foreach ($boards as $board) { if (!$board->isArchive()) { continue; } $status = Archivers::getStatus($board->getName()); switch ($status) { case Archivers::STOPPED_UNCLEAN: Archivers::run($board->getName()); $log("Restarted uncleanly stopped archiver for " . $board->getName() . "."); break; case Archivers::RUNNING: $log("Archiver for {$board->getName()} is running normally."); break; case Archivers::STOPPING: $log("Archiver for {$board->getName()} is stopping normally."); break; case Archivers::STOPPED: $log("Archiver for {$board->getName()} is stopped normally."); break; }
public static function requests(array $path) : array { self::ensureGET(); Site::requirePrivilege(Config::getCfg('permissions')['owner']); return Model::get()->getRequests(); }
public static function post(array $path) : array { if (count($path) < 5) { throw new InvalidRequestURIException(); } $board = strtolower(alphanum($path[3])); $id = $path[4]; $model = Model::get(); $post = $model->getPost($model->getBoard($board), $id); if (count($path) === 6 && $path[5] == 'html') { $content = PostRenderer::renderPost($post); return ["html" => $content]; } return $post->asArray(); }
/** * Formats a plain-text post with HTML. * * Notes: * - cross-board or cross-thread links are not fixed, but instead left as * deadlinks. * - <code><wbr></code> tags are not replaced. * * @param Post $post * @return string HTML-formatted comment */ public static function toHtml(Post $post) { $comment = $post->getComment(); $posts = $post->hasThread() ? $post->getThread()->getPostIds() : []; $search[0] = '~>>([0-9]{1,9})~'; // >>123 type post links $search[1] = "~^>(.*)\$~m"; // >greentext $search[2] = "~>>>/([a-z]{1,4})/~"; // >>>/board/ links $search[3] = "~>>>/([a-z]{1,4})/([0-9]{1,9})~"; // >>>/board/123 type post links $replace[0] = '<a href="" class="quotelink">>>$1</a>'; $replace[1] = '<span class="quote">>$1</span>'; $replace[2] = '<a href="/$1/" class="quotelink">>>>/$1/</a>'; $replace[3] = '<span class="deadlink">>>>/$1/$2</span>'; $srch = array('&', "'", '<', '>', '"'); $rpl = array("&", ''', '<', '>', """); $htmlSpecialCharComment = str_replace($srch, $rpl, $comment); $initialTagComment = preg_replace($search, $replace, $htmlSpecialCharComment); $formattedComment = preg_replace_callback('~<a href="" class="quotelink">>>([0-9]{1,9})</a>~', function ($matches) use($posts, $post) { if (in_array($matches[1], $posts)) { return '<a href="#p' . $matches[1] . '" class="quotelink" ' . 'data-board="' . $post->getBoard()->getName() . '"' . 'data-thread="' . $post->getThreadId() . '"' . 'data-post="' . $matches[1] . '"' . '>>>' . $matches[1] . '</a>'; } else { try { $threadId = Model::get()->getThreadId($post->getBoard(), $matches[1]); return '<a href="' . $threadId . '#p' . $matches[1] . '" class="quotelink" ' . 'data-board="' . $post->getBoard()->getName() . '"' . 'data-thread="' . $threadId . '"' . 'data-post="' . $matches[1] . '"' . '>>>' . $matches[1] . '</a>'; } catch (\NotFoundException $ex) { return '<span class="deadlink">>>' . $matches[1] . '</span>'; } } }, $initialTagComment); $formattedComment = str_replace("\r", "", $formattedComment); $finalComment = str_replace("\n", "<br>", $formattedComment); return $finalComment; }
/** * Routes to the proper view given a path relative to the site root. * @param string $path * @throws Exception * @throws NotFoundException if the path can't be routed */ public static function route(string $path) { $exploded = explode('/', $path); $base = strtolower($exploded[1]); switch ($base) { case "do": // Action pages begin with do Action::run($exploded); break; case "api": // API endpoints begin with api $page = PublicApi::run($exploded); break; case "admin": $page = AdminApi::run($exploded); break; case "_": // Fuuka API support $page = FuukaApiAdaptor::run($exploded); break; default: $pages = Config::getCfg('pages'); $boards = Model::get()->getBoards(true); if (array_key_exists($base, $boards)) { $board = $boards[$base]; if (isset($exploded[2])) { switch ($exploded[2]) { case "catalog": $page = new Catalog($board); break; case "thread": case "res": $num = $exploded[3] ?? ""; if (is_numeric($num)) { $page = new ThreadView(Model::get()->getThread($board, $num)); } else { throw new Exception("Invalid thread id provided"); } break; case "post": $post = Model::get()->getPost($board, $exploded[3] ?? 0); try { $thread = Model::get()->getThread($board, $post->threadid); header("Location: /{$board->getName()}/thread/{$post->getThreadId()}#{$post->getNo()}"); exit; } catch (NotFoundException $ex) { $page = new OrphanPost($post); } break; case "search": $page = new Search($board, $exploded); break; case "": $page = new BoardIndexPage($boards[$base], 1); break; default: if (is_numeric($exploded[2])) { $page = new BoardIndexPage($boards[$base], $exploded[2]); } else { throw new Exception("Unknown board page requested"); } break; } } else { if (!array_key_exists($base, $pages)) { header("Location: {$path}/"); exit; } else { $class = '\\View\\Pages\\' . $pages[$base]; $page = new $class(); } } } else { if (array_key_exists($base, $pages)) { $class = '\\View\\Pages\\' . $pages[$base]; $page = new $class(); } } break; } if (isset($page)) { echo $page->display(); } else { throw new NotFoundException('Unrecognized URL: ' . $path); } }
static function run(string $board) : bool { if (file_exists(Site::getPath() . "/backend/{$board}-archiver.php") && !file_exists(Site::getPath() . "/backend/no_custom_archivers")) { if (self::getStatus($board) == self::STOPPED || self::getStatus($board) == self::STOPPED_UNCLEAN) { if (PHP_OS == "Linux") { exec("cd " . Site::getPath() . "/backend/ && php {$board}-archiver.php -f >> /dev/null &"); } else { $path = Site::getPath() . "/backend/{$board}-archiver.php"; $cmd = "c:/php/php.exe \"{$path}\" -f"; pclose(popen('cd ' . Site::getPath() . "/backend/" . ' && start /b ' . $cmd, 'r')); } return true; } } else { $b = Model::get()->getBoard($board); if (!$b->isArchive()) { return false; } if (self::getStatus($board) == self::STOPPED || self::getStatus($board) == self::STOPPED_UNCLEAN) { if (PHP_OS == "Linux") { exec("cd " . Site::getPath() . "/backend/ && " . "php archiver.php -b {$board} -f >> /dev/null &"); } else { $path = Site::getPath() . "/backend/archiver.php"; $cmd = "c:/php/php.exe \"{$path}\" -b {$board} -f"; pclose(popen('cd ' . Site::getPath() . "/backend/" . ' && start /b ' . $cmd, 'r')); } return true; } } return false; }
public static function getBoardList() : string { $ret = ""; $boards = Model::get()->getBoards(); $types = []; $types['Archives'] = array_filter($boards, function ($b) { /* @var Board $b */ return $b->isArchive(); }); $types['Boards'] = array_filter($boards, function ($b) { /* @var Board $b */ return !$b->isArchive(); }); foreach ($types as $n => $t) { if (count($t) == 0) { continue; } $ret .= $n . ": "; $groups = array(); foreach ($t as $board) { $groups[$board['group']][] = $board; } foreach ($groups as $group) { $ret .= "["; $i = 0; foreach ($group as $board) { if ($i++ > 0) { $ret .= " / "; } $ret .= '<a href="/' . $board['shortname'] . '/" title="' . $board['longname'] . '">' . $board['shortname'] . '</a>'; } $ret .= "] "; } } return $ret; }
private function id($id) { if ($id == null) { throw new Exception('Invalid ID'); } if ($id == 'Heaven') { throw new Exception('ID Heaven posted so much that searching for his posts would slow the server down.'); } $id = str_replace('-', '/', $id); return Model::get()->getPostsByID($this->board, $id, $this->perPage, $this->start); }
private function renderExtraButtons() { $extraButtons = ""; if ($this->user->getPrivilege() >= Site::LEVEL_ADMIN) { $no = Model::get()->getNumberOfReports(); $reports = $no ? " ({$no})" : ""; $extraButtons .= span('[' . a('Reports' . $reports, '/reports') . ']', 'navelement') . PHP_EOL; } if ($this->user->getPrivilege() > Site::LEVEL_ADMIN) { $extraButtons .= span('[' . a('SCP', '/scp') . ']', 'navelement') . PHP_EOL; } if ($this->user->canSearch()) { $extraButtons .= span('[' . a('Search', '/search') . ']', 'navelement') . PHP_EOL; } return $extraButtons; }