Uncaught SmartyException

Unable to load template 'file:api.tpl'

/var/www/mcstatistics/vendor/smarty/smarty/libs/sysplugins/smarty_internal_template.php

https://mcstatistics.org/api
/var/www/mcstatistics/vendor/smarty/smarty/libs/sysplugins/smarty_internal_template.php
    /**
     * render template
     *
     * @param bool      $no_output_filter if true do not run output filter
     * @param null|bool $display          true: display, false: fetch null: sub-template
     *
     * @return string
     * @throws \Exception
     * @throws \SmartyException
     */
    public function render($no_output_filter = true, $display = null)
    {
        if ($this->smarty->debugging) {
            if (!isset($this->smarty->_debug)) {
                $this->smarty->_debug = new Smarty_Internal_Debug();
            }
            $this->smarty->_debug->start_template($this, $display);
        }
        // checks if template exists
        if (!$this->source->exists) {
            throw new SmartyException(
                "Unable to load template '{$this->source->type}:{$this->source->name}'" .
                ($this->_isSubTpl() ? " in '{$this->parent->template_resource}'" : '')
            );
        }
        // disable caching for evaluated code
        if ($this->source->handler->recompiled) {
            $this->caching = Smarty::CACHING_OFF;
        }
        // read from cache or render
        if ($this->caching === Smarty::CACHING_LIFETIME_CURRENT || $this->caching === Smarty::CACHING_LIFETIME_SAVED) {
            if (!isset($this->cached) || $this->cached->cache_id !== $this->cache_id
                || $this->cached->compile_id !== $this->compile_id
            ) {
                $this->loadCached(true);
            }
            $this->cached->render($this, $no_output_filter);
        } else {
            if (!isset($this->compiled) || $this->compiled->compile_id !== $this->compile_id) {
                $this->loadCompiled(true);
            }
/var/www/mcstatistics/vendor/smarty/smarty/libs/sysplugins/smarty_internal_templatebase.php
                if ($template->caching) {
                    // return cache status of template
                    if (!isset($template->cached)) {
                        $template->loadCached();
                    }
                    $result = $template->cached->isCached($template);
                    Smarty_Internal_Template::$isCacheTplObj[ $template->_getTemplateId() ] = $template;
                } else {
                    return false;
                }
            } else {
                if ($saveVars) {
                    $savedTplVars = $template->tpl_vars;
                    $savedConfigVars = $template->config_vars;
                }
                ob_start();
                $template->_mergeVars();
                if (!empty(Smarty::$global_tpl_vars)) {
                    $template->tpl_vars = array_merge(Smarty::$global_tpl_vars, $template->tpl_vars);
                }
                $result = $template->render(false, $function);
                $template->_cleanUp();
                if ($saveVars) {
                    $template->tpl_vars = $savedTplVars;
                    $template->config_vars = $savedConfigVars;
                } else {
                    if (!$function && !isset(Smarty_Internal_Template::$tplObjCache[ $template->templateId ])) {
                        $template->parent = null;
                        $template->tpl_vars = $template->config_vars = array();
                        Smarty_Internal_Template::$tplObjCache[ $template->templateId ] = $template;
                    }
                }
            }

            if (isset($errorHandler)) {
                $errorHandler->deactivate();
            }

            if (isset($_smarty_old_error_level)) {
                error_reporting($_smarty_old_error_level);
            }
/var/www/mcstatistics/vendor/smarty/smarty/libs/sysplugins/smarty_internal_templatebase.php
     * universal cache
     *
     * @var array()
     */
    public $_cache = array();

    /**
     * fetches a rendered Smarty template
     *
     * @param string $template   the resource handle of the template file or template object
     * @param mixed  $cache_id   cache id to be used with this template
     * @param mixed  $compile_id compile id to be used with this template
     * @param object $parent     next higher level of Smarty variables
     *
     * @throws Exception
     * @throws SmartyException
     * @return string rendered template output
     */
    public function fetch($template = null, $cache_id = null, $compile_id = null, $parent = null)
    {
        $result = $this->_execute($template, $cache_id, $compile_id, $parent, 0);
        return $result === null ? ob_get_clean() : $result;
    }

    /**
     * displays a Smarty template
     *
     * @param string $template   the resource handle of the template file or template object
     * @param mixed  $cache_id   cache id to be used with this template
     * @param mixed  $compile_id compile id to be used with this template
     * @param object $parent     next higher level of Smarty variables
     *
     * @throws \Exception
     * @throws \SmartyException
     */
    public function display($template = null, $cache_id = null, $compile_id = null, $parent = null)
    {
        // display template
        $this->_execute($template, $cache_id, $compile_id, $parent, 1);
    }

/var/www/mcstatistics/core/classes/Templates/SmartyTemplateEngine.php
            DebugBarHelper::getInstance()->addSmartyCollector($smarty);
        }

        $this->_securityPolicy = $securityPolicy;
        $this->_smarty = $smarty;

        parent::__construct();
    }

    public function render(string $templateFile): void
    {
        echo $this->fetch($templateFile);
    }

    public function fetch(string $templateFile): string
    {
        $templateFile = str_replace('.tpl', '', $templateFile);

        $this->_smarty->assign($this->getVariables());

        return $this->_smarty->fetch("$templateFile.tpl");
    }

    public function clearCache(): void
    {
        $this->_smarty->clearAllCache();
    }

    /**
     * Add an extra directory to the Smarty security policy.
     *
     * @param  string $dir Directory to add to policy
     * @return void
     */
    public function addSecurityPolicyDirectory(string $dir): void
    {
        $this->_securityPolicy->secure_dir = [...$this->_securityPolicy->secure_dir, $dir];
        $this->_smarty->enableSecurity($this->_securityPolicy);
    }

    /**
/var/www/mcstatistics/core/classes/Templates/SmartyTemplateEngine.php
            'array_key_exists',
        ];
        $securityPolicy->secure_dir = [ROOT_PATH . '/custom/templates', ROOT_PATH . '/custom/panel_templates'];
        $smarty->enableSecurity($securityPolicy);

        $smarty->setCompileDir(ROOT_PATH . '/cache/templates_c');
        $smarty->setTemplateDir($dir);

        if (defined('PHPDEBUGBAR')) {
            DebugBarHelper::getInstance()->addSmartyCollector($smarty);
        }

        $this->_securityPolicy = $securityPolicy;
        $this->_smarty = $smarty;

        parent::__construct();
    }

    public function render(string $templateFile): void
    {
        echo $this->fetch($templateFile);
    }

    public function fetch(string $templateFile): string
    {
        $templateFile = str_replace('.tpl', '', $templateFile);

        $this->_smarty->assign($this->getVariables());

        return $this->_smarty->fetch("$templateFile.tpl");
    }

    public function clearCache(): void
    {
        $this->_smarty->clearAllCache();
    }

    /**
     * Add an extra directory to the Smarty security policy.
     *
     * @param  string $dir Directory to add to policy
/var/www/mcstatistics/core/classes/Templates/TemplateBase.php
    {
        [$css, $js] = $this->assets()->compile();

        // Put the assets at the start of the arrays, so they load first (SBAdmin requires JQuery first, etc.)
        array_unshift($this->_css, ...$css);
        array_unshift($this->_js, ...$js);

        $this->_engine->addVariables([
            'TEMPLATE_CSS' => $this->getCSS(),
            'TEMPLATE_JS' => $this->getJS(),
        ]);

        if (defined('PHPDEBUGBAR') && PHPDEBUGBAR) {
            $debugBar = DebugBarHelper::getInstance()->getDebugBar()->getJavascriptRenderer();
            $this->_engine->addVariables([
                'DEBUGBAR_JS' => $debugBar->renderHead(),
                'DEBUGBAR_HTML' => $debugBar->render(),
            ]);
        }

        $this->_engine->render($template);
    }

    /**
     * Get all internal CSS styles.
     *
     * @return array Array of strings of CSS.
     */
    public function getCSS(): array
    {
        return $this->_css;
    }

    /**
     * Get all internal JS code.
     *
     * @return array Array of strings of JS.
     */
    public function getJS(): array
    {
        return $this->_js;
/var/www/mcstatistics/modules/MCStatistics/pages/api.php
 *
 *  License: MIT
 *
 *  Site terms page
 */

// Always define page name
const PAGE = 'api';
$page_title = 'API';
require_once(ROOT_PATH . '/core/templates/frontend_init.php');

// Load modules + template
Module::loadPage($user, $pages, $cache, $smarty, [$navigation, $cc_nav, $staffcp_nav], $widgets, $template);

$template->onPageLoad();

require(ROOT_PATH . '/core/templates/navbar.php');
require(ROOT_PATH . '/core/templates/footer.php');

// Display template
$template->displayTemplate('api.tpl');
/var/www/mcstatistics/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);
/var/www/mcstatistics/core/templates/frontend_init.php
SELECT * FROM nl2_page_descriptions WHERE `page` = '/api';
        $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))),
/var/www/mcstatistics/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
/var/www/mcstatistics/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)) {
/var/www/mcstatistics/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.
/var/www/mcstatistics/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
/var/www/mcstatistics/core/classes/Integrations/IntegrationBase.php
SELECT * FROM nl2_integrations WHERE name = 'Partydragen';
 * @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.
/var/www/mcstatistics/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
        }
/var/www/mcstatistics/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()) {
/var/www/mcstatistics/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.
/var/www/mcstatistics/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');
/var/www/mcstatistics/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();
/var/www/mcstatistics/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.
/var/www/mcstatistics/core/classes/Database/PhinxAdapter.php
SELECT version, migration_name FROM nl2_phinxlog;

        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>';
            }
        }