Uncaught TypeError

AvatarSource::validImageUrl(): Return value must be of type bool, null returned

/data/core/classes/Avatars/AvatarSource.php

https://dev3.partydragen.com/forum/
/data/core/classes/Avatars/AvatarSource.php
            if (self::validImageUrl($url)) {
                return $url;
            }
        }

        return "https://api.dicebear.com/5.x/initials/png?seed={$data->username}&size={$size}";
    }

    /**
     * Determine if a URL is a valid image URL for avatars.
     *
     * @param  string $url URL to check
     * @return bool   Whether the URL is a valid image URL
     */
    private static function validImageUrl(string $url): bool
    {
        $cache = new Cache(['name' => 'nameless', 'extension' => '.cache', 'path' => ROOT_PATH . '/cache/']);
        $cache->setCache('avatar_validity');

        if ($cache->isCached($url)) {
            return $cache->retrieve($url);
        }

        $is_valid = false;

        try {
            $response = HttpClient::createClient()->head($url);
            $headers = $response->getHeaders();
            if (isset($headers['Content-Type']) && $headers['Content-Type'][0] === 'image/png') {
                $is_valid = true;
            }
        } catch (Exception $ignored) {
        }

        $cache->store($url, $is_valid, 3600);

        return $is_valid;
    }

    /**
     * Get the currently active avatar source.
/data/core/classes/Avatars/AvatarSource.php
                return ($full ? rtrim(URL::getSelfURL(), '/') : '') . ((defined('CONFIG_PATH')) ? CONFIG_PATH . '/' : '/') . 'uploads/avatars/defaults/' . DEFAULT_AVATAR_IMAGE;
            }
        }

        // Attempt to get their MC avatar if Minecraft integration is enabled
        if (Settings::get('mc_integration')) {
            if ($data->uuid != null && $data->uuid != 'none') {
                $uuid = $data->uuid;
            } else {
                $uuid = $data->username;
                // Fallback to steve avatar if they have an invalid username
                if (preg_match('#[^][_A-Za-z0-9]#', $uuid)) {
                    $uuid = 'Steve';
                }
            }

            $url = self::getAvatarFromUUID($uuid, $size);
            // The avatar might be invalid if they are using
            // an MC avatar service that uses only UUIDs
            // and this user doesn't have one
            if (self::validImageUrl($url)) {
                return $url;
            }
        }

        return "https://api.dicebear.com/5.x/initials/png?seed={$data->username}&size={$size}";
    }

    /**
     * Determine if a URL is a valid image URL for avatars.
     *
     * @param  string $url URL to check
     * @return bool   Whether the URL is a valid image URL
     */
    private static function validImageUrl(string $url): bool
    {
        $cache = new Cache(['name' => 'nameless', 'extension' => '.cache', 'path' => ROOT_PATH . '/cache/']);
        $cache->setCache('avatar_validity');

        if ($cache->isCached($url)) {
            return $cache->retrieve($url);
/data/core/classes/Core/User.php
     * @param int  $size Size of image to render in pixels.
     * @param bool $full Whether to use full site URL or not, for external loading - ie discord webhooks.
     *
     * @return string URL to their avatar image.
     */
    public function getAvatar(int $size = 128, bool $full = false): string
    {
        $data_obj = new stdClass();
        // Convert UserData object to stdClass so we can dynamically add the 'uuid' property
        foreach (get_object_vars($this->data()) as $key => $value) {
            $data_obj->{$key} = $value;
        }

        $integrationUser = $this->getIntegration('Minecraft');
        if ($integrationUser != null) {
            $data_obj->uuid = $integrationUser->data()->identifier;
        } else {
            $data_obj->uuid = '';
        }

        return AvatarSource::getAvatarFromUserData($data_obj, $this->hasPermission('usercp.gif_avatar'), $size, $full);
    }

    /**
     * Does the user have a specific permission in any of their groups?
     *
     * @param string $permission Permission node to check recursively for.
     *
     * @return bool Whether they inherit this permission or not.
     */
    public function hasPermission(string $permission): bool
    {
        if (!$this->exists()) {
            return false;
        }

        foreach ($this->getGroups() as $group) {
            $permissions = json_decode($group->permissions, true) ?? [];

            if (isset($permissions['administrator']) && $permissions['administrator'] == 1) {
                return true;
/data/modules/Forum/pages/forum/index.php
]);

// Get forums
$cache_name = 'forum_forums_' . rtrim(implode('-', $groups), '-');
$cache->setCache($cache_name);

if ($cache->isCached('forums')) {
    $forums = $cache->retrieve('forums');
} else {
    $forums = $forum->listAllForums($groups, ($user->isLoggedIn() ? $user->data()->id : 0));

    // Loop through to get last poster avatars and to format a date
    if (count($forums)) {
        foreach ($forums as $key => $item) {
            $forums[$key]['link'] = URL::build('/forum/view/' . urlencode($key) . '-' . $forum->titleToURL($item['title']));
            if (isset($item['subforums']) && count($item['subforums'])) {
                foreach ($item['subforums'] as $subforum_id => $subforum) {
                    if (isset($subforum->last_post)) {
                        $last_post_user = new User($forums[$key]['subforums'][$subforum_id]->last_post->post_creator);

                        $forums[$key]['subforums'][$subforum_id]->last_post->avatar = $last_post_user->getAvatar(64);
                        $forums[$key]['subforums'][$subforum_id]->last_post->user_style = $last_post_user->getGroupStyle();
                        $forums[$key]['subforums'][$subforum_id]->last_post->username = $last_post_user->getDisplayname();
                        $forums[$key]['subforums'][$subforum_id]->last_post->profile = $last_post_user->getProfileURL();

                        if (is_null($forums[$key]['subforums'][$subforum_id]->last_post->created)) {
                            $forums[$key]['subforums'][$subforum_id]->last_post->date_friendly = $timeAgo->inWords($forums[$key]['subforums'][$subforum_id]->last_post->post_date, $language);
                            $forums[$key]['subforums'][$subforum_id]->last_post->post_date = date(DATE_FORMAT, strtotime($forums[$key]['subforums'][$subforum_id]->last_post->post_date));
                        } else {
                            $forums[$key]['subforums'][$subforum_id]->last_post->date_friendly = $timeAgo->inWords($forums[$key]['subforums'][$subforum_id]->last_post->created, $language);
                            $forums[$key]['subforums'][$subforum_id]->last_post->post_date = date(DATE_FORMAT, $forums[$key]['subforums'][$subforum_id]->last_post->created);
                        }
                    }

                    if ($forums[$key]['subforums'][$subforum_id]->redirect_forum == 1 && URL::isExternalURL($forums[$key]['subforums'][$subforum_id]->redirect_url)) {
                        $forums[$key]['subforums'][$subforum_id]->redirect_confirm = $forum_language->get('forum', 'forum_redirect_warning', ['url' => $forums[$key]['subforums'][$subforum_id]->redirect_to]);
                    }
                }
            }
        }
    } else {
/data/index.php
            require(ROOT_PATH . '/modules/Core/pages/index.php');
        }
    }
    die;
}

$route = rtrim(strtok($_GET['route'], '?'), '/');

$all_pages = $pages->returnPages();

if (array_key_exists($route, $all_pages)) {
    $pages->setActivePage($all_pages[$route]);
    if (isset($all_pages[$route]['custom'])) {
        require(implode(DIRECTORY_SEPARATOR, [ROOT_PATH, 'modules', 'Core', 'pages', 'custom.php']));
        die;
    }

    $path = implode(DIRECTORY_SEPARATOR, [ROOT_PATH, 'modules', $all_pages[$route]['module'], $all_pages[$route]['file']]);

    if (file_exists($path)) {
        require($path);
        die;
    }
} else {
    // Use recursion to check - might have URL parameters in path
    $path_array = explode('/', $route);

    for ($i = count($path_array) - 2; $i > 0; $i--) {
        $new_path = '/';
        for ($n = 1; $n <= $i; $n++) {
            $new_path .= $path_array[$n] . '/';
        }

        $new_path = rtrim($new_path, '/');

        if (array_key_exists($new_path, $all_pages)) {
            $path = implode(DIRECTORY_SEPARATOR, [ROOT_PATH, 'modules', $all_pages[$new_path]['module'], $all_pages[$new_path]['file']]);

            if (file_exists($path)) {
                $pages->setActivePage($all_pages[$new_path]);
                require($path);
/data/core/classes/Core/User.php
SELECT nl2_groups.* FROM nl2_users_groups INNER JOIN nl2_groups ON group_id = nl2_groups.id WHERE user_id = '2' AND deleted = 0 ORDER BY `order`;
        ]);

        Session::delete($this->_admSessionName);
        Cookie::delete($this->_cookieName . '_adm');
    }

    /**
     * Get the user's groups.
     *
     * @return array<int, Group> Their groups.
     */
    public function getGroups(): array
    {
        if (isset($this->_groups)) {
            return $this->_groups;
        }

        if (isset(self::$_group_cache[$this->data()->id])) {
            $this->_groups = self::$_group_cache[$this->data()->id];
        } else {
            $groups_query = $this->_db->query('SELECT nl2_groups.* FROM nl2_users_groups INNER JOIN nl2_groups ON group_id = nl2_groups.id WHERE user_id = ? AND deleted = 0 ORDER BY `order`', [$this->data()->id]);
            if ($groups_query->count()) {
                foreach ($groups_query->results() as $item) {
                    $this->_groups[$item->id] = new Group($item);
                }
            } else {
                $this->_groups = [];
            }

            self::$_group_cache[$this->data()->id] = $this->_groups;
        }

        if (!count($this->_groups)) {
            // Get default group
            // TODO: Use PRE_VALIDATED_DEFAULT ?
            $default_group = Group::find(1, 'default_group');
            $default_group_id = $default_group->id ?? 1;

            $this->addGroup($default_group_id);
        }

/data/core/classes/Core/User.php
SELECT nl2_users_integrations.*, nl2_integrations.name as integration_name FROM nl2_users_integrations LEFT JOIN nl2_integrations ON integration_id=nl2_integrations.id WHERE user_id = '2';

        return $this->_groups;
    }

    /**
     * Get the user's integrations.
     *
     * @return IntegrationUser[] Their integrations.
     */
    public function getIntegrations(): array
    {
        if (isset($this->_integrations)) {
            return $this->_integrations;
        }

        $integrations = Integrations::getInstance();

        if (isset(self::$_integration_cache[$this->data()->id])) {
            $integrations_query = self::$_integration_cache[$this->data()->id];
        } else {
            $integrations_query = $this->_db->query('SELECT nl2_users_integrations.*, nl2_integrations.name as integration_name FROM nl2_users_integrations LEFT JOIN nl2_integrations ON integration_id=nl2_integrations.id WHERE user_id = ?', [$this->data()->id]);
            if ($integrations_query->count()) {
                $integrations_query = $integrations_query->results();
            } else {
                $integrations_query = [];
            }
            self::$_integration_cache[$this->data()->id] = $integrations_query;
        }

        $integrations_list = [];
        foreach ($integrations_query as $item) {
            $integration = $integrations->getIntegration($item->integration_name);
            if ($integration != null) {
                $integrationUser = new IntegrationUser($integration, $this->data()->id, 'user_id', $item);

                $integrations_list[$item->integration_name] = $integrationUser;
            }
        }

        return $this->_integrations = $integrations_list;
    }
/data/core/classes/Core/User.php
SELECT * FROM nl2_users WHERE `id` = '2';
    }

    /**
     * Find a user by unique identifier (username, ID, email, etc).
     * Loads instance variables for this class.
     *
     * @param string $value Unique identifier.
     * @param string $field What column to check for their unique identifier in.
     *
     * @return bool True/false on success or failure respectfully.
     */
    private function find(string $value, string $field = 'id'): bool
    {
        if (isset(self::$_user_cache["$value.$field"])) {
            $this->_data = self::$_user_cache["$value.$field"];

            return true;
        }

        if ($field !== 'hash') {
            $data = $this->_db->get('users', [$field, $value]);
        } else {
            $data = $this->_db->query(
                <<<'SQL'
                SELECT
                    nl2_users.*
                FROM nl2_users
                    LEFT JOIN nl2_users_session
                    ON nl2_users.id = nl2_users_session.user_id
                WHERE
                    nl2_users_session.hash = ?
                    AND nl2_users_session.active = 1
                    AND (
                        nl2_users_session.expires_at IS NULL
                        OR nl2_users_session.expires_at > ?
                    )
                SQL,
                [
                    $value,
                    time(),
                ]
/data/core/classes/Core/User.php
SELECT nl2_groups.* FROM nl2_users_groups INNER JOIN nl2_groups ON group_id = nl2_groups.id WHERE user_id = '1' AND deleted = 0 ORDER BY `order`;
        ]);

        Session::delete($this->_admSessionName);
        Cookie::delete($this->_cookieName . '_adm');
    }

    /**
     * Get the user's groups.
     *
     * @return array<int, Group> Their groups.
     */
    public function getGroups(): array
    {
        if (isset($this->_groups)) {
            return $this->_groups;
        }

        if (isset(self::$_group_cache[$this->data()->id])) {
            $this->_groups = self::$_group_cache[$this->data()->id];
        } else {
            $groups_query = $this->_db->query('SELECT nl2_groups.* FROM nl2_users_groups INNER JOIN nl2_groups ON group_id = nl2_groups.id WHERE user_id = ? AND deleted = 0 ORDER BY `order`', [$this->data()->id]);
            if ($groups_query->count()) {
                foreach ($groups_query->results() as $item) {
                    $this->_groups[$item->id] = new Group($item);
                }
            } else {
                $this->_groups = [];
            }

            self::$_group_cache[$this->data()->id] = $this->_groups;
        }

        if (!count($this->_groups)) {
            // Get default group
            // TODO: Use PRE_VALIDATED_DEFAULT ?
            $default_group = Group::find(1, 'default_group');
            $default_group_id = $default_group->id ?? 1;

            $this->addGroup($default_group_id);
        }

/data/core/classes/Core/User.php
SELECT nl2_users_integrations.*, nl2_integrations.name as integration_name FROM nl2_users_integrations LEFT JOIN nl2_integrations ON integration_id=nl2_integrations.id WHERE user_id = '1';

        return $this->_groups;
    }

    /**
     * Get the user's integrations.
     *
     * @return IntegrationUser[] Their integrations.
     */
    public function getIntegrations(): array
    {
        if (isset($this->_integrations)) {
            return $this->_integrations;
        }

        $integrations = Integrations::getInstance();

        if (isset(self::$_integration_cache[$this->data()->id])) {
            $integrations_query = self::$_integration_cache[$this->data()->id];
        } else {
            $integrations_query = $this->_db->query('SELECT nl2_users_integrations.*, nl2_integrations.name as integration_name FROM nl2_users_integrations LEFT JOIN nl2_integrations ON integration_id=nl2_integrations.id WHERE user_id = ?', [$this->data()->id]);
            if ($integrations_query->count()) {
                $integrations_query = $integrations_query->results();
            } else {
                $integrations_query = [];
            }
            self::$_integration_cache[$this->data()->id] = $integrations_query;
        }

        $integrations_list = [];
        foreach ($integrations_query as $item) {
            $integration = $integrations->getIntegration($item->integration_name);
            if ($integration != null) {
                $integrationUser = new IntegrationUser($integration, $this->data()->id, 'user_id', $item);

                $integrations_list[$item->integration_name] = $integrationUser;
            }
        }

        return $this->_integrations = $integrations_list;
    }
/data/core/classes/Core/User.php
SELECT * FROM nl2_users WHERE `id` = '1';
    }

    /**
     * Find a user by unique identifier (username, ID, email, etc).
     * Loads instance variables for this class.
     *
     * @param string $value Unique identifier.
     * @param string $field What column to check for their unique identifier in.
     *
     * @return bool True/false on success or failure respectfully.
     */
    private function find(string $value, string $field = 'id'): bool
    {
        if (isset(self::$_user_cache["$value.$field"])) {
            $this->_data = self::$_user_cache["$value.$field"];

            return true;
        }

        if ($field !== 'hash') {
            $data = $this->_db->get('users', [$field, $value]);
        } else {
            $data = $this->_db->query(
                <<<'SQL'
                SELECT
                    nl2_users.*
                FROM nl2_users
                    LEFT JOIN nl2_users_session
                    ON nl2_users.id = nl2_users_session.user_id
                WHERE
                    nl2_users_session.hash = ?
                    AND nl2_users_session.active = 1
                    AND (
                        nl2_users_session.expires_at IS NULL
                        OR nl2_users_session.expires_at > ?
                    )
                SQL,
                [
                    $value,
                    time(),
                ]
/data/modules/Forum/classes/Forum.php
SELECT COUNT(*) AS `count`
    FROM nl2_posts
    WHERE `forum_id` = '8'
      AND `deleted` = 0;
                                $return[$forum->id]['subforums'][$item->id]->subforums[$subforum->id] = new stdClass();
                                $return[$forum->id]['subforums'][$item->id]->subforums[$subforum->id]->title = $subforum->forum_title;
                                $return[$forum->id]['subforums'][$item->id]->subforums[$subforum->id]->link = URL::build('/forum/view/' . urlencode($subforum->id) . '-' . $this->titleToURL($subforum->forum_title));
                                $return[$forum->id]['subforums'][$item->id]->subforums[$subforum->id]->icon = $subforum->icon;
                            }
                        }

                        // Can the user view other topics?
                        if ($this->canViewOtherTopics($item->id, $groups)) {
                            $topics = $this->_db->query(
                                <<<SQL
                                    SELECT COUNT(*) AS `count`
                                    FROM nl2_topics
                                    WHERE
                                        `forum_id` = ?
                                      AND `deleted` = 0
                                SQL,
                                [$item->id]
                            )->first()->count;

                            $posts = $this->_db->query(
                                <<<SQL
                                    SELECT COUNT(*) AS `count`
                                    FROM nl2_posts
                                    WHERE `forum_id` = ?
                                      AND `deleted` = 0
                                SQL,
                                [$item->id]
                            )->first()->count;

                        } else {
                            $topics = $this->_db->query(
                                <<<SQL
                                    SELECT COUNT(*) AS `count`
                                    FROM nl2_topics
                                    WHERE `forum_id` = ?
                                      AND (`topic_creator` = ? OR `sticky` = 1)
                                      AND `deleted` = 0
                                SQL,
                                [$item->id, $user_id],
                            )->first()->count;
/data/modules/Forum/classes/Forum.php
SELECT COUNT(*) AS `count`
    FROM nl2_topics
    WHERE
        `forum_id` = '8'
      AND `deleted` = 0;
                            $latest_post = $this->getLatestPostInOwnTopicForum($item->id, $user_id);
                        }

                        if (count($subforums)) {
                            $return[$forum->id]['subforums'][$item->id]->subforums = [];

                            foreach ($subforums as $subforum) {
                                if (isset($subforum->last_post_date) && $subforum->last_post_date > $latest_post[0]) {
                                    $latest_post = [$subforum->last_post_date, $subforum->last_user_posted, $subforum->last_topic_posted];
                                }

                                $return[$forum->id]['subforums'][$item->id]->subforums[$subforum->id] = new stdClass();
                                $return[$forum->id]['subforums'][$item->id]->subforums[$subforum->id]->title = $subforum->forum_title;
                                $return[$forum->id]['subforums'][$item->id]->subforums[$subforum->id]->link = URL::build('/forum/view/' . urlencode($subforum->id) . '-' . $this->titleToURL($subforum->forum_title));
                                $return[$forum->id]['subforums'][$item->id]->subforums[$subforum->id]->icon = $subforum->icon;
                            }
                        }

                        // Can the user view other topics?
                        if ($this->canViewOtherTopics($item->id, $groups)) {
                            $topics = $this->_db->query(
                                <<<SQL
                                    SELECT COUNT(*) AS `count`
                                    FROM nl2_topics
                                    WHERE
                                        `forum_id` = ?
                                      AND `deleted` = 0
                                SQL,
                                [$item->id]
                            )->first()->count;

                            $posts = $this->_db->query(
                                <<<SQL
                                    SELECT COUNT(*) AS `count`
                                    FROM nl2_posts
                                    WHERE `forum_id` = ?
                                      AND `deleted` = 0
                                SQL,
                                [$item->id]
                            )->first()->count;

/data/modules/Forum/classes/Forum.php
SELECT * FROM nl2_forums_permissions WHERE `forum_id` = '8';
            }
        }
        return false;
    }

    public function titleToURL(string $topic = null): string {
        return URL::urlSafe($topic ?? '');
    }

    // Returns true/false depending on whether the current user can view a forum
    // Params: $forum_id (integer) - forum id to check, $groups (array) - user groups
    public function canViewOtherTopics(int $forum_id, array $groups = [0]): bool {
        $cache_key = 'topics_view_' . $forum_id . '_' . implode('_', $groups);
        if (isset(self::$_permission_cache[$cache_key])) {
            return true;
        }
        // Does the forum exist?
        $exists = $this->_db->get('forums', ['id', $forum_id])->results();
        if (count($exists)) {
            // Can the user view other topics?
            $access = $this->_db->get('forums_permissions', ['forum_id', $forum_id])->results();

            foreach ($access as $item) {
                if (in_array($item->group_id, $groups)) {
                    if ($item->view_other_topics == 1) {
                        self::$_permission_cache[$cache_key] = true;
                        return true;
                    }
                }
            }
        }

        return false;
    }

    /**
     * Get the newest x topics this user/group can view
     *
     * @param array $groups Array of groups the user is in
     * @param int $user_id User ID
     * @param int $limit Limit of topics to return, default 50
/data/modules/Forum/classes/Forum.php
SELECT * FROM nl2_forums WHERE `id` = '8';
                    self::$_permission_cache[$cache_key] = true;
                    return true;
                }
            }
        }
        return false;
    }

    public function titleToURL(string $topic = null): string {
        return URL::urlSafe($topic ?? '');
    }

    // Returns true/false depending on whether the current user can view a forum
    // Params: $forum_id (integer) - forum id to check, $groups (array) - user groups
    public function canViewOtherTopics(int $forum_id, array $groups = [0]): bool {
        $cache_key = 'topics_view_' . $forum_id . '_' . implode('_', $groups);
        if (isset(self::$_permission_cache[$cache_key])) {
            return true;
        }
        // Does the forum exist?
        $exists = $this->_db->get('forums', ['id', $forum_id])->results();
        if (count($exists)) {
            // Can the user view other topics?
            $access = $this->_db->get('forums_permissions', ['forum_id', $forum_id])->results();

            foreach ($access as $item) {
                if (in_array($item->group_id, $groups)) {
                    if ($item->view_other_topics == 1) {
                        self::$_permission_cache[$cache_key] = true;
                        return true;
                    }
                }
            }
        }

        return false;
    }

    /**
     * Get the newest x topics this user/group can view
     *
/data/modules/Forum/classes/Forum.php
SELECT
        f.*,
        EXISTS (
            SELECT p.ID
            FROM nl2_forums_permissions p
            WHERE p.group_id IN (0)
              AND p.forum_id = f.id
              AND p.view_other_topics = 1
        ) view_other_topics
    FROM nl2_forums f
    WHERE f.parent = '8'
      AND f.id IN
          (SELECT sp.forum_id
           FROM nl2_forums_permissions sp
           WHERE sp.group_id IN (0)
           AND sp.view = 1
           );
     * @param array $groups The user groups
     * @param int $depth The depth of the subforums to get
     * @param ?bool $onlyOwnTopics Whether to only get forums in which the user can only view their own topics (default false)
     * @param ?int $user_id Current user ID (default 0) - only used if $onlyOwnTopics is true
     * @return array Subforums at any level for a forum
     */
    public function getAnySubforums(
        int $forum_id,
        array $groups = [0],
        int $depth = 0,
        ?bool $onlyOwnTopics = false,
        ?int $user_id = 0
    ): array {
        if ($depth == 10) {
            return [];
        }

        $ret = [];
        $groups_in = implode(',', $groups);

        $subforums_query = $this->_db->query(
            <<<SQL
                SELECT
                    f.*,
                    EXISTS (
                        SELECT p.ID
                        FROM nl2_forums_permissions p
                        WHERE p.group_id IN ($groups_in)
                          AND p.forum_id = f.id
                          AND p.view_other_topics = 1
                    ) view_other_topics
                FROM nl2_forums f
                WHERE f.parent = ?
                  AND f.id IN
                      (SELECT sp.forum_id
                       FROM nl2_forums_permissions sp
                       WHERE sp.group_id IN ($groups_in)
                       AND sp.view = 1
                       )
            SQL,
            [$forum_id]
/data/modules/Forum/classes/Forum.php
SELECT t.*,
           p.id as pid,
           p.post_creator,
           p.created
    FROM nl2_topics t
        RIGHT JOIN nl2_posts p
            ON p.id =
               (SELECT ps.id
                FROM nl2_posts ps
                WHERE ps.topic_id = '5'
                  AND ps.deleted = 0
                ORDER BY ps.created DESC LIMIT 1
                )
    WHERE t.id = '5';
                                <<<SQL
                                    SELECT COUNT(*) AS `count`
                                    FROM nl2_posts
                                    WHERE `topic_id` IN
                                          (SELECT `id`
                                           FROM nl2_topics
                                           WHERE `forum_id` = ?
                                             AND (`topic_creator` = ? OR `sticky` = 1)
                                           )
                                      AND `deleted` = 0
                                SQL,
                                [$item->id, $user_id]
                            )->first()->count;
                        }

                        $return[$forum->id]['subforums'][$item->id]->topics = $topics;
                        $return[$forum->id]['subforums'][$item->id]->posts = $posts;

                        // Get latest topic info
                        if ($latest_post[0]) {
                            $latest_topic = $this->_db->query(
                                <<<SQL
                                    SELECT t.*,
                                           p.id as pid,
                                           p.post_creator,
                                           p.created
                                    FROM nl2_topics t
                                        RIGHT JOIN nl2_posts p
                                            ON p.id =
                                               (SELECT ps.id
                                                FROM nl2_posts ps
                                                WHERE ps.topic_id = ?
                                                  AND ps.deleted = 0
                                                ORDER BY ps.created DESC LIMIT 1
                                                )
                                    WHERE t.id = ?
                                SQL,
                                [$latest_post[2], $latest_post[2]]
                            );

                            if ($latest_topic->count() && $latest_topic = $latest_topic->first()) {
/data/modules/Forum/classes/Forum.php
SELECT COUNT(*) AS `count`
    FROM nl2_posts
    WHERE `forum_id` = '7'
      AND `deleted` = 0;
                                $return[$forum->id]['subforums'][$item->id]->subforums[$subforum->id] = new stdClass();
                                $return[$forum->id]['subforums'][$item->id]->subforums[$subforum->id]->title = $subforum->forum_title;
                                $return[$forum->id]['subforums'][$item->id]->subforums[$subforum->id]->link = URL::build('/forum/view/' . urlencode($subforum->id) . '-' . $this->titleToURL($subforum->forum_title));
                                $return[$forum->id]['subforums'][$item->id]->subforums[$subforum->id]->icon = $subforum->icon;
                            }
                        }

                        // Can the user view other topics?
                        if ($this->canViewOtherTopics($item->id, $groups)) {
                            $topics = $this->_db->query(
                                <<<SQL
                                    SELECT COUNT(*) AS `count`
                                    FROM nl2_topics
                                    WHERE
                                        `forum_id` = ?
                                      AND `deleted` = 0
                                SQL,
                                [$item->id]
                            )->first()->count;

                            $posts = $this->_db->query(
                                <<<SQL
                                    SELECT COUNT(*) AS `count`
                                    FROM nl2_posts
                                    WHERE `forum_id` = ?
                                      AND `deleted` = 0
                                SQL,
                                [$item->id]
                            )->first()->count;

                        } else {
                            $topics = $this->_db->query(
                                <<<SQL
                                    SELECT COUNT(*) AS `count`
                                    FROM nl2_topics
                                    WHERE `forum_id` = ?
                                      AND (`topic_creator` = ? OR `sticky` = 1)
                                      AND `deleted` = 0
                                SQL,
                                [$item->id, $user_id],
                            )->first()->count;
/data/modules/Forum/classes/Forum.php
SELECT COUNT(*) AS `count`
    FROM nl2_topics
    WHERE
        `forum_id` = '7'
      AND `deleted` = 0;
                            $latest_post = $this->getLatestPostInOwnTopicForum($item->id, $user_id);
                        }

                        if (count($subforums)) {
                            $return[$forum->id]['subforums'][$item->id]->subforums = [];

                            foreach ($subforums as $subforum) {
                                if (isset($subforum->last_post_date) && $subforum->last_post_date > $latest_post[0]) {
                                    $latest_post = [$subforum->last_post_date, $subforum->last_user_posted, $subforum->last_topic_posted];
                                }

                                $return[$forum->id]['subforums'][$item->id]->subforums[$subforum->id] = new stdClass();
                                $return[$forum->id]['subforums'][$item->id]->subforums[$subforum->id]->title = $subforum->forum_title;
                                $return[$forum->id]['subforums'][$item->id]->subforums[$subforum->id]->link = URL::build('/forum/view/' . urlencode($subforum->id) . '-' . $this->titleToURL($subforum->forum_title));
                                $return[$forum->id]['subforums'][$item->id]->subforums[$subforum->id]->icon = $subforum->icon;
                            }
                        }

                        // Can the user view other topics?
                        if ($this->canViewOtherTopics($item->id, $groups)) {
                            $topics = $this->_db->query(
                                <<<SQL
                                    SELECT COUNT(*) AS `count`
                                    FROM nl2_topics
                                    WHERE
                                        `forum_id` = ?
                                      AND `deleted` = 0
                                SQL,
                                [$item->id]
                            )->first()->count;

                            $posts = $this->_db->query(
                                <<<SQL
                                    SELECT COUNT(*) AS `count`
                                    FROM nl2_posts
                                    WHERE `forum_id` = ?
                                      AND `deleted` = 0
                                SQL,
                                [$item->id]
                            )->first()->count;

/data/modules/Forum/classes/Forum.php
SELECT * FROM nl2_forums_permissions WHERE `forum_id` = '7';
            }
        }
        return false;
    }

    public function titleToURL(string $topic = null): string {
        return URL::urlSafe($topic ?? '');
    }

    // Returns true/false depending on whether the current user can view a forum
    // Params: $forum_id (integer) - forum id to check, $groups (array) - user groups
    public function canViewOtherTopics(int $forum_id, array $groups = [0]): bool {
        $cache_key = 'topics_view_' . $forum_id . '_' . implode('_', $groups);
        if (isset(self::$_permission_cache[$cache_key])) {
            return true;
        }
        // Does the forum exist?
        $exists = $this->_db->get('forums', ['id', $forum_id])->results();
        if (count($exists)) {
            // Can the user view other topics?
            $access = $this->_db->get('forums_permissions', ['forum_id', $forum_id])->results();

            foreach ($access as $item) {
                if (in_array($item->group_id, $groups)) {
                    if ($item->view_other_topics == 1) {
                        self::$_permission_cache[$cache_key] = true;
                        return true;
                    }
                }
            }
        }

        return false;
    }

    /**
     * Get the newest x topics this user/group can view
     *
     * @param array $groups Array of groups the user is in
     * @param int $user_id User ID
     * @param int $limit Limit of topics to return, default 50
/data/modules/Forum/classes/Forum.php
SELECT * FROM nl2_forums WHERE `id` = '7';
                    self::$_permission_cache[$cache_key] = true;
                    return true;
                }
            }
        }
        return false;
    }

    public function titleToURL(string $topic = null): string {
        return URL::urlSafe($topic ?? '');
    }

    // Returns true/false depending on whether the current user can view a forum
    // Params: $forum_id (integer) - forum id to check, $groups (array) - user groups
    public function canViewOtherTopics(int $forum_id, array $groups = [0]): bool {
        $cache_key = 'topics_view_' . $forum_id . '_' . implode('_', $groups);
        if (isset(self::$_permission_cache[$cache_key])) {
            return true;
        }
        // Does the forum exist?
        $exists = $this->_db->get('forums', ['id', $forum_id])->results();
        if (count($exists)) {
            // Can the user view other topics?
            $access = $this->_db->get('forums_permissions', ['forum_id', $forum_id])->results();

            foreach ($access as $item) {
                if (in_array($item->group_id, $groups)) {
                    if ($item->view_other_topics == 1) {
                        self::$_permission_cache[$cache_key] = true;
                        return true;
                    }
                }
            }
        }

        return false;
    }

    /**
     * Get the newest x topics this user/group can view
     *
/data/modules/Forum/classes/Forum.php
SELECT
        f.*,
        EXISTS (
            SELECT p.ID
            FROM nl2_forums_permissions p
            WHERE p.group_id IN (0)
              AND p.forum_id = f.id
              AND p.view_other_topics = 1
        ) view_other_topics
    FROM nl2_forums f
    WHERE f.parent = '7'
      AND f.id IN
          (SELECT sp.forum_id
           FROM nl2_forums_permissions sp
           WHERE sp.group_id IN (0)
           AND sp.view = 1
           );
     * @param array $groups The user groups
     * @param int $depth The depth of the subforums to get
     * @param ?bool $onlyOwnTopics Whether to only get forums in which the user can only view their own topics (default false)
     * @param ?int $user_id Current user ID (default 0) - only used if $onlyOwnTopics is true
     * @return array Subforums at any level for a forum
     */
    public function getAnySubforums(
        int $forum_id,
        array $groups = [0],
        int $depth = 0,
        ?bool $onlyOwnTopics = false,
        ?int $user_id = 0
    ): array {
        if ($depth == 10) {
            return [];
        }

        $ret = [];
        $groups_in = implode(',', $groups);

        $subforums_query = $this->_db->query(
            <<<SQL
                SELECT
                    f.*,
                    EXISTS (
                        SELECT p.ID
                        FROM nl2_forums_permissions p
                        WHERE p.group_id IN ($groups_in)
                          AND p.forum_id = f.id
                          AND p.view_other_topics = 1
                    ) view_other_topics
                FROM nl2_forums f
                WHERE f.parent = ?
                  AND f.id IN
                      (SELECT sp.forum_id
                       FROM nl2_forums_permissions sp
                       WHERE sp.group_id IN ($groups_in)
                       AND sp.view = 1
                       )
            SQL,
            [$forum_id]
/data/modules/Forum/classes/Forum.php
SELECT
        f.*,
        EXISTS (
            SELECT p.ID
            FROM nl2_forums_permissions p
            WHERE p.group_id IN (0)
              AND p.forum_id = f.id
              AND p.view_other_topics = 1
        ) view_other_topics
    FROM nl2_forums AS f
    WHERE f.parent = '1'
      AND f.id IN 
          (SELECT fp.forum_id
           FROM nl2_forums_permissions AS fp
           WHERE fp.group_id IN (0)
           AND fp.view = 1
           )
    ORDER BY f.forum_order ASC;
                WHERE `parent` = 0
                  AND `id` IN 
                      (SELECT `forum_id`
                       FROM nl2_forums_permissions
                       WHERE `group_id` IN ($groups_in)
                       AND `view` = 1
                       )
                ORDER BY `forum_order` ASC
            SQL
        );

        $return = [];

        if ($parent_forums->count()) {
            foreach ($parent_forums->results() as $forum) {
                $return[$forum->id]['description'] = Output::getClean($forum->forum_description);
                $return[$forum->id]['title'] = Output::getClean($forum->forum_title);
                $return[$forum->id]['icon'] = Output::getPurified($forum->icon);

                // Get discussion forums
                $forums = $this->_db->query(
                    <<<SQL
                        SELECT
                            f.*,
                            EXISTS (
                                SELECT p.ID
                                FROM nl2_forums_permissions p
                                WHERE p.group_id IN ($groups_in)
                                  AND p.forum_id = f.id
                                  AND p.view_other_topics = 1
                            ) view_other_topics
                        FROM nl2_forums AS f
                        WHERE f.parent = ?
                          AND f.id IN 
                              (SELECT fp.forum_id
                               FROM nl2_forums_permissions AS fp
                               WHERE fp.group_id IN ($groups_in)
                               AND fp.view = 1
                               )
                        ORDER BY f.forum_order ASC
                    SQL,
/data/modules/Forum/classes/Forum.php
SELECT COUNT(*) AS `count`
    FROM nl2_posts
    WHERE `forum_id` = '6'
      AND `deleted` = 0;
                                $return[$forum->id]['subforums'][$item->id]->subforums[$subforum->id] = new stdClass();
                                $return[$forum->id]['subforums'][$item->id]->subforums[$subforum->id]->title = $subforum->forum_title;
                                $return[$forum->id]['subforums'][$item->id]->subforums[$subforum->id]->link = URL::build('/forum/view/' . urlencode($subforum->id) . '-' . $this->titleToURL($subforum->forum_title));
                                $return[$forum->id]['subforums'][$item->id]->subforums[$subforum->id]->icon = $subforum->icon;
                            }
                        }

                        // Can the user view other topics?
                        if ($this->canViewOtherTopics($item->id, $groups)) {
                            $topics = $this->_db->query(
                                <<<SQL
                                    SELECT COUNT(*) AS `count`
                                    FROM nl2_topics
                                    WHERE
                                        `forum_id` = ?
                                      AND `deleted` = 0
                                SQL,
                                [$item->id]
                            )->first()->count;

                            $posts = $this->_db->query(
                                <<<SQL
                                    SELECT COUNT(*) AS `count`
                                    FROM nl2_posts
                                    WHERE `forum_id` = ?
                                      AND `deleted` = 0
                                SQL,
                                [$item->id]
                            )->first()->count;

                        } else {
                            $topics = $this->_db->query(
                                <<<SQL
                                    SELECT COUNT(*) AS `count`
                                    FROM nl2_topics
                                    WHERE `forum_id` = ?
                                      AND (`topic_creator` = ? OR `sticky` = 1)
                                      AND `deleted` = 0
                                SQL,
                                [$item->id, $user_id],
                            )->first()->count;
/data/modules/Forum/classes/Forum.php
SELECT COUNT(*) AS `count`
    FROM nl2_topics
    WHERE
        `forum_id` = '6'
      AND `deleted` = 0;
                            $latest_post = $this->getLatestPostInOwnTopicForum($item->id, $user_id);
                        }

                        if (count($subforums)) {
                            $return[$forum->id]['subforums'][$item->id]->subforums = [];

                            foreach ($subforums as $subforum) {
                                if (isset($subforum->last_post_date) && $subforum->last_post_date > $latest_post[0]) {
                                    $latest_post = [$subforum->last_post_date, $subforum->last_user_posted, $subforum->last_topic_posted];
                                }

                                $return[$forum->id]['subforums'][$item->id]->subforums[$subforum->id] = new stdClass();
                                $return[$forum->id]['subforums'][$item->id]->subforums[$subforum->id]->title = $subforum->forum_title;
                                $return[$forum->id]['subforums'][$item->id]->subforums[$subforum->id]->link = URL::build('/forum/view/' . urlencode($subforum->id) . '-' . $this->titleToURL($subforum->forum_title));
                                $return[$forum->id]['subforums'][$item->id]->subforums[$subforum->id]->icon = $subforum->icon;
                            }
                        }

                        // Can the user view other topics?
                        if ($this->canViewOtherTopics($item->id, $groups)) {
                            $topics = $this->_db->query(
                                <<<SQL
                                    SELECT COUNT(*) AS `count`
                                    FROM nl2_topics
                                    WHERE
                                        `forum_id` = ?
                                      AND `deleted` = 0
                                SQL,
                                [$item->id]
                            )->first()->count;

                            $posts = $this->_db->query(
                                <<<SQL
                                    SELECT COUNT(*) AS `count`
                                    FROM nl2_posts
                                    WHERE `forum_id` = ?
                                      AND `deleted` = 0
                                SQL,
                                [$item->id]
                            )->first()->count;

/data/modules/Forum/classes/Forum.php
SELECT * FROM nl2_forums_permissions WHERE `forum_id` = '6';
            }
        }
        return false;
    }

    public function titleToURL(string $topic = null): string {
        return URL::urlSafe($topic ?? '');
    }

    // Returns true/false depending on whether the current user can view a forum
    // Params: $forum_id (integer) - forum id to check, $groups (array) - user groups
    public function canViewOtherTopics(int $forum_id, array $groups = [0]): bool {
        $cache_key = 'topics_view_' . $forum_id . '_' . implode('_', $groups);
        if (isset(self::$_permission_cache[$cache_key])) {
            return true;
        }
        // Does the forum exist?
        $exists = $this->_db->get('forums', ['id', $forum_id])->results();
        if (count($exists)) {
            // Can the user view other topics?
            $access = $this->_db->get('forums_permissions', ['forum_id', $forum_id])->results();

            foreach ($access as $item) {
                if (in_array($item->group_id, $groups)) {
                    if ($item->view_other_topics == 1) {
                        self::$_permission_cache[$cache_key] = true;
                        return true;
                    }
                }
            }
        }

        return false;
    }

    /**
     * Get the newest x topics this user/group can view
     *
     * @param array $groups Array of groups the user is in
     * @param int $user_id User ID
     * @param int $limit Limit of topics to return, default 50
/data/modules/Forum/classes/Forum.php
SELECT * FROM nl2_forums WHERE `id` = '6';
                    self::$_permission_cache[$cache_key] = true;
                    return true;
                }
            }
        }
        return false;
    }

    public function titleToURL(string $topic = null): string {
        return URL::urlSafe($topic ?? '');
    }

    // Returns true/false depending on whether the current user can view a forum
    // Params: $forum_id (integer) - forum id to check, $groups (array) - user groups
    public function canViewOtherTopics(int $forum_id, array $groups = [0]): bool {
        $cache_key = 'topics_view_' . $forum_id . '_' . implode('_', $groups);
        if (isset(self::$_permission_cache[$cache_key])) {
            return true;
        }
        // Does the forum exist?
        $exists = $this->_db->get('forums', ['id', $forum_id])->results();
        if (count($exists)) {
            // Can the user view other topics?
            $access = $this->_db->get('forums_permissions', ['forum_id', $forum_id])->results();

            foreach ($access as $item) {
                if (in_array($item->group_id, $groups)) {
                    if ($item->view_other_topics == 1) {
                        self::$_permission_cache[$cache_key] = true;
                        return true;
                    }
                }
            }
        }

        return false;
    }

    /**
     * Get the newest x topics this user/group can view
     *
/data/modules/Forum/classes/Forum.php
SELECT
        f.*,
        EXISTS (
            SELECT p.ID
            FROM nl2_forums_permissions p
            WHERE p.group_id IN (0)
              AND p.forum_id = f.id
              AND p.view_other_topics = 1
        ) view_other_topics
    FROM nl2_forums f
    WHERE f.parent = '6'
      AND f.id IN
          (SELECT sp.forum_id
           FROM nl2_forums_permissions sp
           WHERE sp.group_id IN (0)
           AND sp.view = 1
           );
     * @param array $groups The user groups
     * @param int $depth The depth of the subforums to get
     * @param ?bool $onlyOwnTopics Whether to only get forums in which the user can only view their own topics (default false)
     * @param ?int $user_id Current user ID (default 0) - only used if $onlyOwnTopics is true
     * @return array Subforums at any level for a forum
     */
    public function getAnySubforums(
        int $forum_id,
        array $groups = [0],
        int $depth = 0,
        ?bool $onlyOwnTopics = false,
        ?int $user_id = 0
    ): array {
        if ($depth == 10) {
            return [];
        }

        $ret = [];
        $groups_in = implode(',', $groups);

        $subforums_query = $this->_db->query(
            <<<SQL
                SELECT
                    f.*,
                    EXISTS (
                        SELECT p.ID
                        FROM nl2_forums_permissions p
                        WHERE p.group_id IN ($groups_in)
                          AND p.forum_id = f.id
                          AND p.view_other_topics = 1
                    ) view_other_topics
                FROM nl2_forums f
                WHERE f.parent = ?
                  AND f.id IN
                      (SELECT sp.forum_id
                       FROM nl2_forums_permissions sp
                       WHERE sp.group_id IN ($groups_in)
                       AND sp.view = 1
                       )
            SQL,
            [$forum_id]
/data/modules/Forum/classes/Forum.php
SELECT t.*,
           p.id as pid,
           p.post_creator,
           p.created
    FROM nl2_topics t
        RIGHT JOIN nl2_posts p
            ON p.id =
               (SELECT ps.id
                FROM nl2_posts ps
                WHERE ps.topic_id = '7'
                  AND ps.deleted = 0
                ORDER BY ps.created DESC LIMIT 1
                )
    WHERE t.id = '7';
                                <<<SQL
                                    SELECT COUNT(*) AS `count`
                                    FROM nl2_posts
                                    WHERE `topic_id` IN
                                          (SELECT `id`
                                           FROM nl2_topics
                                           WHERE `forum_id` = ?
                                             AND (`topic_creator` = ? OR `sticky` = 1)
                                           )
                                      AND `deleted` = 0
                                SQL,
                                [$item->id, $user_id]
                            )->first()->count;
                        }

                        $return[$forum->id]['subforums'][$item->id]->topics = $topics;
                        $return[$forum->id]['subforums'][$item->id]->posts = $posts;

                        // Get latest topic info
                        if ($latest_post[0]) {
                            $latest_topic = $this->_db->query(
                                <<<SQL
                                    SELECT t.*,
                                           p.id as pid,
                                           p.post_creator,
                                           p.created
                                    FROM nl2_topics t
                                        RIGHT JOIN nl2_posts p
                                            ON p.id =
                                               (SELECT ps.id
                                                FROM nl2_posts ps
                                                WHERE ps.topic_id = ?
                                                  AND ps.deleted = 0
                                                ORDER BY ps.created DESC LIMIT 1
                                                )
                                    WHERE t.id = ?
                                SQL,
                                [$latest_post[2], $latest_post[2]]
                            );

                            if ($latest_topic->count() && $latest_topic = $latest_topic->first()) {
/data/modules/Forum/classes/Forum.php
SELECT COUNT(*) AS `count`
    FROM nl2_posts
    WHERE `forum_id` = '5'
      AND `deleted` = 0;
                                $return[$forum->id]['subforums'][$item->id]->subforums[$subforum->id] = new stdClass();
                                $return[$forum->id]['subforums'][$item->id]->subforums[$subforum->id]->title = $subforum->forum_title;
                                $return[$forum->id]['subforums'][$item->id]->subforums[$subforum->id]->link = URL::build('/forum/view/' . urlencode($subforum->id) . '-' . $this->titleToURL($subforum->forum_title));
                                $return[$forum->id]['subforums'][$item->id]->subforums[$subforum->id]->icon = $subforum->icon;
                            }
                        }

                        // Can the user view other topics?
                        if ($this->canViewOtherTopics($item->id, $groups)) {
                            $topics = $this->_db->query(
                                <<<SQL
                                    SELECT COUNT(*) AS `count`
                                    FROM nl2_topics
                                    WHERE
                                        `forum_id` = ?
                                      AND `deleted` = 0
                                SQL,
                                [$item->id]
                            )->first()->count;

                            $posts = $this->_db->query(
                                <<<SQL
                                    SELECT COUNT(*) AS `count`
                                    FROM nl2_posts
                                    WHERE `forum_id` = ?
                                      AND `deleted` = 0
                                SQL,
                                [$item->id]
                            )->first()->count;

                        } else {
                            $topics = $this->_db->query(
                                <<<SQL
                                    SELECT COUNT(*) AS `count`
                                    FROM nl2_topics
                                    WHERE `forum_id` = ?
                                      AND (`topic_creator` = ? OR `sticky` = 1)
                                      AND `deleted` = 0
                                SQL,
                                [$item->id, $user_id],
                            )->first()->count;
/data/modules/Forum/classes/Forum.php
SELECT COUNT(*) AS `count`
    FROM nl2_topics
    WHERE
        `forum_id` = '5'
      AND `deleted` = 0;
                            $latest_post = $this->getLatestPostInOwnTopicForum($item->id, $user_id);
                        }

                        if (count($subforums)) {
                            $return[$forum->id]['subforums'][$item->id]->subforums = [];

                            foreach ($subforums as $subforum) {
                                if (isset($subforum->last_post_date) && $subforum->last_post_date > $latest_post[0]) {
                                    $latest_post = [$subforum->last_post_date, $subforum->last_user_posted, $subforum->last_topic_posted];
                                }

                                $return[$forum->id]['subforums'][$item->id]->subforums[$subforum->id] = new stdClass();
                                $return[$forum->id]['subforums'][$item->id]->subforums[$subforum->id]->title = $subforum->forum_title;
                                $return[$forum->id]['subforums'][$item->id]->subforums[$subforum->id]->link = URL::build('/forum/view/' . urlencode($subforum->id) . '-' . $this->titleToURL($subforum->forum_title));
                                $return[$forum->id]['subforums'][$item->id]->subforums[$subforum->id]->icon = $subforum->icon;
                            }
                        }

                        // Can the user view other topics?
                        if ($this->canViewOtherTopics($item->id, $groups)) {
                            $topics = $this->_db->query(
                                <<<SQL
                                    SELECT COUNT(*) AS `count`
                                    FROM nl2_topics
                                    WHERE
                                        `forum_id` = ?
                                      AND `deleted` = 0
                                SQL,
                                [$item->id]
                            )->first()->count;

                            $posts = $this->_db->query(
                                <<<SQL
                                    SELECT COUNT(*) AS `count`
                                    FROM nl2_posts
                                    WHERE `forum_id` = ?
                                      AND `deleted` = 0
                                SQL,
                                [$item->id]
                            )->first()->count;

/data/modules/Forum/classes/Forum.php
SELECT * FROM nl2_forums_permissions WHERE `forum_id` = '5';
            }
        }
        return false;
    }

    public function titleToURL(string $topic = null): string {
        return URL::urlSafe($topic ?? '');
    }

    // Returns true/false depending on whether the current user can view a forum
    // Params: $forum_id (integer) - forum id to check, $groups (array) - user groups
    public function canViewOtherTopics(int $forum_id, array $groups = [0]): bool {
        $cache_key = 'topics_view_' . $forum_id . '_' . implode('_', $groups);
        if (isset(self::$_permission_cache[$cache_key])) {
            return true;
        }
        // Does the forum exist?
        $exists = $this->_db->get('forums', ['id', $forum_id])->results();
        if (count($exists)) {
            // Can the user view other topics?
            $access = $this->_db->get('forums_permissions', ['forum_id', $forum_id])->results();

            foreach ($access as $item) {
                if (in_array($item->group_id, $groups)) {
                    if ($item->view_other_topics == 1) {
                        self::$_permission_cache[$cache_key] = true;
                        return true;
                    }
                }
            }
        }

        return false;
    }

    /**
     * Get the newest x topics this user/group can view
     *
     * @param array $groups Array of groups the user is in
     * @param int $user_id User ID
     * @param int $limit Limit of topics to return, default 50
/data/modules/Forum/classes/Forum.php
SELECT * FROM nl2_forums WHERE `id` = '5';
                    self::$_permission_cache[$cache_key] = true;
                    return true;
                }
            }
        }
        return false;
    }

    public function titleToURL(string $topic = null): string {
        return URL::urlSafe($topic ?? '');
    }

    // Returns true/false depending on whether the current user can view a forum
    // Params: $forum_id (integer) - forum id to check, $groups (array) - user groups
    public function canViewOtherTopics(int $forum_id, array $groups = [0]): bool {
        $cache_key = 'topics_view_' . $forum_id . '_' . implode('_', $groups);
        if (isset(self::$_permission_cache[$cache_key])) {
            return true;
        }
        // Does the forum exist?
        $exists = $this->_db->get('forums', ['id', $forum_id])->results();
        if (count($exists)) {
            // Can the user view other topics?
            $access = $this->_db->get('forums_permissions', ['forum_id', $forum_id])->results();

            foreach ($access as $item) {
                if (in_array($item->group_id, $groups)) {
                    if ($item->view_other_topics == 1) {
                        self::$_permission_cache[$cache_key] = true;
                        return true;
                    }
                }
            }
        }

        return false;
    }

    /**
     * Get the newest x topics this user/group can view
     *
/data/modules/Forum/classes/Forum.php
SELECT
        f.*,
        EXISTS (
            SELECT p.ID
            FROM nl2_forums_permissions p
            WHERE p.group_id IN (0)
              AND p.forum_id = f.id
              AND p.view_other_topics = 1
        ) view_other_topics
    FROM nl2_forums f
    WHERE f.parent = '5'
      AND f.id IN
          (SELECT sp.forum_id
           FROM nl2_forums_permissions sp
           WHERE sp.group_id IN (0)
           AND sp.view = 1
           );
     * @param array $groups The user groups
     * @param int $depth The depth of the subforums to get
     * @param ?bool $onlyOwnTopics Whether to only get forums in which the user can only view their own topics (default false)
     * @param ?int $user_id Current user ID (default 0) - only used if $onlyOwnTopics is true
     * @return array Subforums at any level for a forum
     */
    public function getAnySubforums(
        int $forum_id,
        array $groups = [0],
        int $depth = 0,
        ?bool $onlyOwnTopics = false,
        ?int $user_id = 0
    ): array {
        if ($depth == 10) {
            return [];
        }

        $ret = [];
        $groups_in = implode(',', $groups);

        $subforums_query = $this->_db->query(
            <<<SQL
                SELECT
                    f.*,
                    EXISTS (
                        SELECT p.ID
                        FROM nl2_forums_permissions p
                        WHERE p.group_id IN ($groups_in)
                          AND p.forum_id = f.id
                          AND p.view_other_topics = 1
                    ) view_other_topics
                FROM nl2_forums f
                WHERE f.parent = ?
                  AND f.id IN
                      (SELECT sp.forum_id
                       FROM nl2_forums_permissions sp
                       WHERE sp.group_id IN ($groups_in)
                       AND sp.view = 1
                       )
            SQL,
            [$forum_id]
/data/modules/Forum/classes/Forum.php
SELECT t.*,
           p.id as pid,
           p.post_creator,
           p.created
    FROM nl2_topics t
        RIGHT JOIN nl2_posts p
            ON p.id =
               (SELECT ps.id
                FROM nl2_posts ps
                WHERE ps.topic_id = '1'
                  AND ps.deleted = 0
                ORDER BY ps.created DESC LIMIT 1
                )
    WHERE t.id = '1';
                                <<<SQL
                                    SELECT COUNT(*) AS `count`
                                    FROM nl2_posts
                                    WHERE `topic_id` IN
                                          (SELECT `id`
                                           FROM nl2_topics
                                           WHERE `forum_id` = ?
                                             AND (`topic_creator` = ? OR `sticky` = 1)
                                           )
                                      AND `deleted` = 0
                                SQL,
                                [$item->id, $user_id]
                            )->first()->count;
                        }

                        $return[$forum->id]['subforums'][$item->id]->topics = $topics;
                        $return[$forum->id]['subforums'][$item->id]->posts = $posts;

                        // Get latest topic info
                        if ($latest_post[0]) {
                            $latest_topic = $this->_db->query(
                                <<<SQL
                                    SELECT t.*,
                                           p.id as pid,
                                           p.post_creator,
                                           p.created
                                    FROM nl2_topics t
                                        RIGHT JOIN nl2_posts p
                                            ON p.id =
                                               (SELECT ps.id
                                                FROM nl2_posts ps
                                                WHERE ps.topic_id = ?
                                                  AND ps.deleted = 0
                                                ORDER BY ps.created DESC LIMIT 1
                                                )
                                    WHERE t.id = ?
                                SQL,
                                [$latest_post[2], $latest_post[2]]
                            );

                            if ($latest_topic->count() && $latest_topic = $latest_topic->first()) {
/data/modules/Forum/classes/Forum.php
SELECT COUNT(*) AS `count`
    FROM nl2_posts
    WHERE `forum_id` = '2'
      AND `deleted` = 0;
                                $return[$forum->id]['subforums'][$item->id]->subforums[$subforum->id] = new stdClass();
                                $return[$forum->id]['subforums'][$item->id]->subforums[$subforum->id]->title = $subforum->forum_title;
                                $return[$forum->id]['subforums'][$item->id]->subforums[$subforum->id]->link = URL::build('/forum/view/' . urlencode($subforum->id) . '-' . $this->titleToURL($subforum->forum_title));
                                $return[$forum->id]['subforums'][$item->id]->subforums[$subforum->id]->icon = $subforum->icon;
                            }
                        }

                        // Can the user view other topics?
                        if ($this->canViewOtherTopics($item->id, $groups)) {
                            $topics = $this->_db->query(
                                <<<SQL
                                    SELECT COUNT(*) AS `count`
                                    FROM nl2_topics
                                    WHERE
                                        `forum_id` = ?
                                      AND `deleted` = 0
                                SQL,
                                [$item->id]
                            )->first()->count;

                            $posts = $this->_db->query(
                                <<<SQL
                                    SELECT COUNT(*) AS `count`
                                    FROM nl2_posts
                                    WHERE `forum_id` = ?
                                      AND `deleted` = 0
                                SQL,
                                [$item->id]
                            )->first()->count;

                        } else {
                            $topics = $this->_db->query(
                                <<<SQL
                                    SELECT COUNT(*) AS `count`
                                    FROM nl2_topics
                                    WHERE `forum_id` = ?
                                      AND (`topic_creator` = ? OR `sticky` = 1)
                                      AND `deleted` = 0
                                SQL,
                                [$item->id, $user_id],
                            )->first()->count;
/data/modules/Forum/classes/Forum.php
SELECT COUNT(*) AS `count`
    FROM nl2_topics
    WHERE
        `forum_id` = '2'
      AND `deleted` = 0;
                            $latest_post = $this->getLatestPostInOwnTopicForum($item->id, $user_id);
                        }

                        if (count($subforums)) {
                            $return[$forum->id]['subforums'][$item->id]->subforums = [];

                            foreach ($subforums as $subforum) {
                                if (isset($subforum->last_post_date) && $subforum->last_post_date > $latest_post[0]) {
                                    $latest_post = [$subforum->last_post_date, $subforum->last_user_posted, $subforum->last_topic_posted];
                                }

                                $return[$forum->id]['subforums'][$item->id]->subforums[$subforum->id] = new stdClass();
                                $return[$forum->id]['subforums'][$item->id]->subforums[$subforum->id]->title = $subforum->forum_title;
                                $return[$forum->id]['subforums'][$item->id]->subforums[$subforum->id]->link = URL::build('/forum/view/' . urlencode($subforum->id) . '-' . $this->titleToURL($subforum->forum_title));
                                $return[$forum->id]['subforums'][$item->id]->subforums[$subforum->id]->icon = $subforum->icon;
                            }
                        }

                        // Can the user view other topics?
                        if ($this->canViewOtherTopics($item->id, $groups)) {
                            $topics = $this->_db->query(
                                <<<SQL
                                    SELECT COUNT(*) AS `count`
                                    FROM nl2_topics
                                    WHERE
                                        `forum_id` = ?
                                      AND `deleted` = 0
                                SQL,
                                [$item->id]
                            )->first()->count;

                            $posts = $this->_db->query(
                                <<<SQL
                                    SELECT COUNT(*) AS `count`
                                    FROM nl2_posts
                                    WHERE `forum_id` = ?
                                      AND `deleted` = 0
                                SQL,
                                [$item->id]
                            )->first()->count;

/data/modules/Forum/classes/Forum.php
SELECT * FROM nl2_forums_permissions WHERE `forum_id` = '2';
            }
        }
        return false;
    }

    public function titleToURL(string $topic = null): string {
        return URL::urlSafe($topic ?? '');
    }

    // Returns true/false depending on whether the current user can view a forum
    // Params: $forum_id (integer) - forum id to check, $groups (array) - user groups
    public function canViewOtherTopics(int $forum_id, array $groups = [0]): bool {
        $cache_key = 'topics_view_' . $forum_id . '_' . implode('_', $groups);
        if (isset(self::$_permission_cache[$cache_key])) {
            return true;
        }
        // Does the forum exist?
        $exists = $this->_db->get('forums', ['id', $forum_id])->results();
        if (count($exists)) {
            // Can the user view other topics?
            $access = $this->_db->get('forums_permissions', ['forum_id', $forum_id])->results();

            foreach ($access as $item) {
                if (in_array($item->group_id, $groups)) {
                    if ($item->view_other_topics == 1) {
                        self::$_permission_cache[$cache_key] = true;
                        return true;
                    }
                }
            }
        }

        return false;
    }

    /**
     * Get the newest x topics this user/group can view
     *
     * @param array $groups Array of groups the user is in
     * @param int $user_id User ID
     * @param int $limit Limit of topics to return, default 50
/data/modules/Forum/classes/Forum.php
SELECT * FROM nl2_forums WHERE `id` = '2';
                    self::$_permission_cache[$cache_key] = true;
                    return true;
                }
            }
        }
        return false;
    }

    public function titleToURL(string $topic = null): string {
        return URL::urlSafe($topic ?? '');
    }

    // Returns true/false depending on whether the current user can view a forum
    // Params: $forum_id (integer) - forum id to check, $groups (array) - user groups
    public function canViewOtherTopics(int $forum_id, array $groups = [0]): bool {
        $cache_key = 'topics_view_' . $forum_id . '_' . implode('_', $groups);
        if (isset(self::$_permission_cache[$cache_key])) {
            return true;
        }
        // Does the forum exist?
        $exists = $this->_db->get('forums', ['id', $forum_id])->results();
        if (count($exists)) {
            // Can the user view other topics?
            $access = $this->_db->get('forums_permissions', ['forum_id', $forum_id])->results();

            foreach ($access as $item) {
                if (in_array($item->group_id, $groups)) {
                    if ($item->view_other_topics == 1) {
                        self::$_permission_cache[$cache_key] = true;
                        return true;
                    }
                }
            }
        }

        return false;
    }

    /**
     * Get the newest x topics this user/group can view
     *
/data/modules/Forum/classes/Forum.php
SELECT
        f.*,
        EXISTS (
            SELECT p.ID
            FROM nl2_forums_permissions p
            WHERE p.group_id IN (0)
              AND p.forum_id = f.id
              AND p.view_other_topics = 1
        ) view_other_topics
    FROM nl2_forums f
    WHERE f.parent = '2'
      AND f.id IN
          (SELECT sp.forum_id
           FROM nl2_forums_permissions sp
           WHERE sp.group_id IN (0)
           AND sp.view = 1
           );
     * @param array $groups The user groups
     * @param int $depth The depth of the subforums to get
     * @param ?bool $onlyOwnTopics Whether to only get forums in which the user can only view their own topics (default false)
     * @param ?int $user_id Current user ID (default 0) - only used if $onlyOwnTopics is true
     * @return array Subforums at any level for a forum
     */
    public function getAnySubforums(
        int $forum_id,
        array $groups = [0],
        int $depth = 0,
        ?bool $onlyOwnTopics = false,
        ?int $user_id = 0
    ): array {
        if ($depth == 10) {
            return [];
        }

        $ret = [];
        $groups_in = implode(',', $groups);

        $subforums_query = $this->_db->query(
            <<<SQL
                SELECT
                    f.*,
                    EXISTS (
                        SELECT p.ID
                        FROM nl2_forums_permissions p
                        WHERE p.group_id IN ($groups_in)
                          AND p.forum_id = f.id
                          AND p.view_other_topics = 1
                    ) view_other_topics
                FROM nl2_forums f
                WHERE f.parent = ?
                  AND f.id IN
                      (SELECT sp.forum_id
                       FROM nl2_forums_permissions sp
                       WHERE sp.group_id IN ($groups_in)
                       AND sp.view = 1
                       )
            SQL,
            [$forum_id]
/data/modules/Forum/classes/Forum.php
SELECT t.*,
           p.id as pid,
           p.post_creator,
           p.created
    FROM nl2_topics t
        RIGHT JOIN nl2_posts p
            ON p.id =
               (SELECT ps.id
                FROM nl2_posts ps
                WHERE ps.topic_id = '6'
                  AND ps.deleted = 0
                ORDER BY ps.created DESC LIMIT 1
                )
    WHERE t.id = '6';
                                <<<SQL
                                    SELECT COUNT(*) AS `count`
                                    FROM nl2_posts
                                    WHERE `topic_id` IN
                                          (SELECT `id`
                                           FROM nl2_topics
                                           WHERE `forum_id` = ?
                                             AND (`topic_creator` = ? OR `sticky` = 1)
                                           )
                                      AND `deleted` = 0
                                SQL,
                                [$item->id, $user_id]
                            )->first()->count;
                        }

                        $return[$forum->id]['subforums'][$item->id]->topics = $topics;
                        $return[$forum->id]['subforums'][$item->id]->posts = $posts;

                        // Get latest topic info
                        if ($latest_post[0]) {
                            $latest_topic = $this->_db->query(
                                <<<SQL
                                    SELECT t.*,
                                           p.id as pid,
                                           p.post_creator,
                                           p.created
                                    FROM nl2_topics t
                                        RIGHT JOIN nl2_posts p
                                            ON p.id =
                                               (SELECT ps.id
                                                FROM nl2_posts ps
                                                WHERE ps.topic_id = ?
                                                  AND ps.deleted = 0
                                                ORDER BY ps.created DESC LIMIT 1
                                                )
                                    WHERE t.id = ?
                                SQL,
                                [$latest_post[2], $latest_post[2]]
                            );

                            if ($latest_topic->count() && $latest_topic = $latest_topic->first()) {
/data/modules/Forum/classes/Forum.php
SELECT COUNT(*) AS `count`
    FROM nl2_posts
    WHERE `forum_id` = '4'
      AND `deleted` = 0;
                                $return[$forum->id]['subforums'][$item->id]->subforums[$subforum->id] = new stdClass();
                                $return[$forum->id]['subforums'][$item->id]->subforums[$subforum->id]->title = $subforum->forum_title;
                                $return[$forum->id]['subforums'][$item->id]->subforums[$subforum->id]->link = URL::build('/forum/view/' . urlencode($subforum->id) . '-' . $this->titleToURL($subforum->forum_title));
                                $return[$forum->id]['subforums'][$item->id]->subforums[$subforum->id]->icon = $subforum->icon;
                            }
                        }

                        // Can the user view other topics?
                        if ($this->canViewOtherTopics($item->id, $groups)) {
                            $topics = $this->_db->query(
                                <<<SQL
                                    SELECT COUNT(*) AS `count`
                                    FROM nl2_topics
                                    WHERE
                                        `forum_id` = ?
                                      AND `deleted` = 0
                                SQL,
                                [$item->id]
                            )->first()->count;

                            $posts = $this->_db->query(
                                <<<SQL
                                    SELECT COUNT(*) AS `count`
                                    FROM nl2_posts
                                    WHERE `forum_id` = ?
                                      AND `deleted` = 0
                                SQL,
                                [$item->id]
                            )->first()->count;

                        } else {
                            $topics = $this->_db->query(
                                <<<SQL
                                    SELECT COUNT(*) AS `count`
                                    FROM nl2_topics
                                    WHERE `forum_id` = ?
                                      AND (`topic_creator` = ? OR `sticky` = 1)
                                      AND `deleted` = 0
                                SQL,
                                [$item->id, $user_id],
                            )->first()->count;
/data/modules/Forum/classes/Forum.php
SELECT COUNT(*) AS `count`
    FROM nl2_topics
    WHERE
        `forum_id` = '4'
      AND `deleted` = 0;
                            $latest_post = $this->getLatestPostInOwnTopicForum($item->id, $user_id);
                        }

                        if (count($subforums)) {
                            $return[$forum->id]['subforums'][$item->id]->subforums = [];

                            foreach ($subforums as $subforum) {
                                if (isset($subforum->last_post_date) && $subforum->last_post_date > $latest_post[0]) {
                                    $latest_post = [$subforum->last_post_date, $subforum->last_user_posted, $subforum->last_topic_posted];
                                }

                                $return[$forum->id]['subforums'][$item->id]->subforums[$subforum->id] = new stdClass();
                                $return[$forum->id]['subforums'][$item->id]->subforums[$subforum->id]->title = $subforum->forum_title;
                                $return[$forum->id]['subforums'][$item->id]->subforums[$subforum->id]->link = URL::build('/forum/view/' . urlencode($subforum->id) . '-' . $this->titleToURL($subforum->forum_title));
                                $return[$forum->id]['subforums'][$item->id]->subforums[$subforum->id]->icon = $subforum->icon;
                            }
                        }

                        // Can the user view other topics?
                        if ($this->canViewOtherTopics($item->id, $groups)) {
                            $topics = $this->_db->query(
                                <<<SQL
                                    SELECT COUNT(*) AS `count`
                                    FROM nl2_topics
                                    WHERE
                                        `forum_id` = ?
                                      AND `deleted` = 0
                                SQL,
                                [$item->id]
                            )->first()->count;

                            $posts = $this->_db->query(
                                <<<SQL
                                    SELECT COUNT(*) AS `count`
                                    FROM nl2_posts
                                    WHERE `forum_id` = ?
                                      AND `deleted` = 0
                                SQL,
                                [$item->id]
                            )->first()->count;

/data/modules/Forum/classes/Forum.php
SELECT * FROM nl2_forums_permissions WHERE `forum_id` = '4';
            }
        }
        return false;
    }

    public function titleToURL(string $topic = null): string {
        return URL::urlSafe($topic ?? '');
    }

    // Returns true/false depending on whether the current user can view a forum
    // Params: $forum_id (integer) - forum id to check, $groups (array) - user groups
    public function canViewOtherTopics(int $forum_id, array $groups = [0]): bool {
        $cache_key = 'topics_view_' . $forum_id . '_' . implode('_', $groups);
        if (isset(self::$_permission_cache[$cache_key])) {
            return true;
        }
        // Does the forum exist?
        $exists = $this->_db->get('forums', ['id', $forum_id])->results();
        if (count($exists)) {
            // Can the user view other topics?
            $access = $this->_db->get('forums_permissions', ['forum_id', $forum_id])->results();

            foreach ($access as $item) {
                if (in_array($item->group_id, $groups)) {
                    if ($item->view_other_topics == 1) {
                        self::$_permission_cache[$cache_key] = true;
                        return true;
                    }
                }
            }
        }

        return false;
    }

    /**
     * Get the newest x topics this user/group can view
     *
     * @param array $groups Array of groups the user is in
     * @param int $user_id User ID
     * @param int $limit Limit of topics to return, default 50
/data/modules/Forum/classes/Forum.php
SELECT * FROM nl2_forums WHERE `id` = '4';
                    self::$_permission_cache[$cache_key] = true;
                    return true;
                }
            }
        }
        return false;
    }

    public function titleToURL(string $topic = null): string {
        return URL::urlSafe($topic ?? '');
    }

    // Returns true/false depending on whether the current user can view a forum
    // Params: $forum_id (integer) - forum id to check, $groups (array) - user groups
    public function canViewOtherTopics(int $forum_id, array $groups = [0]): bool {
        $cache_key = 'topics_view_' . $forum_id . '_' . implode('_', $groups);
        if (isset(self::$_permission_cache[$cache_key])) {
            return true;
        }
        // Does the forum exist?
        $exists = $this->_db->get('forums', ['id', $forum_id])->results();
        if (count($exists)) {
            // Can the user view other topics?
            $access = $this->_db->get('forums_permissions', ['forum_id', $forum_id])->results();

            foreach ($access as $item) {
                if (in_array($item->group_id, $groups)) {
                    if ($item->view_other_topics == 1) {
                        self::$_permission_cache[$cache_key] = true;
                        return true;
                    }
                }
            }
        }

        return false;
    }

    /**
     * Get the newest x topics this user/group can view
     *
/data/modules/Forum/classes/Forum.php
SELECT
        f.*,
        EXISTS (
            SELECT p.ID
            FROM nl2_forums_permissions p
            WHERE p.group_id IN (0)
              AND p.forum_id = f.id
              AND p.view_other_topics = 1
        ) view_other_topics
    FROM nl2_forums f
    WHERE f.parent = '4'
      AND f.id IN
          (SELECT sp.forum_id
           FROM nl2_forums_permissions sp
           WHERE sp.group_id IN (0)
           AND sp.view = 1
           );
     * @param array $groups The user groups
     * @param int $depth The depth of the subforums to get
     * @param ?bool $onlyOwnTopics Whether to only get forums in which the user can only view their own topics (default false)
     * @param ?int $user_id Current user ID (default 0) - only used if $onlyOwnTopics is true
     * @return array Subforums at any level for a forum
     */
    public function getAnySubforums(
        int $forum_id,
        array $groups = [0],
        int $depth = 0,
        ?bool $onlyOwnTopics = false,
        ?int $user_id = 0
    ): array {
        if ($depth == 10) {
            return [];
        }

        $ret = [];
        $groups_in = implode(',', $groups);

        $subforums_query = $this->_db->query(
            <<<SQL
                SELECT
                    f.*,
                    EXISTS (
                        SELECT p.ID
                        FROM nl2_forums_permissions p
                        WHERE p.group_id IN ($groups_in)
                          AND p.forum_id = f.id
                          AND p.view_other_topics = 1
                    ) view_other_topics
                FROM nl2_forums f
                WHERE f.parent = ?
                  AND f.id IN
                      (SELECT sp.forum_id
                       FROM nl2_forums_permissions sp
                       WHERE sp.group_id IN ($groups_in)
                       AND sp.view = 1
                       )
            SQL,
            [$forum_id]
/data/modules/Forum/classes/Forum.php
SELECT
        f.*,
        EXISTS (
            SELECT p.ID
            FROM nl2_forums_permissions p
            WHERE p.group_id IN (0)
              AND p.forum_id = f.id
              AND p.view_other_topics = 1
        ) view_other_topics
    FROM nl2_forums AS f
    WHERE f.parent = '3'
      AND f.id IN 
          (SELECT fp.forum_id
           FROM nl2_forums_permissions AS fp
           WHERE fp.group_id IN (0)
           AND fp.view = 1
           )
    ORDER BY f.forum_order ASC;
                WHERE `parent` = 0
                  AND `id` IN 
                      (SELECT `forum_id`
                       FROM nl2_forums_permissions
                       WHERE `group_id` IN ($groups_in)
                       AND `view` = 1
                       )
                ORDER BY `forum_order` ASC
            SQL
        );

        $return = [];

        if ($parent_forums->count()) {
            foreach ($parent_forums->results() as $forum) {
                $return[$forum->id]['description'] = Output::getClean($forum->forum_description);
                $return[$forum->id]['title'] = Output::getClean($forum->forum_title);
                $return[$forum->id]['icon'] = Output::getPurified($forum->icon);

                // Get discussion forums
                $forums = $this->_db->query(
                    <<<SQL
                        SELECT
                            f.*,
                            EXISTS (
                                SELECT p.ID
                                FROM nl2_forums_permissions p
                                WHERE p.group_id IN ($groups_in)
                                  AND p.forum_id = f.id
                                  AND p.view_other_topics = 1
                            ) view_other_topics
                        FROM nl2_forums AS f
                        WHERE f.parent = ?
                          AND f.id IN 
                              (SELECT fp.forum_id
                               FROM nl2_forums_permissions AS fp
                               WHERE fp.group_id IN ($groups_in)
                               AND fp.view = 1
                               )
                        ORDER BY f.forum_order ASC
                    SQL,
/data/modules/Forum/classes/Forum.php
SELECT *
    FROM `nl2_forums`
    WHERE `parent` = 0
      AND `id` IN 
          (SELECT `forum_id`
           FROM nl2_forums_permissions
           WHERE `group_id` IN (0)
           AND `view` = 1
           )
    ORDER BY `forum_order` ASC;

    /**
     * Get an array of forums a user can access, including topic information
     *
     * @param array $groups Users groups
     * @param int $user_id User ID
     * @return array Array of forums a user can access
     */
    public function listAllForums(array $groups = [0], int $user_id = 0): array {
        if (in_array(0, $groups)) {
            $user_id = 0;
        }

        if (!$user_id) {
            $user_id = 0;
        }

        $groups_in = implode(',', $groups);

        // Get a list of parent forums
        $parent_forums = $this->_db->query(
            <<<SQL
                SELECT *
                FROM `nl2_forums`
                WHERE `parent` = 0
                  AND `id` IN 
                      (SELECT `forum_id`
                       FROM nl2_forums_permissions
                       WHERE `group_id` IN ($groups_in)
                       AND `view` = 1
                       )
                ORDER BY `forum_order` ASC
            SQL
        );

        $return = [];

        if ($parent_forums->count()) {
            foreach ($parent_forums->results() as $forum) {
                $return[$forum->id]['description'] = Output::getClean($forum->forum_description);
                $return[$forum->id]['title'] = Output::getClean($forum->forum_title);
/data/core/templates/frontend_init.php
SELECT * FROM nl2_page_descriptions WHERE `page` = '/forum';
        $default_group = $cache->retrieve('default_group');
    } else {
        try {
            $default_group = Group::find(1, 'default_group')->id;
        } catch (Exception $e) {
            $default_group = 1;
        }

        $cache->store('default_group', $default_group);
    }
}

// Page metadata
if (isset($_GET['route']) && $_GET['route'] != '/') {
    $route = rtrim($_GET['route'], '/');
} else {
    $route = '/';
}

if (!defined('PAGE_DESCRIPTION')) {
    $page_metadata = DB::getInstance()->get('page_descriptions', ['page', $route]);
    if ($page_metadata->count()) {
        $page_metadata = $page_metadata->first();
        $template->getEngine()->addVariables([
            'PAGE_DESCRIPTION' => str_replace('{site}', Output::getClean(SITE_NAME), addslashes(strip_tags($page_metadata->description))),
            'PAGE_KEYWORDS' => addslashes(strip_tags($page_metadata->tags)),
        ]);

        $og_image = $page_metadata->image;
        if ($og_image) {
            $template->getEngine()->addVariable('OG_IMAGE', rtrim(URL::getSelfURL(), '/') . $og_image);
        }
    } else {
        $template->getEngine()->addVariables([
            'PAGE_DESCRIPTION' => str_replace('{site}', Output::getClean(SITE_NAME), addslashes(strip_tags(Settings::get('default_meta_description', '')))),
            'PAGE_KEYWORDS' => addslashes(strip_tags(Settings::get('default_meta_keywords', ''))),
        ]);
    }
} else {
    $template->getEngine()->addVariables([
        'PAGE_DESCRIPTION' => str_replace('{site}', Output::getClean(SITE_NAME), addslashes(strip_tags(PAGE_DESCRIPTION))),
/data/core/classes/Core/Settings.php
SELECT `name`, `value` FROM `nl2_settings` WHERE `module` = 'Tailwind';
        $cache_name = $module !== null ? $module : 'core';
        self::$_cached_settings[$cache_name] = $cache;
    }

    /**
     * Get a setting from the database table `nl2_settings`.
     *
     * @param  string  $setting  Setting to check.
     * @param  ?string $fallback Fallback to return if $setting is not set in DB. Defaults to null.
     * @param  string  $module   Module name to keep settings separate from other modules. Set module
     *                           to 'Core' for global settings.
     * @return ?string Setting from DB or $fallback.
     */
    public static function get(string $setting, ?string $fallback = null, string $module = 'core'): ?string
    {
        if (!self::hasSettingsCache($module)) {
            // Load all settings for this module and store it as a dictionary
            if ($module === 'core') {
                $result = DB::getInstance()->query('SELECT `name`, `value` FROM `nl2_settings` WHERE `module` IS NULL')->results();
            } else {
                $result = DB::getInstance()->query('SELECT `name`, `value` FROM `nl2_settings` WHERE `module` = ?', [$module])->results();
            }

            $cache = [];
            foreach ($result as $row) {
                $cache[$row->name] = $row->value;
            }
            self::setSettingsCache($module, $cache);
        }

        $cache = &self::getSettingsCache($module);

        return $cache[$setting] ?? $fallback;
    }

    /**
     * Modify a setting in the database table `nl2_settings`.
     *
     * @param string      $setting   Setting name.
     * @param string|null $new_value New setting value, or null to delete
     * @param string      $module    Module name to keep settings separate from other modules. Set module
/data/core/init.php
SELECT * FROM nl2_groups WHERE `default_group` = '1';
                            : [DiscordHook::class, 'execute'],
                        'events' => json_decode($hook->events, true),
                    ];
                }
                $cache->store('hooks', $hook_array);
            }
        }
    }
    EventHandler::registerWebhooks($hook_array);

    // Get IP
    $ip = HttpUtils::getRemoteAddress();

    // Define default group pre validation
    $cache->setCache('pre_validation_default');
    $group_id = null;

    if ($cache->isCached('pre_validation_default')) {
        $group_id = $cache->retrieve('pre_validation_default');
    } else {
        $group_id = DB::getInstance()->get('groups', ['default_group', '1'])->results();
        $group_id = $group_id[0]->id;
    }

    define('PRE_VALIDATED_DEFAULT', $group_id);

    // Perform tasks if the user is logged in
    if ($user->isLoggedIn()) {
        Debugging::setCanViewDetailedError($user->hasPermission('admincp.errors'));
        Debugging::setCanGenerateDebugLink($user->hasPermission('admincp.core.debugging'));

        // Ensure a user is not banned
        if ($user->data()->isbanned == 1) {
            $user->logout();
            Session::flash('home_error', $language->get('user', 'you_have_been_banned'));
            Redirect::to(URL::build('/'));
        }

        // Is the IP address banned?
        $ip_bans = DB::getInstance()->get('ip_bans', ['ip', $ip])->results();
        if (count($ip_bans)) {
/data/modules/Lists/module.php
SELECT vanity_url FROM `nl2_lists_submissions` WHERE `vanity_url` IS NOT NULL;
        EventHandler::registerEvent(SubmissionVotedEvent::class);
        EventHandler::registerEvent(SubmissionBumpedEvent::class);
        EventHandler::registerEvent(SubmissionFollowedEvent::class);
        EventHandler::registerEvent(SubmissionUnfollowedEvent::class);

        // Store Listener
        EventHandler::registerListener(PaymentCompletedEvent::class, [StorePaymentListener::class, 'paymentCompleted']);

        // Register page for each lists
        $lists = DB::getInstance()->query('SELECT * FROM nl2_lists WHERE provider IS NOT NULL AND url IS NOT NULL');
        if ($lists->count()) {
            foreach ($lists->results() as $list) {
                // Register page
                $pages->add('Lists', $list->url, 'pages/submissions.php', 'lists-' . $list->id, true);
                $pages->add('Lists', $list->url . '/tags', 'pages/submissions_tags.php', 'lists-' . $list->id, true);
                $pages->add('Lists', $list->url . '/new', 'pages/new_submission.php', 'lists-' . $list->id);
            }
        }

        // Register page for each submission that has own vanity url
        $submissions = DB::getInstance()->query('SELECT vanity_url FROM `nl2_lists_submissions` WHERE `vanity_url` IS NOT NULL');
        foreach ($submissions->results() as $submission) {
            $pages->add('Lists', '/' . $submission->vanity_url, 'pages/submission.php');
        }
    }

    public function onInstall() {
        PhinxAdapter::migrate($this->getName(), __DIR__ . '/includes/migrations');

        mkdir(ROOT_PATH . '/uploads/lists_icons');
        mkdir(ROOT_PATH . '/uploads/lists_images');
        mkdir(ROOT_PATH . '/uploads/lists_banners');
    }

    public function onUninstall() {
        PhinxAdapter::rollback($this->getName(), __DIR__ . '/includes/migrations');
    }

    public function onEnable() {
        PhinxAdapter::migrate($this->getName(), __DIR__ . '/includes/migrations');

/data/modules/Lists/module.php
SELECT * FROM nl2_lists WHERE provider IS NOT NULL AND url IS NOT NULL;
        Lists::getInstance()->registerProviderConnection(new DefaultConnection());
        Lists::getInstance()->registerProviderConnection(new MinecraftConnection());
        Lists::getInstance()->registerProviderConnection(new VotifierConnection());
        Lists::getInstance()->registerProviderConnection(new MCStatisticsConnection());
        Lists::getInstance()->registerProviderConnection(new DiscordConnection());
        Lists::getInstance()->registerProviderConnection(new NamelessMCConnection());
        Lists::getInstance()->registerProviderConnection(new BF2142Connection());

        EventHandler::registerEvent(SubmissionCreatedEvent::class);
        EventHandler::registerEvent(SubmissionUpdatedEvent::class);
        EventHandler::registerEvent(SubmissionDeletedEvent::class);
        EventHandler::registerEvent(SubmissionVotedEvent::class);
        EventHandler::registerEvent(SubmissionBumpedEvent::class);
        EventHandler::registerEvent(SubmissionFollowedEvent::class);
        EventHandler::registerEvent(SubmissionUnfollowedEvent::class);

        // Store Listener
        EventHandler::registerListener(PaymentCompletedEvent::class, [StorePaymentListener::class, 'paymentCompleted']);

        // Register page for each lists
        $lists = DB::getInstance()->query('SELECT * FROM nl2_lists WHERE provider IS NOT NULL AND url IS NOT NULL');
        if ($lists->count()) {
            foreach ($lists->results() as $list) {
                // Register page
                $pages->add('Lists', $list->url, 'pages/submissions.php', 'lists-' . $list->id, true);
                $pages->add('Lists', $list->url . '/tags', 'pages/submissions_tags.php', 'lists-' . $list->id, true);
                $pages->add('Lists', $list->url . '/new', 'pages/new_submission.php', 'lists-' . $list->id);
            }
        }

        // Register page for each submission that has own vanity url
        $submissions = DB::getInstance()->query('SELECT vanity_url FROM `nl2_lists_submissions` WHERE `vanity_url` IS NOT NULL');
        foreach ($submissions->results() as $submission) {
            $pages->add('Lists', '/' . $submission->vanity_url, 'pages/submission.php');
        }
    }

    public function onInstall() {
        PhinxAdapter::migrate($this->getName(), __DIR__ . '/includes/migrations');

        mkdir(ROOT_PATH . '/uploads/lists_icons');
/data/core/classes/Database/PhinxAdapter.php
SELECT version, migration_name FROM nl2_phinxlog_lists;

        if (!$migrationDir) {
            $migrationDir = __DIR__ . '/../../migrations';
        }

        $migration_files = array_map(
            static function ($file_name) {
                [$version, $migration_name] = explode('_', $file_name, 2);
                $migration_name = str_replace(['.php', '_'], '', ucwords($migration_name, '_'));

                return $version . '_' . $migration_name;
            },
            array_filter(scandir($migrationDir), static function ($file_name) {
                // Pattern that matches Phinx migration file names (eg: 20230403000000_create_stroopwafel_table.php)
                return preg_match('/^\d{14}_\w+\.php$/', $file_name);
            }),
        );

        $migration_database_entries = array_map(static function ($row) {
            return $row->version . '_' . $row->migration_name;
        }, DB::getInstance()->query("SELECT version, migration_name FROM $table")->results());

        $missing = array_diff($migration_files, $migration_database_entries);
        $extra = array_diff($migration_database_entries, $migration_files);

        if ($returnResults) {
            return [
                'missing' => count($missing),
                'extra' => count($extra),
            ];
        }

        // Likely a pull from the repo dev branch or migrations
        // weren't run during an upgrade script.
        if (($missing_count = count($missing)) > 0) {
            echo "There are $missing_count migrations files which have not been executed:" . '<br>';
            foreach ($missing as $missing_migration) {
                echo " - $missing_migration" . '<br>';
            }
        }

/data/core/classes/Integrations/IntegrationBase.php
SELECT * FROM nl2_integrations WHERE name = 'Discord';
 * @author Partydragen
 * @version 2.1.0
 * @license MIT
 */
abstract class IntegrationBase
{
    private DB $_db;
    private IntegrationData $_data;
    protected string $_icon;
    private array $_errors = [];
    protected Language $_language;
    protected ?string $_settings = null;

    protected string $_name;
    protected ?int $_order;

    public function __construct()
    {
        $this->_db = DB::getInstance();

        $integration = $this->_db->query('SELECT * FROM nl2_integrations WHERE name = ?', [$this->_name]);
        if ($integration->count()) {
            $integration = $integration->first();

            $this->_data = new IntegrationData($integration);
            $this->_order = $integration->order;
        } else {
            // Register integration to database
            $this->_db->query('INSERT INTO nl2_integrations (name) VALUES (?)', [
                $this->_name,
            ]);

            $integration = $this->_db->query('SELECT * FROM nl2_integrations WHERE name = ?', [$this->_name])->first();

            $this->_data = new IntegrationData($integration);
            $this->_order = $integration->order;
        }
    }

    /**
     * Get the name of this integration.
/data/core/classes/Core/Settings.php
SELECT `name`, `value` FROM `nl2_settings` WHERE `module` = 'Store';
        $cache_name = $module !== null ? $module : 'core';
        self::$_cached_settings[$cache_name] = $cache;
    }

    /**
     * Get a setting from the database table `nl2_settings`.
     *
     * @param  string  $setting  Setting to check.
     * @param  ?string $fallback Fallback to return if $setting is not set in DB. Defaults to null.
     * @param  string  $module   Module name to keep settings separate from other modules. Set module
     *                           to 'Core' for global settings.
     * @return ?string Setting from DB or $fallback.
     */
    public static function get(string $setting, ?string $fallback = null, string $module = 'core'): ?string
    {
        if (!self::hasSettingsCache($module)) {
            // Load all settings for this module and store it as a dictionary
            if ($module === 'core') {
                $result = DB::getInstance()->query('SELECT `name`, `value` FROM `nl2_settings` WHERE `module` IS NULL')->results();
            } else {
                $result = DB::getInstance()->query('SELECT `name`, `value` FROM `nl2_settings` WHERE `module` = ?', [$module])->results();
            }

            $cache = [];
            foreach ($result as $row) {
                $cache[$row->name] = $row->value;
            }
            self::setSettingsCache($module, $cache);
        }

        $cache = &self::getSettingsCache($module);

        return $cache[$setting] ?? $fallback;
    }

    /**
     * Modify a setting in the database table `nl2_settings`.
     *
     * @param string      $setting   Setting name.
     * @param string|null $new_value New setting value, or null to delete
     * @param string      $module    Module name to keep settings separate from other modules. Set module
/data/core/classes/Core/Settings.php
SELECT `name`, `value` FROM `nl2_settings` WHERE `module` = 'Referrals';
        $cache_name = $module !== null ? $module : 'core';
        self::$_cached_settings[$cache_name] = $cache;
    }

    /**
     * Get a setting from the database table `nl2_settings`.
     *
     * @param  string  $setting  Setting to check.
     * @param  ?string $fallback Fallback to return if $setting is not set in DB. Defaults to null.
     * @param  string  $module   Module name to keep settings separate from other modules. Set module
     *                           to 'Core' for global settings.
     * @return ?string Setting from DB or $fallback.
     */
    public static function get(string $setting, ?string $fallback = null, string $module = 'core'): ?string
    {
        if (!self::hasSettingsCache($module)) {
            // Load all settings for this module and store it as a dictionary
            if ($module === 'core') {
                $result = DB::getInstance()->query('SELECT `name`, `value` FROM `nl2_settings` WHERE `module` IS NULL')->results();
            } else {
                $result = DB::getInstance()->query('SELECT `name`, `value` FROM `nl2_settings` WHERE `module` = ?', [$module])->results();
            }

            $cache = [];
            foreach ($result as $row) {
                $cache[$row->name] = $row->value;
            }
            self::setSettingsCache($module, $cache);
        }

        $cache = &self::getSettingsCache($module);

        return $cache[$setting] ?? $fallback;
    }

    /**
     * Modify a setting in the database table `nl2_settings`.
     *
     * @param string      $setting   Setting name.
     * @param string|null $new_value New setting value, or null to delete
     * @param string      $module    Module name to keep settings separate from other modules. Set module
/data/modules/Referrals/module.php
SELECT id, custom_url FROM nl2_referrals WHERE disabled = 0 AND custom_url IS NOT NULL;

        // Check if module version changed
        $cache->setCache('referrals_module_cache');
        if (!$cache->isCached('module_version')) {
            $cache->store('module_version', $module_version);
        } else {
            if ($module_version != $cache->retrieve('module_version')) {
                // Version have changed, Perform actions
                $this->initialiseUpdate($cache->retrieve('module_version'));

                $cache->store('module_version', $module_version);

                if ($cache->isCached('update_check')) {
                    $cache->erase('update_check');
                }
            }
        }

        try {
            // Define URLs which belong to this module
            $referrals = $this->_db->query('SELECT id, custom_url FROM nl2_referrals WHERE disabled = 0 AND custom_url IS NOT NULL');
            if ($referrals->count()) {
                foreach ($referrals->results() as $referral) {
                    $pages->add('Referrals', '/' . $referral->custom_url, 'pages/referral.php');
                }
            }

        } catch (Exception $e) {
            // Database tables don't exist yet
        }

        // Events
        EventHandler::registerEvent(ReferralRegistrationEvent::class);

        // Listeners
        EventHandler::registerListener(UserRegisteredEvent::class, [RegistrationListener::class, 'registration']);
        EventHandler::registerListener(UserValidatedEvent::class, [RegistrationListener::class, 'userValidated']);
        EventHandler::registerListener(DiscordWebhookFormatterEvent::class, [RegistrationListener::class, 'discordEmbedFormatter']);

        // Store integration
        $percentage = Settings::get('store_sale_percentage', '0', 'Referrals');
/data/modules/Forms/module.php
SELECT form_id FROM nl2_forms_permissions WHERE form_id = '1' AND post = 1 AND group_id IN(0);

        try {
            $forms = $this->_db->query('SELECT id, link_location, url, icon, title, guest FROM nl2_forms')->results();
            if (count($forms)) {
                if ($user->isLoggedIn()) {
                    $group_ids = implode(',', $user->getAllGroupIds());
                } else {
                    $group_ids = implode(',', array(0));
                }

                foreach ($forms as $form) {
                    // Register form page
                    $pages->add('Forms', $form->url, 'pages/form.php', 'form-' . $form->id, true);

                    $perm = false;
                    if (!$user->isLoggedIn() && $form->guest == 1) {
                        $perm = true;
                    }

                    if (!$perm) {
                        $hasperm = $this->_db->query('SELECT form_id FROM nl2_forms_permissions WHERE form_id = ? AND post = 1 AND group_id IN(' . $group_ids . ')', array($form->id));
                        if ($hasperm->count()) {
                            $perm = true;
                        }
                    }

                    // Check cache first
                    $cache->setCache('navbar_order');
                    if (!$cache->isCached('form-' . $form->id . '_order')) {
                        // Create cache entry now
                        $form_order = 5;
                        $cache->store('form-' . $form->id . '_order', 5);
                    } else {
                        $form_order = $cache->retrieve('form-' . $form->id . '_order');
                    }

                    // Add link location to navigation if user have permission
                    if ($perm) {
                        switch ($form->link_location) {
                            case 1:
                                // Navbar
/data/modules/Forms/module.php
SELECT id, link_location, url, icon, title, guest FROM nl2_forms;
        $pages->add('Forms', '/user/submissions', 'pages/user/submissions.php');

        // Check if module version changed
        $cache->setCache('forms_module_cache');
        if (!$cache->isCached('module_version')) {
            $cache->store('module_version', $module_version);
        } else {
            if ($module_version != $cache->retrieve('module_version')) {
                // Version have changed, Perform actions
                $this->initialiseUpdate($cache->retrieve('module_version'));

                $cache->store('module_version', $module_version);

                if ($cache->isCached('update_check')) {
                    $cache->erase('update_check');
                }
            }
        }

        try {
            $forms = $this->_db->query('SELECT id, link_location, url, icon, title, guest FROM nl2_forms')->results();
            if (count($forms)) {
                if ($user->isLoggedIn()) {
                    $group_ids = implode(',', $user->getAllGroupIds());
                } else {
                    $group_ids = implode(',', array(0));
                }

                foreach ($forms as $form) {
                    // Register form page
                    $pages->add('Forms', $form->url, 'pages/form.php', 'form-' . $form->id, true);

                    $perm = false;
                    if (!$user->isLoggedIn() && $form->guest == 1) {
                        $perm = true;
                    }

                    if (!$perm) {
                        $hasperm = $this->_db->query('SELECT form_id FROM nl2_forms_permissions WHERE form_id = ? AND post = 1 AND group_id IN(' . $group_ids . ')', array($form->id));
                        if ($hasperm->count()) {
                            $perm = true;
/data/modules/OAuth2/module.php
SELECT * FROM nl2_oauth2_applications WHERE nameless = 1 AND enabled = 1;

        // Check if module version changed
        $cache->setCache('oauth2_module_cache');
        if (!$cache->isCached('module_version')) {
            $cache->store('module_version', $module_version);
        } else {
            if ($module_version != $cache->retrieve('module_version')) {
                // Version have changed, Perform actions
                $this->initialiseUpdate($cache->retrieve('module_version'));

                $cache->store('module_version', $module_version);

                if ($cache->isCached('update_check')) {
                    $cache->erase('update_check');
                }
            }
        }

        try {
            // Register integrations for namelessmc applications
            $applications = $this->_db->query("SELECT * FROM nl2_oauth2_applications WHERE nameless = 1 AND enabled = 1")->results();
            foreach ($applications as $app) {
                $application = new Application(null, null, $app);
                Integrations::getInstance()->registerIntegration(new ApplicationIntegration($language, $application));

                NamelessOAuth::getInstance()->registerProvider(strtolower($application->getName()), 'OAuth2', [
                    'class' => NamelessProvider::class,
                    'user_id_name' => 'id',
                    'scope_id_name' => 'identify',
                    'icon' => 'fa-solid fa-globe',
                    'verify_email' => static fn () => true,
                ]);

                // Register group sync for namelessmc application if enabled
                if ($application->data()->group_sync) {
                    GroupSyncManager::getInstance()->registerInjector(new ApplicationGroupSyncInjector($application));
                }
            }
        } catch (Exception $e) {
            // Database tables don't exist yet
        }
/data/core/classes/Core/Module.php
SELECT * FROM nl2_modules WHERE `name` = 'Core';

    /**
     * Get this module's ID.
     *
     * @return int The ID for the module
     */
    public function getId(): int
    {
        return DB::getInstance()->query('SELECT `id` FROM nl2_modules WHERE `name` = ?', [$this->_name])->first()->id;
    }

    /**
     * Get a module ID from name.
     *
     * @param string $name Module name
     *
     * @return ?int Module ID
     */
    public static function getIdFromName(string $name): ?int
    {
        $query = DB::getInstance()->get('modules', ['name', $name]);

        if ($query->count()) {
            return $query->first()->id;
        }

        return null;
    }

    /**
     * Get a module name from ID.
     *
     * @param int $id Module ID
     *
     * @return ?string Module name
     */
    public static function getNameFromId(int $id): ?string
    {
        $query = DB::getInstance()->get('modules', ['id', $id]);

        if ($query->count()) {
/data/core/classes/Integrations/IntegrationBase.php
SELECT * FROM nl2_integrations WHERE name = 'Google';
 * @author Partydragen
 * @version 2.1.0
 * @license MIT
 */
abstract class IntegrationBase
{
    private DB $_db;
    private IntegrationData $_data;
    protected string $_icon;
    private array $_errors = [];
    protected Language $_language;
    protected ?string $_settings = null;

    protected string $_name;
    protected ?int $_order;

    public function __construct()
    {
        $this->_db = DB::getInstance();

        $integration = $this->_db->query('SELECT * FROM nl2_integrations WHERE name = ?', [$this->_name]);
        if ($integration->count()) {
            $integration = $integration->first();

            $this->_data = new IntegrationData($integration);
            $this->_order = $integration->order;
        } else {
            // Register integration to database
            $this->_db->query('INSERT INTO nl2_integrations (name) VALUES (?)', [
                $this->_name,
            ]);

            $integration = $this->_db->query('SELECT * FROM nl2_integrations WHERE name = ?', [$this->_name])->first();

            $this->_data = new IntegrationData($integration);
            $this->_order = $integration->order;
        }
    }

    /**
     * Get the name of this integration.
/data/core/classes/Integrations/IntegrationBase.php
SELECT * FROM nl2_integrations WHERE name = 'Minecraft';
 * @author Partydragen
 * @version 2.1.0
 * @license MIT
 */
abstract class IntegrationBase
{
    private DB $_db;
    private IntegrationData $_data;
    protected string $_icon;
    private array $_errors = [];
    protected Language $_language;
    protected ?string $_settings = null;

    protected string $_name;
    protected ?int $_order;

    public function __construct()
    {
        $this->_db = DB::getInstance();

        $integration = $this->_db->query('SELECT * FROM nl2_integrations WHERE name = ?', [$this->_name]);
        if ($integration->count()) {
            $integration = $integration->first();

            $this->_data = new IntegrationData($integration);
            $this->_order = $integration->order;
        } else {
            // Register integration to database
            $this->_db->query('INSERT INTO nl2_integrations (name) VALUES (?)', [
                $this->_name,
            ]);

            $integration = $this->_db->query('SELECT * FROM nl2_integrations WHERE name = ?', [$this->_name])->first();

            $this->_data = new IntegrationData($integration);
            $this->_order = $integration->order;
        }
    }

    /**
     * Get the name of this integration.
/data/modules/Core/module.php
SELECT * FROM nl2_custom_pages_permissions WHERE `group_id` = '0';
                                                $navigation->add(
                                                    $custom_page->id,
                                                    Output::getClean($custom_page->title),
                                                    (is_null($redirect)) ? URL::build(Output::urlEncodeAllowSlashes($custom_page->url)) : $redirect,
                                                    'footer', $custom_page->target ? '_blank' : null,
                                                    2000,
                                                    $custom_page->icon
                                                );
                                                break;
                                        }
                                        break 2;
                                    }

                                    break;
                                }
                            }
                        }
                    }
                }
            } else {
                $custom_page_permissions = DB::getInstance()->get('custom_pages_permissions', ['group_id', 0])->results();
                if (count($custom_page_permissions)) {
                    foreach ($custom_pages as $custom_page) {
                        $redirect = null;

                        if ($custom_page->redirect == 1) {
                            $redirect = Output::getClean($custom_page->link);
                        }

                        $pages->addCustom(Output::urlEncodeAllowSlashes($custom_page->url), Output::getClean($custom_page->title), !$custom_page->basic);

                        foreach ($custom_page_permissions as $permission) {
                            if ($permission->page_id == $custom_page->id) {
                                if ($permission->view == 1) {
                                    // Check cache for order
                                    if (!$cache->isCached($custom_page->id . '_order')) {
                                        // Create cache entry now
                                        $page_order = 200;
                                        $cache->store($custom_page->id . '_order', 200);
                                    } else {
                                        $page_order = $cache->retrieve($custom_page->id . '_order');
/data/modules/Core/module.php
SELECT * FROM nl2_custom_pages WHERE `id` <> '0';
        }

        // "More" dropdown
        $cache->setCache('navbar_icons');
        if ($cache->isCached('more_dropdown_icon')) {
            $icon = $cache->retrieve('more_dropdown_icon');
        } else {
            $icon = '';
        }

        $cache->setCache('navbar_order');
        if ($cache->isCached('more_dropdown_order')) {
            $order = $cache->retrieve('more_dropdown_order');
        } else {
            $order = 2500;
        }

        $navigation->addDropdown('more_dropdown', $language->get('general', 'more'), 'top', $order, $icon);

        // Custom pages
        $custom_pages = DB::getInstance()->get('custom_pages', ['id', '<>', 0])->results();
        if (count($custom_pages)) {
            $more = [];
            $cache->setCache('navbar_order');

            if ($user->isLoggedIn()) {
                // Check all groups
                $user_groups = $user->getAllGroupIds();

                foreach ($custom_pages as $custom_page) {
                    $redirect = null;

                    // Get redirect URL if enabled
                    if ($custom_page->redirect == 1) {
                        $redirect = $custom_page->link;
                    }

                    $pages->addCustom(Output::urlEncodeAllowSlashes($custom_page->url), Output::getClean($custom_page->title), !$custom_page->basic);

                    foreach ($user_groups as $user_group) {
                        $custom_page_permissions = DB::getInstance()->get('custom_pages_permissions', ['group_id', $user_group])->results();
/data/core/classes/Core/Settings.php
SELECT `name`, `value` FROM `nl2_settings` WHERE `module` IS NULL;
    private static function setSettingsCache(?string $module, array $cache): void
    {
        $cache_name = $module !== null ? $module : 'core';
        self::$_cached_settings[$cache_name] = $cache;
    }

    /**
     * Get a setting from the database table `nl2_settings`.
     *
     * @param  string  $setting  Setting to check.
     * @param  ?string $fallback Fallback to return if $setting is not set in DB. Defaults to null.
     * @param  string  $module   Module name to keep settings separate from other modules. Set module
     *                           to 'Core' for global settings.
     * @return ?string Setting from DB or $fallback.
     */
    public static function get(string $setting, ?string $fallback = null, string $module = 'core'): ?string
    {
        if (!self::hasSettingsCache($module)) {
            // Load all settings for this module and store it as a dictionary
            if ($module === 'core') {
                $result = DB::getInstance()->query('SELECT `name`, `value` FROM `nl2_settings` WHERE `module` IS NULL')->results();
            } else {
                $result = DB::getInstance()->query('SELECT `name`, `value` FROM `nl2_settings` WHERE `module` = ?', [$module])->results();
            }

            $cache = [];
            foreach ($result as $row) {
                $cache[$row->name] = $row->value;
            }
            self::setSettingsCache($module, $cache);
        }

        $cache = &self::getSettingsCache($module);

        return $cache[$setting] ?? $fallback;
    }

    /**
     * Modify a setting in the database table `nl2_settings`.
     *
     * @param string      $setting   Setting name.