get_int("forum_version") < 1) {
            $database->create_table("forum_threads", "
					id SCORE_AIPK,
					sticky SCORE_BOOL NOT NULL DEFAULT SCORE_BOOL_N,
					title VARCHAR(255) NOT NULL,
					user_id INTEGER NOT NULL,
					date SCORE_DATETIME NOT NULL,
					uptodate SCORE_DATETIME NOT NULL,
					FOREIGN KEY (user_id) REFERENCES users(id) ON UPDATE CASCADE ON DELETE RESTRICT
					");
            $database->execute("CREATE INDEX forum_threads_date_idx ON forum_threads(date)", []);
            $database->create_table("forum_posts", "
					id SCORE_AIPK,
					thread_id INTEGER NOT NULL,
					user_id INTEGER NOT NULL,
					date SCORE_DATETIME NOT NULL,
					message TEXT,
					FOREIGN KEY (user_id) REFERENCES users(id) ON UPDATE CASCADE ON DELETE RESTRICT,
					FOREIGN KEY (thread_id) REFERENCES forum_threads (id) ON UPDATE CASCADE ON DELETE CASCADE
					");
            $database->execute("CREATE INDEX forum_posts_date_idx ON forum_posts(date)", []);
            $config->set_int("forum_version", 2);
            $config->set_int("forumTitleSubString", 25);
            $config->set_int("forumThreadsPerPage", 15);
            $config->set_int("forumPostsPerPage", 15);
            $config->set_int("forumMaxCharsPerPost", 512);
            log_info("forum", "extension installed");
        }
        if ($config->get_int("forum_version") < 2) {
            $database->execute("ALTER TABLE forum_threads ADD FOREIGN KEY (user_id) REFERENCES users(id) ON UPDATE CASCADE ON DELETE RESTRICT");
            $database->execute("ALTER TABLE forum_posts ADD FOREIGN KEY (user_id) REFERENCES users(id) ON UPDATE CASCADE ON DELETE RESTRICT");
            $config->set_int("forum_version", 2);
        }
    }
    public function onSetupBuilding(SetupBuildingEvent $event)
    {
        $sb = new SetupBlock("Forum");
        $sb->add_int_option("forumTitleSubString", "Title max long: ");
        $sb->add_int_option("forumThreadsPerPage", "
Threads per page: ");
        $sb->add_int_option("forumPostsPerPage", "
Posts per page: ");
        $sb->add_int_option("forumMaxCharsPerPost", "
Max chars per post: ");
        $event->panel->add_block($sb);
    }
    public function onUserPageBuilding(UserPageBuildingEvent $event)
    {
        global $database;
        $threads_count = $database->get_one("SELECT COUNT(*) FROM forum_threads WHERE user_id=?", [$event->display_user->id]);
        $posts_count = $database->get_one("SELECT COUNT(*) FROM forum_posts WHERE user_id=?", [$event->display_user->id]);
        $days_old = ((time() - strtotime($event->display_user->join_date)) / 86400) + 1;
        $threads_rate = sprintf("%.1f", ($threads_count / $days_old));
        $posts_rate = sprintf("%.1f", ($posts_count / $days_old));
        $event->add_stats("Forum threads: $threads_count, $threads_rate per day");
        $event->add_stats("Forum posts: $posts_count, $posts_rate per day");
    }
    public function onPageRequest(PageRequestEvent $event)
    {
        global $page, $user;
        if ($event->page_matches("forum")) {
            switch ($event->get_arg(0)) {
                case "index":
                    $this->show_last_threads($page, $event, $user->is_admin());
                    if (!$user->is_anonymous()) {
                        $this->theme->display_new_thread_composer($page);
                    }
                    break;
                case "view":
                    $threadID = int_escape($event->get_arg(1));
                    $pageNumber = int_escape($event->get_arg(2));
                    list($errors) = $this->sanity_check_viewed_thread($threadID);
                    if ($errors!=null) {
                        $this->theme->display_error(500, "Error", $errors);
                        break;
                    }
                    $this->show_posts($event, $user->is_admin());
                    if ($user->is_admin()) {
                        $this->theme->add_actions_block($page, $threadID);
                    }
                    if (!$user->is_anonymous()) {
                        $this->theme->display_new_post_composer($page, $threadID);
                    }
                    break;
                case "new":
                    global $page;
                    $this->theme->display_new_thread_composer($page);
                    break;
                case "create":
                    $redirectTo = "forum/index";
                    if (!$user->is_anonymous()) {
                        list($errors) = $this->sanity_check_new_thread();
                        if ($errors!=null) {
                            $this->theme->display_error(500, "Error", $errors);
                            break;
                        }
                        $newThreadID = $this->save_new_thread($user);
                        $this->save_new_post($newThreadID, $user);
                        $redirectTo = "forum/view/".$newThreadID."/1";
                    }
                    $page->set_mode(PageMode::REDIRECT);
                    $page->set_redirect(make_link($redirectTo));
                    break;
                case "delete":
                    $threadID = int_escape($event->get_arg(1));
                    $postID = int_escape($event->get_arg(2));
                    if ($user->is_admin()) {
                        $this->delete_post($postID);
                    }
                    $page->set_mode(PageMode::REDIRECT);
                    $page->set_redirect(make_link("forum/view/".$threadID));
                    break;
                case "nuke":
                    $threadID = int_escape($event->get_arg(1));
                    if ($user->is_admin()) {
                        $this->delete_thread($threadID);
                    }
                    $page->set_mode(PageMode::REDIRECT);
                    $page->set_redirect(make_link("forum/index"));
                    break;
                case "answer":
                    $threadID = int_escape($_POST["threadID"]);
                    $total_pages = $this->get_total_pages_for_thread($threadID);
                    if (!$user->is_anonymous()) {
                        list($errors) = $this->sanity_check_new_post();
                        if ($errors!=null) {
                            $this->theme->display_error(500, "Error", $errors);
                            break;
                        }
                        $this->save_new_post($threadID, $user);
                    }
                    $page->set_mode(PageMode::REDIRECT);
                    $page->set_redirect(make_link("forum/view/".$threadID."/".$total_pages));
                    break;
                default:
                    $page->set_mode(PageMode::REDIRECT);
                    $page->set_redirect(make_link("forum/index"));
                    //$this->theme->display_error(400, "Invalid action", "You should check forum/index.");
                    break;
            }
        }
    }
    private function get_total_pages_for_thread(int $threadID)
    {
        global $database, $config;
        $result = $database->get_row("SELECT COUNT(1) AS count FROM forum_posts WHERE thread_id = ?", [$threadID]);
        return ceil($result["count"] / $config->get_int("forumPostsPerPage"));
    }
    private function sanity_check_new_thread()
    {
        $errors = null;
        if (!array_key_exists("title", $_POST)) {
            $errors .= "