make_link("user/{$row[$this->name]}")], $row[$this->name]);
    }
}
class UserActionColumn extends ActionColumn
{
    public function __construct()
    {
        parent::__construct("id", "User Links");
        $this->sortable = false;
    }
    public function display(array $row)
    {
        return A(["href"=>make_link("post/list/user={$row['name']}/1")], "Posts");
    }
}
class UserTable extends Table
{
    public function __construct(\FFSPHP\PDO $db)
    {
        global $_shm_user_classes;
        $classes = [];
        foreach ($_shm_user_classes as $cls) {
            $classes[$cls->name] = $cls->name;
        }
        ksort($classes);
        parent::__construct($db);
        $this->table = "users";
        $this->base_query = "SELECT * FROM users";
        $this->size = 100;
        $this->limit = 1000000;
        $this->set_columns([
            new IntegerColumn("id", "ID"),
            new UserNameColumn("name", "Name"),
            new EnumColumn("class", "Class", $classes),
            // Added later, for admins only
            // new TextColumn("email", "Email"),
            new DateColumn("joindate", "Join Date"),
            new UserActionColumn(),
        ]);
        $this->order_by = ["id DESC"];
        $this->table_attrs = ["class" => "zebra"];
    }
}
class UserCreationException extends SCoreException
{
}
class NullUserException extends SCoreException
{
}
class UserPage extends Extension
{
    /** @var UserPageTheme $theme */
    public $theme;
    public function onInitExt(InitExtEvent $event)
    {
        global $config;
        $config->set_default_bool("login_signup_enabled", true);
        $config->set_default_int("login_memory", 365);
        $config->set_default_string("avatar_host", "none");
        $config->set_default_int("avatar_gravatar_size", 80);
        $config->set_default_string("avatar_gravatar_default", "");
        $config->set_default_string("avatar_gravatar_rating", "g");
        $config->set_default_bool("login_tac_bbcode", true);
    }
    public function onUserLogin(UserLoginEvent $event)
    {
        global $user;
        $user = $event->user;
    }
    public function onPageRequest(PageRequestEvent $event)
    {
        global $config, $database, $page, $user;
        $this->show_user_info();
        if ($event->page_matches("user_admin")) {
            if ($event->get_arg(0) == "login") {
                if (isset($_POST['user']) && isset($_POST['pass'])) {
                    $this->page_login($_POST['user'], $_POST['pass']);
                } else {
                    $this->theme->display_login_page($page);
                }
            } elseif ($event->get_arg(0) == "recover") {
                $this->page_recover($_POST['username']);
            } elseif ($event->get_arg(0) == "create") {
                $this->page_create();
            } elseif ($event->get_arg(0) == "create_other") {
                $uce = new UserCreationEvent($_POST['name'], $_POST['pass1'], $_POST['email'], false);
                send_event($uce);
                $page->set_mode(PageMode::REDIRECT);
                $page->set_redirect(make_link("admin"));
                $page->flash("Created new user");
            } elseif ($event->get_arg(0) == "list") {
                $t = new UserTable($database->raw_db());
                $t->token = $user->get_auth_token();
                $t->inputs = $_GET;
                if ($user->can(Permissions::DELETE_USER)) {
                    $col = new TextColumn("email", "Email");
                    // $t->columns[] = $col;
                    array_splice($t->columns, 2, 0, [$col]);
                }
                $this->theme->display_user_list($page, $t->table($t->query()), $t->paginator());
            } elseif ($event->get_arg(0) == "logout") {
                $this->page_logout();
            }
            if (!$user->check_auth_token()) {
                return;
            } elseif ($event->get_arg(0) == "change_name") {
                $input = validate_input([
                    'id' => 'user_id,exists',
                    'name' => 'user_name',
                ]);
                $duser = User::by_id($input['id']);
                $this->change_name_wrapper($duser, $input['name']);
            } elseif ($event->get_arg(0) == "change_pass") {
                $input = validate_input([
                    'id' => 'user_id,exists',
                    'pass1' => 'password',
                    'pass2' => 'password',
                ]);
                $duser = User::by_id($input['id']);
                $this->change_password_wrapper($duser, $input['pass1'], $input['pass2']);
            } elseif ($event->get_arg(0) == "change_email") {
                $input = validate_input([
                    'id' => 'user_id,exists',
                    'address' => 'email',
                ]);
                $duser = User::by_id($input['id']);
                $this->change_email_wrapper($duser, $input['address']);
            } elseif ($event->get_arg(0) == "change_class") {
                $input = validate_input([
                    'id' => 'user_id,exists',
                    'class' => 'user_class',
                ]);
                $duser = User::by_id($input['id']);
                $this->change_class_wrapper($duser, $input['class']);
            } elseif ($event->get_arg(0) == "delete_user") {
                $this->delete_user($page, isset($_POST["with_images"]), isset($_POST["with_comments"]));
            }
        }
        if ($event->page_matches("user")) {
            $display_user = ($event->count_args() == 0) ? $user : User::by_name($event->get_arg(0));
            if ($event->count_args() == 0 && $user->is_anonymous()) {
                $this->theme->display_error(
                    401,
                    "Not Logged In",
                    "You aren't logged in. First do that, then you can see your stats."
                );
            } elseif (!is_null($display_user) && ($display_user->id != $config->get_int("anon_id"))) {
                $e = new UserPageBuildingEvent($display_user);
                send_event($e);
                $this->display_stats($e);
            } else {
                $this->theme->display_error(
                    404,
                    "No Such User",
                    "If you typed the ID by hand, try again; if you came from a link on this ".
                    "site, it might be bug report time..."
                );
            }
        }
    }
    public function onUserPageBuilding(UserPageBuildingEvent $event)
    {
        global $user, $config;
        $h_join_date = autodate($event->display_user->join_date);
        if ($event->display_user->can(Permissions::HELLBANNED)) {
            $h_class = $event->display_user->class->parent->name;
        } else {
            $h_class = $event->display_user->class->name;
        }
        $event->add_stats("Joined: $h_join_date", 10);
        if ($user->name == $event->display_user->name) {
            $event->add_stats("Current IP: {$_SERVER['REMOTE_ADDR']}", 80);
        }
        $event->add_stats("Class: $h_class", 90);
        $av = $event->display_user->get_avatar_html();
        if ($av) {
            $event->add_stats($av, 0);
        } elseif ((
            $config->get_string("avatar_host") == "gravatar"
        ) &&
            ($user->id == $event->display_user->id)
        ) {
            $event->add_stats(
                "No avatar? This gallery uses Gravatar for avatar hosting, use the".
                "
same email address here and there to have your avatar synced
",
                0
            );
        }
    }
    public function onPageNavBuilding(PageNavBuildingEvent $event)
    {
        global $user;
        if ($user->is_anonymous()) {
            $event->add_nav_link("user", new Link('user_admin/login'), "Account", null, 10);
        } else {
            $event->add_nav_link("user", new Link('user'), "Account", null, 10);
        }
    }
    private function display_stats(UserPageBuildingEvent $event)
    {
        global $user, $page, $config;
        ksort($event->stats);
        $this->theme->display_user_page($event->display_user, $event->stats);
        if (!$user->is_anonymous()) {
            if ($user->id == $event->display_user->id || $user->can("edit_user_info")) {
                $user_config = UserConfig::get_for_user($event->display_user->id);
                $uobe = new UserOperationsBuildingEvent($event->display_user, $user_config);
                send_event($uobe);
                $page->add_block(new Block("Operations", $this->theme->build_operations($event->display_user, $uobe), "main", 60));
            }
        }
        if ($user->id == $event->display_user->id) {
            $ubbe = new UserBlockBuildingEvent();
            send_event($ubbe);
            ksort($ubbe->parts);
            $this->theme->display_user_links($page, $user, $ubbe->parts);
        }
        if (
            ($user->can(Permissions::VIEW_IP) || ($user->is_logged_in() && $user->id == $event->display_user->id)) && # admin or self-user
            ($event->display_user->id != $config->get_int('anon_id')) # don't show anon's IP list, it is le huge
        ) {
            $this->theme->display_ip_list(
                $page,
                $this->count_upload_ips($event->display_user),
                $this->count_comment_ips($event->display_user),
                $this->count_log_ips($event->display_user)
            );
        }
    }
    public function onSetupBuilding(SetupBuildingEvent $event)
    {
        global $config;
        $hosts = [
            "None" => "none",
            "Gravatar" => "gravatar"
        ];
        $sb = $event->panel->create_new_block("User Options");
        $sb->start_table();
        $sb->add_bool_option(UserConfig::ENABLE_API_KEYS, "Enable user API keys", true);
        $sb->add_bool_option("login_signup_enabled", "Allow new signups", true);
        $sb->add_longtext_option("login_tac", "Terms & Conditions", true);
        $sb->add_choice_option(
            "user_loginshowprofile",
            [
                "return to previous page" => 0, // 0 is default
                "send to user profile" => 1],
            "On log in/out",
            true
        );
        $sb->add_choice_option("avatar_host", $hosts, "Avatars", true);
        if ($config->get_string("avatar_host") == "gravatar") {
            $sb->start_table_row();
            $sb->start_table_cell(2);
            $sb->add_label("